Very strange behaviour with IDT under long pure 64-bit mode
Posted: Sat Jul 13, 2013 7:39 am
Hi everyone,
I'm facing this very strange behaviour trying to setup an IDT under long IA-32e mode. I followed the Intel manuals instructions and yet something's bugging me desperately for the past 4-5 hours now. Here's the setup, then I'll describe the problem:
CODE64_SEL is 0x08 and in my GDT table that entry has the value 0x002F9A000000FFFF, which is 64-bit pure mode, execute/read, present, base 0, limit 0xFF[...], but base and limit don't matter. Now, this is the funny thing that's happening. When executing the instruction "call __interruptProcedure" you would expect the CPU to PUSH on the stack the RIP (which points to "RET"). That happens precisely so with the given code (by looking at RAX after HLT-ing in __interruptProcedure, I know it's ok). But IF I UNCOMMENT the "STI" instruction, what is being pushed on the stack instead is the address of the "IRETQ" instruction inside __interruptProcedure. If I comment the "CALL __interruptProcedure", keep "STI" uncommented and uncomment "INT 32" as well, the same RIP is pushed on the stack => pointing to "IRETQ". If, in this situation, I also remove the HLT instruction inside __interruptProcedure, QEMU simply resets (probably because IRETQ returns to itself??).
Thank you very much in advance for your suggestions,
John X.
P.S.: Other notes - The assembler I use is YASM (an extension of NASM which uses basically the same syntax), running the code using QEMU. I was able to see the values of the registers by using QEMU's Monitor feature. The execution stops inside __interruptProcedure at the HLT instruction; when that happens, RAX should be loaded with the RIP pushed on the stack (mov rax, [rsp]). In QEMU Monitor I'm running the command "x /1i $eax" to see the instruction pointed by the RIP on the stack; this results in "RET" when STI is commented out and in "IRETQ" when it's uncommented. See details on the bottom of this page http://en.wikibooks.org/wiki/QEMU/Monitor regarding the QEMU Monitor command "x /1i $eax". Also, I've set-up the paging tables for long mode and everything is working properly regarding that. Each IDT entry is set-up to point to __interruptProcedure and to be a trap-gate, running under ring 0, present and to use the 0x08 code selector I've mentioned earlier. The IST for each IDT entry is set to be 0.
I'm facing this very strange behaviour trying to setup an IDT under long IA-32e mode. I followed the Intel manuals instructions and yet something's bugging me desperately for the past 4-5 hours now. Here's the setup, then I'll describe the problem:
Code: Select all
[bits 64]
__setupIDT:
; RBX|RAX = IDT entry (trap-gate, 32 bits), pointing to __interruptProcedure
mov rax, 0x8F00_0000_0000
mov rbx, CODE64_SEL
shl rbx, 16
or rax, rbx
mov rbx, qword __interruptProcedure
and rbx, 0xFFFF0000
shl rbx, 32
or rax, rbx
mov rbx, qword __interruptProcedure
and rbx, 0xFFFF
or rax, rbx
mov rbx, qword __interruptProcedure
shr rbx, 32
mov rdi, qword IDT
mov cx, 256
__setupIDT_NextIDTStore:
mov [rdi], rax
mov [rdi+8], rbx
add rdi, 16
dec cx
jnz __setupIDT_NextIDTStore
mov rax, qword IDTR
lidt [rax]
;sti
lea rbx, [rip]
;int 32
call __interruptProcedure
ret
align 16
__interruptProcedure:
mov rax, [rsp]
hlt
iretq
align 16
IDTR:
idt_limit dw (256 * 16 - 1)
idt_base dq IDT
align 16
IDT:
times 256 ddq 0x00000000_00000000_0000_0000_0000_0000 ; set-up by code
Thank you very much in advance for your suggestions,
John X.
P.S.: Other notes - The assembler I use is YASM (an extension of NASM which uses basically the same syntax), running the code using QEMU. I was able to see the values of the registers by using QEMU's Monitor feature. The execution stops inside __interruptProcedure at the HLT instruction; when that happens, RAX should be loaded with the RIP pushed on the stack (mov rax, [rsp]). In QEMU Monitor I'm running the command "x /1i $eax" to see the instruction pointed by the RIP on the stack; this results in "RET" when STI is commented out and in "IRETQ" when it's uncommented. See details on the bottom of this page http://en.wikibooks.org/wiki/QEMU/Monitor regarding the QEMU Monitor command "x /1i $eax". Also, I've set-up the paging tables for long mode and everything is working properly regarding that. Each IDT entry is set-up to point to __interruptProcedure and to be a trap-gate, running under ring 0, present and to use the 0x08 code selector I've mentioned earlier. The IST for each IDT entry is set to be 0.