assemblyx86interrupt-handlingprotected-mode80286

Why do I get triple fault when trying to handle an exception on 286 but not on a modern CPU nor Bochs?


I'm trying to initialize protected mode with exception handling on an AMD 286 system. I've debugged the code below on Bochs, and it works fine there. So does it when run on a Pentium 4 machine. But on the 286 it simply triple faults when it gets to the int3 instruction. The observable behavior is: if I comment out int3, I get the "OK" showed on the screen indefinitely, while with the code as is, the system reboots.

The code is to be compiled by FASM, and the binary put into a boot sector of an HDD or FDD. I'm actually running it from a 1.4M floppy.

 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

I suppose I'm using some CPU feature that the 286 doesn't support, but what exactly and where?


Solution

  • Many of these issues are captured in my General Bootloader Tips in another Stackoverflow answer.