Skip to content

最后一次跟 BIOS 说再见 ​

咱们把 stage2 的家底再盘点一遍,这一回要看的是它们脚下踩的地板。栈是咱们自己的了,A20 的门也开了,内存图谱存好了档,画布也挂上了墙。这些活儿没有一样是白干的,可它们全是用 16 位实模式的方式干成的:咱们发号施令用的指令,宽度是 16 位的,而咱们算地址的法子,是把段寄存器左移四位、再拼上一个 16 位的偏移。至于地板本身,咱们是一天都没换过的。

这块地板挤不挤呢?前面几站里咱们其实已经反复尝过了。段加偏移的这套算术,理论上够得着的也就 1MB 出头,而上一站问回来的 framebuffer 物理地址是 0xFD000000,离地板的边沿远得没了边——将来真要画像素,还得指望显卡把显存映射到低地址才够得着,受制的地方到处都是。更麻烦的是,这套寻址是完全不设防的:任何一个地址,只要咱们算得出来,就能直接写进去了,而一支跑偏的指针把别人家的内存涂了,CPU 是连一声都不会吭的。眼下咱们写的还是引导代码,家小业小的,等内核真的跑起来,这样的不设防就是大隐患了。

还有一件咱们天天在用、却不太留意的事:BIOS 的中断,是随时候着命的。咱们问内存、配屏幕,走的都是 INT 指令去敲 BIOS 的门。可门是双向的——定时器到了点、键盘敲了键,这些异步事件也会来敲咱们的门,而在实模式里,接住它们的还是 BIOS。咱们的 stage2 从头到尾没准备过任何处理程序,眼下之所以平安无事,是因为这些中断恰好都有人接得住的。等咱们把 CPU 换到新世界,那套中断向量表就成了废纸,再有人来敲门的时候,就没人应了。

所以要换地板。咱们搬进保护模式的新世界,地址翻译就换了一套章法:段寄存器里装的不再是段基址,而是查表的索引,表里的每个段带着基址、限长和访问权限,CPU 会照着检查的。咱们要管大内存,靠的是一张把段设得全平的表,这套做法咱们下一节细讲。这里咱们把本站的四步预告出来,它们是一个连续的动作:cli 把中断的门关上,lgdt 把新表立了起来,置 CR0 的 PE 位把开关拨过去,末了一句远跳刷新 CS,也就正式跨了线。

四步里头的第一步,就值得咱们停下来看一眼。告别之所以要办得这么郑重,是因为它不可逆:咱们跨过远跳之后,BIOS 的中断入口就全部作废了,而咱们自己又没有中断表,想回头借 BIOS 的手,是借不着了。上一站配屏的时候咱们说过,VBE 是 BIOS 给咱们的最后一次服务。现在咱们回头看,本站的全部前置其实早就排好了:读盘、A20、内存图谱、屏幕参数,凡是需要 BIOS 应答的活,一件都不剩了。起居室收拾干净了,灯一盏盏地关掉,门带上——本站干的就是这个。

到了收工的时候,面板的尾巴跟上一站不一样了:上一站收尾的那行 [stage2] alive 退了场,给咱们换上来的是两行新字:

text
VESA: VBE 0x0300 93 modes
VESA: 0x4144 1024*768*32 LFB
  fb 00000000FD000000 pitch 4096
[stage2] leaving real mode
[pm] 32-bit world alive

头一行是 16 位世界的遗言,而后一行是 32 位世界的第一句话。两行之间隔着的,就是本站要跨的门槛。它们怎么一前一后接上的,咱们一节一节来。

交代一句老话:段寄存器左移四位的实模式寻址,前置卷的汇编课里有底子可挖,咱们这几站一直在用它,这里就不重讲了。

r05_long_mode-2-gfc5569b · fc5569b · 2026-10-01