为什么我在尝试处理 286 上的异常时会出现三重错误,但在现代 CPU 或 Bochs 上却没有?



我正在尝试在 AMD 286 系统上初始化具有异常处理的保护模式。我已经在 Bochs 上调试了下面的代码,它在那里工作正常。在奔腾 4 机器上运行时也是如此。但是在 286 上,当它到达int3指令时,它只是三重错误。可观察到的行为是:如果我注释掉int3,我会无限期地显示在屏幕上的"OK",而按原样使用代码,系统重新启动。

代码将由 FASM 编译,并将二进制文件放入 HDD 或 FDD 的引导扇区中。我实际上是从 1.4M 软盘上运行它的。

org 0x7c00
use16
CODE_SELECTOR     = code_descr - gdt
DATA_SELECTOR     = data_descr - gdt
; print "OK" on the screen to see that we've actually started
push     0xb800
pop      es
xor      di,di
mov      ax, 0x0700+'O'
stosw
mov      ax, 0x0700+'K'
stosw
; clear the rest of the screen
mov      cx, 80*25*2-2
mov      ax, 0x0720
rep stosw
lgdt     [cs:gdtr]
cli
smsw     ax
or       al, 1
lmsw     ax
jmp      CODE_SELECTOR:enterPM
enterPM:
lidt     [idtr]
mov      cx, DATA_SELECTOR
mov      es, cx
mov      ss, cx
mov      ds, cx
int3     ; cause an exception
jmp      $
intHandler:
jmp      $
gdt:
dq       0
data_descr:
dw       0xffff     ; limit
dw       0x0000     ; base 15:0
db       0x00       ; base 23:16
db       10010011b  ; present, ring0, non-system, data, extending upwards, writable, accessed
dw       0          ; reserved on 286
code_descr:
dw       0xffff     ; limit
dw       0x0000     ; base 15:0
db       0x00       ; base 23:16
db       10011011b  ; present, ring0, non-system, code, non-conforming, readable, accessed
dw       0          ; reserved on 286
gdtr:
dw       gdtr-gdt-1
gdtBase:
dd       gdt
idt:
rept 14
{
dw       intHandler
dw       CODE_SELECTOR
db       0
db       11100111b    ; present, ring3, system, 16-bit trap gate
dw       0            ; reserved on 286
}
idtr:
dw       idtr-idt-1
idtBase:
dd       idt
finish:
db       (0x7dfe-finish) dup(0)
dw       0xaa55

我想我正在使用 286 不支持的一些 CPU 功能,但究竟是什么以及在哪里?

  • 在保护模式代码中,您有:

    lidt     [idtr]
    mov      cx, DATA_SELECTOR
    mov      es, cx
    mov      ss, cx
    mov      ds, cx
    

    这依赖于在执行lidt [idtr]之前将DS设置为0x0000 在进入保护模式之前(并且DS描述符缓存中的相应基址为 0(。该指令具有隐式DS段。在使用 16 位选择器设置段寄存器之后放置lidt指令,而不是之前。

  • 虽然它没有表现为硬件上的错误,但在实模式下,您的代码还依赖于将CS设置为指令lgdt [cs:gdtr]0x0000。不能保证CS0x0000,因为某些 BIOS 很可能使用非零CS来访问引导加载程序。例如,0x07c0:0x0000 也将到达物理地址 0x07c00 (0x07c0<<4+0x0000=0x07c00(。在实模式代码中,我建议将DS设置为零并使用lgdt [gdtr].

  • 进入保护模式后,在使用堆栈之前,应设置SP。中断将要求堆栈指针在某个位置有效。将其初始化为 0x0000 将使堆栈从 64KiB 段的顶部向下增长。您不应该依赖它恰好指向某个在保护模式下不会干扰您正在运行的系统的地方(即在您的引导加载程序代码/数据之上(。

  • 在使用任何字符串指令(如 STOS/SCAS/CMPS/LODS (之前,应确保按预期设置方向标志。由于您依靠向前移动,因此您应该清除方向标志,CLD.您不应该假设方向标志在进入引导加载程序时是明确的。

其中许多问题在我的一般引导加载程序提示中的另一个 Stackoverflow 答案中都有捕获。

相关内容

  • 没有找到相关文章

最新更新