[SOLVED] INT=0x0d coming from ring 3?
Posted: Sat Aug 17, 2024 5:00 pm
I remapped the PIC to point to non-BIOS interrupts. Then I enabled interrupts, wrote a small irq handler that just sends EOI back to the PIC and returns. I run it in qemu with
Huh. I get a GPF after recieving a PIT interrupt. But hold on, why is cpl 3? Why is the KERNEL SELECTOR 1140h? I never jumped to usermode! I don't have anything in my GDT for usermode!?
I added a breakpoint right after init_idt() to see if ISR/IRQ initialization was the problem. It wasn't.
So I add a bit of code to my ISR handler, namely:
Running with '-debugcon stdio' in qemu does not output an f. It is still (weirdly) talking about a TSS! Do I need a TSS for SSE? I shouldn't, I would've seen this issue way before I got to where I am.
Code
isr.s
My ISR/IRQ handler (isr.c)
The registers_t structure
What's going on here?
Code: Select all
qemu-system-i386 -d int -M smm=off
Code: Select all
Servicing hardware INT=0x20
0: v=20 e=0000 i=0 cpl=0 IP=0008:002005c5 pc=002005c5 SP=0010:00209868 env->regs[R_EAX]=00201000
EAX=00201000 EBX=00010000 ECX=00202ff3 EDX=00000010
ESI=00000000 EDI=00000000 EBP=00209870 ESP=00209868
EIP=002005c5 EFL=00200202 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00200030 00000017
IDT= 00205010 000007ff
CR0=00000013 CR2=00000000 CR3=00000000 CR4=00000600
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=00209868 CCO=EFLAGS
EFER=0000000000000000
check_exception old: 0xffffffff new 0xd
1: v=0d e=0000 i=0 cpl=3 IP=1140:0000156a pc=0001296a SP=0000:2d3c280c env->regs[R_EAX]=00000001
EAX=00000001 EBX=00200202 ECX=0000fffd EDX=00000000
ESI=00000000 EDI=00000020 EBP=002005c5 ESP=2d3c280c
EIP=0000156a EFL=003a6686 [D-S--P-] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
CS =1140 00011400 0000ffff 0000f300 DPL=3 DS16 [-WA]
SS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
DS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
FS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
GS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00200030 00000017
IDT= 00205010 000007ff
CR0=00000013 CR2=00000000 CR3=00000000 CR4=00000600
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=000000ff CCO=ADDB
EFER=0000000000000000
qemu: fatal: invalid tss type
EAX=00000001 EBX=00200202 ECX=0000fffd EDX=00000000
ESI=00000000 EDI=00000020 EBP=002005c5 ESP=2d3c280c
EIP=0000156a EFL=003a6686 [D-S--P-] CPL=3 II=0 A20=1 SMM=0 HLT=0
ES =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
CS =1140 00011400 0000ffff 0000f300 DPL=3 DS16 [-WA]
SS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
DS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
FS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
GS =0000 00000000 0000ffff 0000f300 DPL=3 DS16 [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT= 00200030 00000017
IDT= 00205010 000007ff
CR0=00000013 CR2=00000000 CR3=00000000 CR4=00000600
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000
DR6=ffff0ff0 DR7=00000400
CCS=00000000 CCD=000000ff CCO=ADDB
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000
XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000
XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000
XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
Aborted (core dumped)
I added a breakpoint right after init_idt() to see if ISR/IRQ initialization was the problem. It wasn't.
So I add a bit of code to my ISR handler, namely:
Code: Select all
isr_stub:
mov al, 66h ;; 'f'
out 0xe9, al
Code
isr.s
Code: Select all
extern exception_handler
%macro isr_err_stub 1
isr_stub_%+%1:
;; The error code has already been pushed to the stack
;; when the interrupt is raised, so all we need to do is
;; push the ISR number
push byte %1
jmp isr_stub
iret
%endmacro
%macro isr_no_err_stub 1
isr_stub_%+%1:
push byte 0 ;; Empty error code
push byte %1 ;; Push the ISR number to the stack
jmp isr_stub
iret
%endmacro
isr_stub:
mov al, 0x66
out 0xe9, al
pusha ;; Push all general purpose registers
cld ;; ABI requires DF clear on function entry.
call exception_handler
popa
add esp, 8
iret
isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
isr_no_err_stub 4
isr_no_err_stub 5
isr_no_err_stub 6
isr_no_err_stub 7
isr_err_stub 8
isr_no_err_stub 9
isr_err_stub 10
isr_err_stub 11
isr_err_stub 12
isr_err_stub 13
isr_err_stub 14
isr_no_err_stub 15
isr_no_err_stub 16
isr_err_stub 17
isr_no_err_stub 18
isr_no_err_stub 19
isr_no_err_stub 20
isr_no_err_stub 21
isr_no_err_stub 22
isr_no_err_stub 23
isr_no_err_stub 24
isr_no_err_stub 25
isr_no_err_stub 26
isr_no_err_stub 27
isr_no_err_stub 28
isr_no_err_stub 29
isr_err_stub 30
isr_no_err_stub 31
global isr_stub_table
isr_stub_table:
%assign i 0
%rep 32
dd isr_stub_%+i
%assign i i+1
%endrep
;; Like the ISRs, we need to do the same thing for the IRQs.
;; Instead of just halting, we want to eventually go and handle it,
;; when I write handlers for the IRQs.
;; Parameter one is the IRQ number itself,
;; and parameter two is the IRQ number + 32
%macro IRQ 2
irq_stub_%1:
push byte 0 ;; No error code (obviously)
push byte %2 ;; This is the IRQ
jmp irq_stub
%endmacro
IRQ 0, 32
IRQ 1, 33
IRQ 2, 34
IRQ 3, 35
IRQ 4, 36
IRQ 5, 37
IRQ 6, 38
IRQ 7, 39
IRQ 8, 40
IRQ 9, 41
IRQ 10, 42
IRQ 11, 43
IRQ 12, 44
IRQ 13, 45
IRQ 14, 46
IRQ 15, 47
extern irq_handler ;; see isr.c
irq_stub:
cld ;; ABI requires DF clear on function entry.
call irq_handler
popa
add esp, 8
iret
global irq_stub_table
irq_stub_table:
%assign j 0
%rep 15
dd irq_stub_%+j
%assign j j+1
%endrep
Code: Select all
#include "cpu/isr.h"
// Gets the bad opcode at the IP.
// Adapted from my other project, mmlv.
uint32_t get_faulty_opcode(uint32_t ip){
return (*(uint32_t*)ip) & 0xff;
}
/*
31 16 15 3 2 1 0
+---+-- --+---+---+-- --+---+---+---+---+
| Reserved | Index | Tbl | E |
+---+-- --+---+---+-- --+---+---+---+---+ */
void analyze_gpf(uint32_t errcode){
setcolor(0x00027d);
printf("\nAnalysis:\n");
if(errcode & (1<<0)) printf("This exception originated externally from processor\n");
uint32_t tables = (errcode & (1<<1)) + (errcode & (1<<2));
switch(tables){
case 0b00:
printf("Descriptor in GDT caused fault.\n");
break;
case 0b01:
printf("Descriptor in IDT caused fault.\n");
break;
case 0b10:
printf("Descriptor in LDT caused fault.\n");
break;
case 0b11:
printf("Descriptor in IDT caused fault (binary 0b11)\n");
break;
}
// Bits 3-15 are the selector
printf("Index: %d (hex: %x)\n", (errcode >> 3) & 0x1FFF, (errcode >> 3) & 0x1FFF);
}
__attribute__((interrupt))
void exception_handler(registers_t* r){
setcolor(0xFF0000);
printf("Exception occurred at IP=%x:%x. (Opcode(s): %x %x)\n",
r->cs, r->ip,
get_faulty_opcode(r->ip), get_faulty_opcode(r->ip+1));
printf("Interrupt number is %d (hex: %xh) [ec: %d]\n", r->int_no,
r->int_no, r->errcode);
// If the opcode is 0x0d (GPF) then we can analyze what actually
// happened.
if(r->int_no == 0x0d && r->errcode != 0) analyze_gpf(r->errcode);
for(;;) __asm__("cli; hlt");
}
void irq_handler(registers_t* r){
// Acknowledge the interrupt, send an EOI.
// Did the IRQ come from slave PIC? (Anything above IRQ7 [39])
if(r->int_no >= 40){
outb(0xa0,0x20);
}
outb(0x20, 0x20); // Also send EOI to master PIC.
}
Code: Select all
typedef struct {
uint32_t ds;
uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax;
uint32_t int_no, errcode;
uint32_t ip, cs, eflags, useless, ss;
} registers_t