调试kernel时对bios加电后过程的一些心得

 前几天折腾了一下kernel 并debug了引导和初始化部分,有一些心得,更正了一直以来的很多错误认识。
这里还是拿linux 0.00 的代码来实验,制作好引导盘后启动 bochs (已重新编译加了 –enable-debugger –enable-disasm 参数)
 
下图是IBM PC内存的分布状况:
调试kernel时对bios加电后过程的一些心得_第1张图片
(注意: 0xA0000 is the pointer address to the Graphical Mode and 0xB8000 is the pointer address to the Color Text Mode and 0xB0000 is the pointer to the Monochrome text Mode. 
 
尽管上面很多区域在bios初始化后填充了内容但其实是可以将kernel 载入 除640KB – 1MB 以及4G高位内存附近的任何存储空间中。因为BIOS提供的参数数据和中断向量kernel并不使用,可以被直接覆盖。
 
下面总结一下整个过程:
 
  1.
加电后cpu 的 状态是:
eax:0×00000000, ebx:0×00000000, ecx:0×00000000, edx:0×00000543
ebp:0×00000000, esp:0×00000000, esi:0×00000000, edi:0×00000000
eip:0x0000fff0, eflags:0×00000002, inhibit_mask:0
cs:s=0xf000, dl=0x0000ffff, dh=0xff009bff, valid=1
ss:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
ds:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
es:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
fs:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
gs:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
ldtr:s=0×0000, dl=0×00000000, dh=0×00000000, valid=0
tr:s=0×0000, dl=0×00000000, dh=0×00000000, valid=0
gdtr:base=0×00000000, limit=0xffff
idtr:base=0×00000000, limit=0xffff
dr0:0×00000000, dr1:0×00000000, dr2:0×00000000
dr3:0×00000000, dr6:0xffff0ff0, dr7:0×00000400
cr0:0×00000010, cr1:0×00000000, cr2:0×00000000
cr3:0×00000000, cr4:0×00000000

通过cs:ip可以发现第一条指令是在0xf000:0xfff0 执行的. 如果只读过大学那些涉及皮毛的书你肯定会认为其物理地址就是0xffff0 (0xf000<<1+0xfff0)。但这样的认识仅仅是针对i8086 ,真正的x86 架构即i80386 之后是这样的:cpu刚刚设置cs:0xf000的时候其实并不是像课本上说的那样在实模式下。此时的物理地址需要根据cs中的隐藏寄存器dl=0x0000ffff, dh=0xff009bff中去查。这个其实就是段描述符(至少在保护模式下是,这里也暂时这样称呼吧) dh的最高8位和最低8位以及dl的高16位组成了32位段基址:0xffff0000 再加上ip:0xfff0 那么物理地址就是0xfffffff0 正好是4GB空间的最后16Byte。
即执行的第一条指令也在这里:
(0) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b         ; ea5be000f0
跳转到真正BIOS程序的入口地址

这里做很精妙,因为intel考虑到兼容性问题设置了cpu加电后cs:ip=0xf000:0xfff0: 如果是i8086那么该跳转指令将在16位cpu可寻址最大空间1MB(基本内存)的最后16Byte,如果是i80386则将在32位cpu可寻址最大空间4GB的最后16Byte。

再反汇编看一下:

<bochs:3> x /10 0xf000:0xfff0
[bochs]:
0x000ffff0 <bogus+       0>:    0x00e05bea      0x2f3730f0      0x302f3630      0xe4fc0035
0×00100000 <bogus+      16>:    0×00000000      0×00000000      0×00000000      0×00000000
0×00100010 <bogus+      32>:    0×00000000      0×00000000

<bochs:4> u /10
000ffff0: (                    ): jmp far f000:e05b         ; ea5be000f0
000ffff5: (                    ): xor byte ptr ds:[bx], dh  ; 3037
000ffff7: (                    ): das                       ; 2f
000ffff8: (                    ): xor byte ptr ds:0x302f, dh ; 30362f30
000ffffc: (                    ): xor ax, 0xfc00            ; 3500fc
000fffff: (                    ): in al, 0×0                ; e400
00100001: (                    ): add byte ptr ds:[bx+si], al ; 0000
00100003: (                    ): add byte ptr ds:[bx+si], al ; 0000
00100005: (                    ): add byte ptr ds:[bx+si], al ; 0000
00100007: (                    ): add byte ptr ds:[bx+si], al ; 0000

这里应为CR0的PE标志复位所以会显示不正确的物理地址(000ffff0,00100007等)

2.

但刚才那种即非实模式也非保护模式的特殊寻址方式不会持续很久,因为intel规定当这时cs再次载入值时cpu将完全进入实模式。也就是说刚才jmp far f000:e05b 导致cs载入0xf000(注意这个是长跳转哦) 这就使cpu进入实模式,而且物理地址即0xf000<1+0xe05b=0xfe05b (实模式段长64KB)
反汇编看一下代码:
(0) [0x000fe05b] f000:e05b (unk. ctxt): xor ax, ax                ; 31c0
<bochs:3> n
Next at t=2
(0) [0x000fe05d] f000:e05d (unk. ctxt): out 0xd, al               ; e60d
<bochs:4>
Next at t=3
(0) [0x000fe05f] f000:e05f (unk. ctxt): out 0xda, al              ; e6da
现在的代码都是在1MB空间里执行的。

3.
执行完BIOS代码后(现在的BIOS 都相当大和复杂)默认从fd0(IBM PC传承的传统)将第一扇区的内容copy到物理地址0x7c00。 但有趣的是这里0x7c00处是一个长跳转指令,而且呈现的虚拟地址是0×0:0x7c00:
(0) [0x00007c00] 0000:7c00 (unk. ctxt): jmpfar 07c0:0005         ; ea0500c007
单步执行后:
(0) [0x00007c05] 07c0:0005 (unk. ctxt): mov ax, cs                ; 8cc8

可以看到重新加载了cs寄存器,但物理地址还是紧接着跳转指令。从dump_cpu中也可以看到:

eax:0x0fffaa55, ebx:0×00000000, ecx:0×00110001, edx:0×00000000
ebp:0×00000000, esp:0x0000fffe, esi:0×00007362, edi:0x0000ffde
eip:0×00000005, eflags:0×00000082, inhibit_mask:0
cs:s=0x07c0, dl=0x7c00ffff, dh=0x00009b00, valid=1
ss:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=7
ds:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
es:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
fs:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
gs:s=0×0000, dl=0x0000ffff, dh=0×00009300, valid=1
ldtr:s=0×0000, dl=0×00000000, dh=0×00000000, valid=0
tr:s=0×0000, dl=0×00000000, dh=0×00000000, valid=0
gdtr:base=0×00000000, limit=0xffff
idtr:base=0×00000000, limit=0xffff
dr0:0×00000000, dr1:0×00000000, dr2:0×00000000
dr3:0×00000000, dr6:0xffff0ff0, dr7:0×00000400
cr0:0×00000010, cr1:0×00000000, cr2:0×00000000
cr3:0×00000000, cr4:0×00000000

不过这样做的目的还真的不太清楚了

4.
进入保护模式后bochs的偏移地址会变更为32位 :
(0) [0x00007c46] 07c0:0046 (unk. ctxt): mov ax, 0×1               ; b80100         
<bochs:1407>                                                                       
Next at t=483362698                                                                
(0) [0x00007c49] 07c0:0049 (unk. ctxt): lmsw ax                   ; 0f01f0         
<bochs:1408>                                                                       
Next at t=483362699                                                                
(0) [0x00007c4c] 07c0:0000004c (unk. ctxt): jmp far 0008:0000         ; ea00000800

中国在能够输出自己的价值观之前不会成为真正的强国

你可能感兴趣的:(c,linux,汇编,IBM,存储,byte)