Loader需要做的事
Loader详解 基础数据定义和头文件引用 1 2 3 4 5 6 7 8 9 10 11 12 [org 0x10000] jmp LabelStart %include "boot/fat12.inc" BaseOfKernelFile equ 0x00 OffsetOfKernelFile equ 0x100000 BaseTmpOfKernelAddr equ 0x00 OffsetTmpOfKernelFile equ 0x7e00 MemoryStructBufferAddr equ 0x7e00
fat12.inc为FAT12文件系统结构。
定义内核程序的起始地址为0x100000(1MB),因为1MB以下的内存并不是全部可用的空间。这段内存被划分为若干个子空间段,可以是内存空间、非内存空间以及地址空间。内核程序很可能超过1MB,所以让内核跳过这段复杂的空间。
0x7e00是内核程序的临时转存空间,由于内核读取是通过BIOS中断INT 0x13调用,BIOS在实模式下只支持1MB的物理地址空间寻址,所以需要先存储到临时空间,然后再通过特殊的操作传到1MB以上空间中。当内核程序全部转存到内存空间后,这个临时转存空间可以改为内存结构数据的存储空间,供内核程序在初始化时候使用。
LOADER引导程序入口 1 2 3 [SECTION .s16] [BITS 16] LableStart:
[SECTION .s16]为定义一个.s16的段,[BITS 16]伪指令可以通知编译器编译代码时在16位宽的处理器上运行
开启实模式下4GB寻址功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [SECTION gdt] LABEL_GDT: dd 0,0 LABEL_DESC_CODE32: dd 0x0000FFFF,0x00CF9A00 LABEL_DESC_DATA32: dd 0x0000FFFF,0x00CF9200 GdtLen equ $ - LABEL_GDT GdtPtr dw GdtLen - 1 dd LABEL_GDT SelectorCode32 equ LABEL_DESC_CODE32 - LABEL_GDT SelectorData32 equ LABEL_DESC_DATA32 - LABEL_GDT ; ...... ; open address A20 push ax in al, 0x92 or al, 00000010b out 0x92, al pop ax cli db 0x66 lgdt [GdtPtr] mov eax, cr0 or eax, 1 mov cr0, eax mov ax, SelectorData32 mov fs, ax mov eax, cr0 and al, 11111110b mov cr0, eax sti
开启地址A20功能,此功能属于历史遗留问题。最初处理器只有20根地址线,只有1MB的寻址能力,如果超过了1MB的寻址范围操作,也只有低20位是有效的。为了保证向下兼容,便出现了一个控制开启/禁止1MB以上地址空间的开关。当时的8042键盘控制器上恰好有空闲的端口引脚(输出端口P2,引脚P21),从而使用此引脚作为控制开关,即A20功能。如果A20引脚为低电平,那么只有低20位有效。
开启A20功能的方法:
操作键盘控制器,由于键盘控制器是低速设备,以至于功能开启速度过慢
A20快速门(Fast Gate A20),它使用I/O端口0x92来处理A20信号线。对于不含键盘控制器的操作系统,就只能使用0x92端口来控制,但是该端口可能被其他设备使用
通过读0xee端口来开启A20信号线,而写该端口则会禁止A20信号线
上面代码通过A20快速门来开启A20。
当A20功能开启后,紧接着使用cli指令关闭外部中断,再通过指令LGDT加载保护模式结构数据信息,并置CR0寄存器的第0位来开启保护模式。当进入保护模式后,为FS段寄存器加载新的数据段值,一旦完成数据加载就从保护模式中退出,并开启外部中断。
整个操作实现了保护模式的开启和关闭,其目的是为了FS段寄存器可以在实模式下寻址能力超过1MB,也就是Big Real Mode模式。
FAT12文件系统搜索内核文件程序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 ; search kernel.bin mov word [SectorNo], SectorNumOfRootDirStart SearchInRootDirBegin: cmp word [RootDirSizeForLoop], 0 jz NoKernelBin dec word [RootDirSizeForLoop] mov ax, 0x0000 mov es, ax mov bx, 0x8000 mov ax, [SectorNo] mov cl, 1 call ReadOneSector mov si, KernelFileName mov di, 0x8000 cld mov dx, 0x10 SearchForKernelBin: cmp dx, 0 jz GotoNextSectorInRoorDir dec dx mov cx, 11 CmpFileName: cmp cx, 0 jz FileNameFound dec cx lodsb cmp al, byte [es:di] jz GoOn jmp Different GoOn: inc di jmp CmpFileName Different: and di, 0xffe0 add di, 0x20 mov si, KernelFileName jmp SearchForKernelBin GotoNextSectorInRoorDir: add word [SectorNo], 1 jmp SearchInRootDirBegin ; display on screen: Error: KERNEL NOT FOUNT NoKernelBin: mov ax, 0x1301 mov bx, 0x008c mov dx, 0x0300 ; row 3 mov cx, 23 push ax mov ax, ds mov es, ax pop ax mov bp, NoKernelBinMessage int 0x10 jmp $
将KERNEL读取到物理内存中 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 ; found kernel.bin name in root dir FileNameFound: mov ax, RootDirSectors and di, 0xffe0 add di, 0x1a mov cx, word [es:di] push cx add cx, ax add cx, SectorBalance mov eax, BaseTmpOfKernelAddr ; base of kernel file mov es, ax mov bx, OffsetTmpOfKernelFile ; offset of kernel file mov ax, cx GoOnLoadingFile: push ax push bx mov ah, 0x0e mov al, '.' mov bl, 0x0f int 0x10 pop bx pop ax mov cl, 1 call ReadOneSector pop ax push cx push eax push fs push edi push ds push esi mov cx, 0x200 mov ax, BaseOfKernelFile mov fs, ax mov edi, dword [OffsetOfKernelFileCount] mov ax, BaseTmpOfKernelAddr mov ds, ax mov esi, OffsetTmpOfKernelFile MovKernel: mov al, byte [ds:esi] mov byte [fs:edi], al inc esi inc edi loop MovKernel mov eax, 0x1000 mov ds, eax mov dword [OffsetOfKernelFileCount], edi pop esi pop ds pop edi pop fs pop eax pop cx call GetFATEntry cmp ax, 0xfff jz FileLoaded push ax mov dx, RootDirSectors add ax, dx add ax, SectorBalance jmp GoOnLoadingFile
这部分负责将内核程序读取到临时转存空间中,随后再将它移动到1MB以上的物理内存空间。为了避免转存发生错误,一个字节一个字节的复制,借助汇编的LOOP指令可以完成。由于内核体积庞大必须逐个簇读取和转存,那么每次转存的时候必须保存目标偏移,该值(EDI寄存器)保存于临时变量OffsetOfKernelFileCount中。
加载完成后显示字母G 1 2 3 4 5 6 FileLoaded: mov ax, 0xb800 mov gs, ax mov ah, 0x0f ; 0000 black background, 1111 white word mov al, 'G' mov [gs:((80*0+39)*2)], ax ; 0 row, 39 colum
关闭软驱马达 1 2 3 4 5 6 KillMotor: push dx mov dx, 0x03f2 mov al, 0 out dx, al pop dx
关闭软驱马达是通过向I/O端口0x3f2写入控制命令实现的,此端口控制着软盘驱动器的不少硬件功能
内核程序从软盘加载到内存,可以向此I/O端口写入0关闭全部软盘驱动器。在使用OUT汇编指令操作I/O端口时,需要注意8位端口与16位端口的使用区别。
OUT指令的源操作数根据端口位宽可以选用AL/AX/EAX寄存器;目的操作数可以是立即数或DX寄存器,其中立即数的取值范围只能是8位宽(0~0xFF),而DX寄存器允许的取值范围是16位宽(0~0xFFFF)
物理地址空间信息获取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 ; get memory address size type mov ax, 0x1301 mov bx, 0xf mov dx, 0x400 mov cx, 24 push ax mov ax, ds mov es, ax pop ax mov bp, StartGetMemStructMessage int 0x10 mov ebx, 0 mov ax, 0x00 mov es, ax mov di, MemoryStructBufferAddr GetMemStruct: mov eax, 0x0e820 mov ecx, 20 mov edx, 0x534d4150 int 0x15 jc GetMemFailed add di, 20 cmp ebx, 0 jne GetMemStruct jmp GetMemOk GetMemFailed: mov ax, 0x1301 mov bx, 0x008c mov dx, 0x0500 mov cx, 23 push ax mov ax, ds mov es, ax pop ax mov bp, GetMemStructErrorMessage int 0x10 jmp $ GetMemOk: mov ax, 0x1301 mov bx, 0x000f mov dx, 0x0600 mov cx, 29 push ax mov ax, ds mov es, ax pop ax mov bp, GetMemStructOkMessage int 0x10
物理地址空间信息由一个结构体数组构成,计算机平台的地址空间划分情况都能从这个结构体数组中反映出来,它记录的地址空间类型包括可用物理内存地址空间、设备寄存器地址空间、内存空洞等。
这段程序借助BIOS中断服务程序INT 0x15来获取物理地址空间信息,并将其保存到0x7e00地址处的临时空间内,操作系统会在初始化内存管理单元时解析该结构体数组。
显示AL寄存器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ; display num in al DisplauAL: push ecx push edx push edi mov edi, [DispalyPosition] mov ah, 0x0f mov dl, al shr al, 4 mov ecx, 2 .begin: and al, 0x0f cmp al, 9 jz .1 add al, '0' jmp .2 .1: sub al, 0x0a add al, 'A' .2: mov [gs:edi], ax add edi, 2 mov al, dl loop .begin mov [DispalyPosition], edi pop edi pop edx pop ecx ret
DisplauAL的功能:显示16进制数字
先保存即将变更的寄存器值到栈中,然后把变量DispalyPosition保存的屏幕偏移值(字符游标索引值)载入到edi寄存器中,并向AH寄存器存入字体的颜色属性值。为了先显示AL寄存器的高四位数据,暂时吧AL寄存器的低4位数据保存在DL寄存器。然后将AL寄存器的高4位与9比较,如果大于9,则减去0x0a并与字符A相加,否则与字符0相加。然后将AX寄存器(AL与AH寄存器组合而成)的值,保存到GS段寄存器的基地址、DispalyPosition变量为偏移的显示字符内存空间中。
显示视频图像芯片的查询信息 1 2 3 4 5 6 7 ; set the SVGA mode (VESA VBE) mov ax, 0x4f02 mov bx, 0x4180 ; model: 0x180 or 0x143 int 0x10 cmp ax, 0x004f jnz SetSVGAModeVesaVbeFailed
这段程序设置了SVGA芯片的显示模式, 0x180 和 0x143是显示模式:
切换保护模式需要的系统数据结构 1 2 3 4 5 6 7 8 9 10 11 [SECTION gdt] GDT: dd 0,0 DESC_CODE32: dd 0x0000FFFF,0x00CF9A00 DESC_DATA32: dd 0x0000FFFF,0x00CF9200 GdtLen equ $ - GDT GdtPtr dw GdtLen - 1 dd GDT SelectorCode32 equ DESC_CODE32 - GDT SelectorData32 equ DESC_DATA32 - GDT
这是个临时的GDT表。为了避免保护模式段结构的复杂型,此处将代码段和数据段的段地址都设置在0x00000000地址处,此处限长位0xffffffff,即段可以索引0~4GB的内存地址空间。
GDT表的基地址和长度必须借助LGDT指令才能加载得到GDTR寄存器,而GDTR寄存器是一个6B的结构,结构中的低2B保存GDT表的长度,高4B保存GDT表的基地址,标识符GdtPrt是此结构的起始地址。这个GDT表用于开启Big Real Mode模式,由于其数据结构被设置成平坦地址空间(0~4GB地址空间),所以FS段寄存器可以寻址整个4GB内存地址空间。
SelectorCode32和SelectorData32是两个段选择子,它们是段描述符在GDT表中的索引号。
开辟IDT内存空间 1 2 3 4 5 6 7 8 ; tmp IDT IDT: times 0x50 dq 0 IDT_END: IDT_POINTER: dw IDT_END - IDT - 1 dd IDT
切换到保护模式 在处理器切换到保护模式之前,引导加载程序已使用CLI指令禁止外部中断,所以在切换到保护模式的过程中不会产生中断和异常,进而不必完整初始化IDT,只要有相应的结构体即可。如果能保证在切换过程中不会产生任何异常,即使没有IDT也可以。
处理器进入保护模式的标志:执行MOV汇编指令置位CR0控制寄存器的PE标志位(可同时置CR0寄存器的PG标志位开启分页机制)。进入保护模式后,处理器将从0特权级开始执行。具体步骤如下:
执行CLI指令禁止可屏蔽的硬件中断,对于不可屏蔽中断NMI只能借助外部电路才能禁止。(模式切换过程中必须保证在切换过程中不能产生中断和异常)
执行LGDT指令将GDT的基地址和长度加载到GDTR寄存器
执行MOV CR0指令置CR0控制寄存器的PE标志位。(可同时置CR0控制寄存器的PG标志位)
一旦MOV CR0指令执行结束,其后必须执行一条远跳转(far JMP)或远调用(far CALL)指令,切换到保护模式的代码区执行
通过执行JMP或CALL指令,可以改变处理器的执行流水先,进而使处理器加载执行保护模式的代码段
如果开启分页机制,那么MOV CR0指令和JMP/CALL指令必须位于同一地址映射页面内。(因为保护模式和分页机制使能后的物理地址,与执行JMP/CALL指令的线性地址相同)至于JMP或CALL指令的目标地址,则无需进行同一性地址映射(线性地址和物理地址重合)
如需使用LDT,则需借助LLDT汇编指令将GDT内的LDT段选择子加载到LDTR寄存器中。
执行LTR指令将一个TSS段描述符的段选择子加载到TR任务寄存器。处理器对TSS段结构无特殊要求,凡是可写内存空间都可以
进入保护模式后,数据段寄存器仍然保留这实模式的段数据,必须重新加载数据段选择子或使用JMP/CALL执行新任务,便可以将其更新为保护模式。对于不使用的数据段寄存器(DS和SS寄存器除外),可将NULL段选择子加载到其中。
执行LIDT指令,将保护模式下的IDT表的基地址和长度加载到IDTR寄存器
执行STI指令使能可屏蔽硬件中断,并执行必要的硬件操作使能NMI不可屏蔽中断。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 ; init IDT GDT goto protect mode cli ; close interrupt db 0x66 lgdt [GdtPtr] mov eax, cr0 or eax, 1 mov cr0, eax jmp dword SelectorCode32:GoToTmpProtect
db 0x66这个字节是LGDT和LIDT汇编指令的前缀,用于修饰当前指令的操作数是32位宽。而最后一条远跳指令明确指定目标代码段选择子和段内偏移地址。
从保护模式到IA-32e模式 在进入IA-32e模式前,处理器依然要位IA-32e模式准备执行代码,必要的系统数据结构以及配置相关控制寄存器。此外处理器只能在开启分页机制的保护模式下才能切换到IA-32e模式
系统数据结构。系统各个描述符表寄存器必须重新加载(GDRT、LDTR、IDTR、TR)为IA-32e模式的64位描述符表
中断和异常。在IDTR寄存器更新为64位中断描述符表IDT前不要触发中断和异常。
IA32_EFER寄存器(位于MSR寄存器组)的LME标志位用于控制IA-32e模式的开启和关闭,该寄存器会伴随处理器的重启而清零。IA-32e模式的页管理机制将物理地址扩展为4层页表结构。IA-32e模式激活前(CR0.PG=1,处理器运行在32位兼容模式),CR3寄存器仅有低32位可写入数据,从而限制页表只能寻址4GB的物理内存空间、
IA-32e模式的初始化步骤:
在保护模式下,使用MOV CR0汇编指令复位CR0控制寄存器的PG标志位,以关闭分页机制。
置位CR4控制寄存器的PAE标志位,开启物理地址扩展功能(PAE)。在IA-32e模式的初始化过程中,如果PAE开启失败,将会产生通用保护性异常。
将页目录(顶层页表PML4)的物理基地址加载到CR3控制寄存器
置IA32_EFER寄存器的LME标志位,开启IA-32e模式
置CR0控制寄存器的PG标志位开启分页机制,此时处理器会自从置IA32_EFER的LMA标志位。当执行MOV CR0指令开启分页机制时,其后指令必须位于同一性地址映射的页面内(直到处理器进入IA-32e模式后,才可以使用非同一性地址映射的页面)
如果试图改变IA32_EFER、CR0.PG和CR4.PAE等影响IA-32e模式开启的标志位,处理器会进行64位模式的一致性检测,以确保处理器不会进入未定义模式或不可预测状态。如果一致性检测失败,处理器将会产生通用保护性异常(#GP)。以下之一会导致检测失败:
当开启分页机制后,再试图使能或禁止IA-32e模式
当开启IA-32e模式后,试图在开启物理地址扩展(PAE)功能前使能分页机制
在激活IA-32e模式后,试图禁止PAE
当CS寄存器的L位被置位时,在试图激活IA-32e模式
如果TR寄存器加载的是16位TSS段结构
IA-32e模式的临时GDT表 1 2 3 4 5 6 7 8 9 10 11 [SECTION gdt64] GDT64: dq 0x0000000000000000 DESC_CODE64: dq 0x0020980000000000 DESC_DATA64: dq 0x0000920000000000 GdtLen64 equ $ - GDT64 GdtPtr64 dw GdtLen64 - 1 dd GDT64 SelectorCode64 equ DESC_CODE64 - GDT64 SelectorData64 equ DESC_DATA64 - GDT64
IA-32e模式简化了保护模式的段结构,删掉冗余的段基地址和段限长,使段直接覆盖整个线性地址空间,进而变成平坦地址空间。
初始化各个段寄存器以及栈指针 1 2 3 4 5 6 7 8 9 10 11 12 13 [SECTION .s32] [BITS 32] GoToTmpProtect: ; go to tmp long mode mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov ss, ax mov esp, 0x7e00 call SupportLongMode test eax, eax jz NoSupport
检测处理器是否支持IA-32e模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ; test support long mode or not SupportLongMode: mov eax, 0x80000000 cpuid cmp eax, 0x80000001 setnb al jb SupportLongModeDone mov eax, 0x80000001 cpuid bt edx, 29 setc al SupportLongModeDone: movzx eax, al ret NoSupport: jmp $
由于CPUID指令的扩展功能项0x80000001的第29位,指示处理器是否支持IA-32e模式。首先检测当前处理器对CPUID指令的支持情况,判断该指令的最大扩展功能号是否超过0x80000000。只有当CPUID指令的扩展功能号大于等于0x80000001时,才有可能支持64位长模式。所以需要先检测CPUID指令支持的扩展功能号,再读取相应的标志位。最后将读取的结果存入EAX寄存器供模块调用者判断。
CPUID指令:
EFLAGS标志寄存器的ID标志位(第21位)表明处理器是否支持CPUID指令,如果程序可以操作(置位和复位)此标志位,则说明处理器支持CPUID指令,CPUID指令在64位模式和32位模式执行效果相同。
CPUID指令会根据EAX寄存器传入的基础功能号,查询处理器的鉴定信息和机能信息,其返回结构将保存在EAX、EBX、ECX、EDX寄存器中
配置临时页目录项和页表项 1 2 3 4 5 6 7 8 9 10 ; init temporary page table 0x90000 mov dword [0x90000], 0x91007 mov dword [0x90800], 0x91007 mov dword [0x91000], 0x92007 mov dword [0x92000], 0x000083 mov dword [0x92008], 0x200083 mov dword [0x92010], 0x400083 mov dword [0x92018], 0x600083 mov dword [0x92020], 0x800083 mov dword [0x92028], 0xa00083
将IA-32e模式的页目录首地址设置在0x90000处,并相继配置各级页表项的值(该值由页表起始地址和页属性组成)。
重新加载全局描述符表GDT 1 2 3 4 5 6 7 8 9 10 ; load GDTR db 0x66 lgdt [GdtPtr64] mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x7e00
使用LGDT汇编指令,加载IA-32e模式的临时GDT表到GDTR寄存器中,并将临时GDT表的数据段初始化到各个数据段寄存器(除了CS段寄存器),由于CS寄存器不能采用直接复制方式来该表,所以必须借助跨段跳转指令(far JMP)或跨段调用指令(far CALL)才能改变
开启物理地址扩展功能(PAE) 1 2 3 4 ; open PAE mov eax, cr4 bts eax, 5 mov cr4, eax
CR4控制寄存器的第5位是PAE功能的标志位,置位该标志位可开启PAE。
CR3控制寄存器设置 1 2 3 ; load cr3 mov eax, 0x90000 mov cr3, eax
将页目录基地址加载到CR3控制寄存器。
置位IA32_EFER寄存器LME标志位激活IA-32e模式 1 2 3 4 5 6 ; enble long-mode mov ecx, 0x0c0000080 ; IA32_EFER rdmsr bts eax, 8 wrmsr
借助RDMSR/WRMSR指令可以访问64位的MSR寄存器。在访问MSR寄存器前,必须向ECX寄存器(在64位模式下,RCX寄存器的高32位被忽略)传入寄存器地址。而目标MSR寄存器则是由EDX:EAX组成的64位寄存器代表,其中EDX寄存器保存MSR寄存器的高32位,EAX寄存器保存低32位。(64位模式下,RAX和RDX寄存器的高32位均为0)
RDMSR与WRMSR指令必须在0特权级或者实模式下执行。在使用这两条指令前,应该使用CPUID指令(CPUID.01h:EDX[5]=1)来检测处理器是否支持MSR寄存器组。
使能分页寄存器 1 2 3 4 5 ; open PE and paging mov eax, cr0 bts eax, 0 bts eax, 31 mov cr0, eax
置位CR0控制寄存器的PG标志位。到这里处理器进入IA-32e模式。但是处理器目前还在执行保护模式的程序,这种状态叫做兼容模式,即运行在IA-32e模式下的32位模式程序。若要真正运行在IA-32e模式,还需要一条跨段跳转/调用指令将CS段寄存器的值更新为IA-32e模式的代码段描述符。
从Loader跳转到内核程序 1 jmp SelectorCode64:OffsetOfKernelFile
测试 开启bochs虚拟机,使用b命令在0x100000处设置一个断点:
1 2 Num Type Disp Enb Address 1 pbreakpoint keep y 0x000000100000
c执行到端点处,查看各个段寄存器状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 es:0x0010, dh=0x00009300, dl=0x00000000, valid=1 Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed cs:0x0008, dh=0x00209900, dl=0x00000000, valid=1 Code segment, base=0x00000000, limit=0x00000000, Execute-Only, Non-Conforming, Accessed, 64-bit ss:0x0010, dh=0x00009300, dl=0x00000000, valid=1 Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed ds:0x0010, dh=0x00009300, dl=0x00000000, valid=1 Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed fs:0x0010, dh=0x00009300, dl=0x00000000, valid=1 Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed gs:0x0010, dh=0x00009300, dl=0x00000000, valid=1 Data segment, base=0x00000000, limit=0x00000000, Read/Write, Accessed ldtr:0x0000, dh=0x00008200, dl=0x0000ffff, valid=1 tr:0x0000, dh=0x00008b00, dl=0x0000ffff, valid=1 gdtr:base=0x0000000000010064, limit=0x17 idtr:base=0x0000000000000000, limit=0x3ff
可以看出所有段寄存器均被赋值为IA-32a模式的段描述符,经过跳转后的CS段寄存器,它也运行在IA-32e模式下。
q退出时也可看到状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 00015213659i[ ] dbg: Quit 00015213659i[CPU0 ] CPU is in long mode (active) 00015213659i[CPU0 ] CS.mode = 64 bit 00015213659i[CPU0 ] SS.mode = 64 bit 00015213659i[CPU0 ] EFER = 0x00000500 00015213659i[CPU0 ] | RAX=00000000e0000011 RBX=0000000000000000 00015213659i[CPU0 ] | RCX=00000000c0000080 RDX=0000000000000000 00015213659i[CPU0 ] | RSP=0000000000007e00 RBP=00000000000094cd 00015213659i[CPU0 ] | RSI=00000000000080ae RDI=000000000000c800 00015213659i[CPU0 ] | R8=0000000000000000 R9=0000000000000000 00015213659i[CPU0 ] | R10=0000000000000000 R11=0000000000000000 00015213659i[CPU0 ] | R12=0000000000000000 R13=0000000000000000 00015213659i[CPU0 ] | R14=0000000000000000 R15=0000000000000000 00015213659i[CPU0 ] | IOPL=0 id vip vif ac vm rf nt of df if tf sf zf af pf cf 00015213659i[CPU0 ] | SEG sltr(index|ti|rpl) base limit G D 00015213659i[CPU0 ] | CS:0008( 0001| 0| 0) 00000000 00000000 0 0 00015213659i[CPU0 ] | DS:0010( 0002| 0| 0) 00000000 00000000 0 0 00015213659i[CPU0 ] | SS:0010( 0002| 0| 0) 00000000 00000000 0 0 00015213659i[CPU0 ] | ES:0010( 0002| 0| 0) 00000000 00000000 0 0 00015213659i[CPU0 ] | FS:0010( 0002| 0| 0) 00000000 00000000 0 0 00015213659i[CPU0 ] | GS:0010( 0002| 0| 0) 00000000 00000000 0 0 00015213659i[CPU0 ] | MSR_FS_BASE:0000000000000000 00015213659i[CPU0 ] | MSR_GS_BASE:0000000000000000 00015213659i[CPU0 ] | RIP=0000000000100000 (0000000000100000) 00015213659i[CPU0 ] | CR0=0xe0000011 CR2=0x0000000000000000 00015213659i[CPU0 ] | CR3=0x0000000000090000 CR4=0x00000020 00015213659i[CMOS ] Last time is 1657731512 (Thu Jul 14 00:58:32 2022) 00015213659i[XGUI ] Exit 00015213659i[SIM ] quit_sim called with exit code 0
随着Loader引导加载程序最后一条跳转指令,处理器的控制权就到了内核程序上。