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

2019-04-13 15:32发布

 前几天折腾了一下kernel 并debug了引导和初始化部分,有一些心得,更正了一直以来的很多错误认识。 这里还是拿linux 0.00 的代码来实验,制作好引导盘后启动 bochs (已重新编译加了 –enable-debugger –enable-disasm 参数)   下图是IBM PC内存的分布状况: (注意: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。 再反汇编看一下: x /10 0xf000:0xfff0
[bochs]:
0x000ffff0 :    0x00e05bea      0x2f3730f0      0x302f3630      0xe4fc0035
0×00100000 :    0×00000000      0×00000000      0×00000000      0×00000000
0×00100010 :    0×00000000      0×00000000 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
n
Next at t=2
(0) [0x000fe05d] f000:e05d (unk. ctxt): out 0xd, al               ; e60d

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         
                                                                       
Next at t=483362698                                                                
(0) [0x00007c49] 07c0:0049 (unk. ctxt): lmsw ax                   ; 0f01f0         
                                                                       
Next at t=483362699                                                                
(0) [0x00007c4c] 07c0:0000004c (unk. ctxt): jmp far 0008:0000         ; ea00000800 中国在能够输出自己的价值观之前不会成为真正的强国