Assembly 跳转到保护模式时重置
我正在开发一个引导加载程序来加载我没有使用GRUB的操作系统,因为我想学习汇编程序,我的代码会出现三次错误并重置QEMU。以下是相关代码: 引导加载程序 基于BOCHS debugger并尝试使用jmp$在不同点停止程序,我推断问题在于这一行:Assembly 跳转到保护模式时重置,assembly,x86,nasm,bootloader,osdev,Assembly,X86,Nasm,Bootloader,Osdev,我正在开发一个引导加载程序来加载我没有使用GRUB的操作系统,因为我想学习汇编程序,我的代码会出现三次错误并重置QEMU。以下是相关代码: 引导加载程序 基于BOCHS debugger并尝试使用jmp$在不同点停止程序,我推断问题在于这一行: jmp 0x8:Init32 另外,我没有包括函数文件,因为我认为它们在这里没有用处。您没有显示磁盘加载函数,但最终它必须执行与以下操作等效的操作: mov ax, 0201h mov cx, 0002h mov dh, 0
jmp 0x8:Init32
另外,我没有包括函数文件,因为我认为它们在这里没有用处。您没有显示磁盘加载函数,但最终它必须执行与以下操作等效的操作:
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 7e00h
int 13h
这将把CHS=0,0,2加载到引导加载程序后面的ES:BX 0x0000:0x7e00。或者,您可以将ES:BX设置为0x07e0:0x0000。由于您声称问题似乎是跳转到保护模式,我将假设磁盘负载正在工作
这实际上只剩下GDT可能是问题,而不是潜在的堆栈问题1。我确实看到了一个严重的问题,它可能导致远JMP进入保护模式失败:
.desc:
dw .end - GDT - 1
db GDT
应该是一个长度为-1的单词dw,后面跟一个DWORD dd。您已经将基定义为单字节db!您需要将其更改为dd。它应该如下所示:
.desc:
dw .end - GDT - 1
dd GDT
不幸的是,NASM不会试图告诉您它填充到字节中的值是从另一个值截取的。此问题可能导致您的问题,因为代码描述符无效,导致jmp 0x8:Init32出现三重故障并导致重新启动
如果使用BOCHS进行调试,则可以将断点设置为0x7c00,并逐条执行指令,直到指令位于lgdt[GDT.desc]之后。您本来可以使用信息GDT查看GDT。您可能会发现基数错误,并且所有条目都不正确
附加说明
1您已将堆栈指针SS:SP设置为0x0000:0x0500。堆栈从该地址向下扩展。0x0000:0x0500包含的数据,其正下方是。你应该把这堆东西放在更安全的地方。0x0000:0x7c00在引导加载程序下方向下生长,在它撞击BDA之前会留下大量的堆栈空间
您需要在进入受保护模式之前关闭中断,这样您就可以在代码开始时简单地执行CLI,避免所有CLI/STI指令。或者,您可以一直启用它们,并在进入保护模式之前执行CLI
通常情况下,通过首先将SS更新为新的值和SP,使中断能够同时更新SS:SP。这是因为更新SS的副作用是在执行以下指令之前禁用中断。如果更新SP是以下指令,则操作将以原子方式完成,并且更新SS和SP之间不会发生中断。在某些8088处理器上,存在一个缺陷,该缺陷没有发生,但此处不适用,因为您的代码需要386+
在实模式代码中设置BP没有任何效果,因为您没有在任何地方使用堆栈帧,也没有调用任何需要将BP设置为特定值的BIOS例程。您也不需要设置GS和FS,因为您的代码没有在实际模式中使用它们
代码的开头可能显示为:
Start:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax ; Set SS:SP to grow down beneath bootloader at 0x0000:0x7c00
mov sp, 7c00h
cld
我有额外的引导程序提示,在这可能是有用的
您没有显示磁盘加载功能,但最终它必须执行以下等效操作:
mov ax, 0201h
mov cx, 0002h
mov dh, 0
mov bx, 7e00h
int 13h
这将把CHS=0,0,2加载到引导加载程序后面的ES:BX 0x0000:0x7e00。或者,您可以将ES:BX设置为0x07e0:0x0000。由于您声称问题似乎是跳转到保护模式,我将假设磁盘负载正在工作
这实际上只剩下GDT可能是问题,而不是潜在的堆栈问题1。我确实看到了一个严重的问题,它可能导致远JMP进入保护模式失败:
.desc:
dw .end - GDT - 1
db GDT
应该是一个长度为-1的单词dw,后面跟一个DWORD dd。您已经将基定义为单字节db!您需要将其更改为dd。它应该如下所示:
.desc:
dw .end - GDT - 1
dd GDT
不幸的是,NASM不会试图告诉您它填充到字节中的值是从另一个值截取的。此问题可能导致您的问题,因为代码描述符无效,导致jmp 0x8:Init32出现三重故障并导致重新启动
如果使用BOCHS进行调试,则可以将断点设置为0x7c00,并逐条执行指令,直到指令位于lgdt[GDT.desc]之后。您本来可以使用信息GDT查看GDT。您可能会发现基数错误,并且所有条目都不正确
附加说明
1您已将堆栈指针SS:SP设置为0x0000:0x0500。堆栈从该地址向下扩展。0x0000:0x0500包含的数据,其正下方是。你应该把这堆东西放在更安全的地方。0x0000:0x7c00向下生长在引导加载程序下面,在它撞击B之前会留下大量的堆栈空间
爸爸
您需要在进入受保护模式之前关闭中断,这样您就可以在代码开始时简单地执行CLI,避免所有CLI/STI指令。或者,您可以一直启用它们,并在进入保护模式之前执行CLI
通常情况下,通过首先将SS更新为新的值和SP,使中断能够同时更新SS:SP。这是因为更新SS的副作用是在执行以下指令之前禁用中断。如果更新SP是以下指令,则操作将以原子方式完成,并且更新SS和SP之间不会发生中断。在某些8088处理器上,存在一个缺陷,该缺陷没有发生,但此处不适用,因为您的代码需要386+
在实模式代码中设置BP没有任何效果,因为您没有在任何地方使用堆栈帧,也没有调用任何需要将BP设置为特定值的BIOS例程。您也不需要设置GS和FS,因为您的代码没有在实际模式中使用它们
代码的开头可能显示为:
Start:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax ; Set SS:SP to grow down beneath bootloader at 0x0000:0x7c00
mov sp, 7c00h
cld
我有额外的引导程序提示,在这可能是有用的
GDT记录GDTR应该是一个长度为-1的DWORD dw,后面跟一个DWORD dd-对dw的引用应该读字。GDT记录GDTR应该是一个长度为-1的DWORD dw,后面跟一个DWORD dd-对dw的引用应该读字。