I think i've read a dozen or so questions that are basically a duplicate of this one, but I still haven't found a solution.
The desired result is to enter protected mode and halt with no faults. The problem i'm experiencing is a triple fault after executing the intersegment jmp with 6 byte immediate value.
here's my code that produces the fault in DOSBox and on a Pentium II PC running MS-DOS 7. Assembler is MASM 5.10
single segment stack
assume cs:single,ds:single
gdt dq 0
c_limit_lo dw 0ffffh
c_base_lo dw 0
c_base_mid db 0
c_priv db 10011110b ;present set, highest priv, type set, conforming, read
c_limit_hi db 11001111b ;granularity set, operand size 32
c_base_hi db 0
d_limit_lo dw 0ffffh
d_base_lo dw 0
d_base_mid db 0
d_priv db 10010010b ;present set, highest priv, type clr, expand dn, write
d_limit_hi db 11001111b ;granularity set, big set
d_base_hi db 0
gdt_end:
gdt_limit dw gdt_end-offset gdt-1
gdt_addr dd ?
start:
mov ax, cs
mov ds, ax
;calc phys address of current code segment and
;insert it into code and data descriptors
.386p
xor eax, eax
mov ax, cs
mov cl, 4
shl eax, cl ;multiply cs by 16 to get phys address of seg
mov edx, eax
mov c_base_lo, ax
mov d_base_lo, ax ;low word
mov cl, 16
shr eax, cl
mov c_base_mid, al
mov d_base_mid, al ;middle byte
mov c_base_hi, ah
mov d_base_hi, ah ;high byte
add edx, offset gdt ;add offset of gdt
mov gdt_addr, edx ;gdt address set
;attempt to enter protected mode
cli ;disable interrupts
in al, 70h
or al, 80h
out 70h, al ;turn off nonmasked interrupts
in al, 92h
or al, 2
out 92h, al ;enable A20 line
lgdt [gdt_limit]
mov eax, cr0
or eax, 1
mov cr0, eax ;enter protected mode
db 66h ;specify 32-bit operand
jmp_op db 0eah ;manually encoded "jmp 8h:enter_32" TRIPLE FAULT
jmp_loc_lo dw offset enter_32
jmp_loc_hi dw 0
jmp_sel dw 8
enter_32:
mov eax, 0ffffffffh ;sometimes doesn't triple fault on infinite jump or hlt instruction
back:jmp back ;but always triple faults on mov
the_stack db 64 dup (0ffh) ;64 byte stack
single ends
end start
The triple fault seems to be "luck" based to some extent. Certain configurations of 0x67 prefixes and nops after the far jump cause the cpu to behave as if it's halted. I don't really understand it.
I think i'm generating the wrong jump target.
Update: It's not faulting with single byte instructions (instructions with a single encoding regardless of cpu mode). I think I'll try jumping into a USE32 defined segment.
This code does not fault:
jmp_op db 0eah
jmp_loc_lo dw offset enter_32
jmp_loc_hi dw 0
jmp_sel dw 8
enter_32:
aaa
daa
cmc
cld
cli
stc
nop
aaa
daa
cmc
cld
cli
stc
nop
hlt