Configuración de interrupciones en modo protegido (x86)
¿Cuál es el proceso de configuración de interrupciones para el modo protegido?
Esta el enlace dice que uno debería:
Hacer espacio para la tabla del descriptor de interrupcionesDígale a la CPU dónde está ese espacio (vea el Tutorial GDT: lidt funciona de la misma manera que lgdt)Dígale al PIC que ya no desea usar los valores predeterminados del BIOS (consulte Programación de los chips PIC)Escriba un par de manejadores de ISR (consulte Rutinas de servicio de interrupción) para IRQ y excepcionesPonga las direcciones de los manejadores ISR en los descriptores apropiados.Habilite todas las interrupciones admitidas en la máscara IRQ (del PIC)El tercer paso no tiene sentido para mí (miréesta enlace, pero no había nada acerca de decirle nada al PIC), así que lo ignoré y completé los siguientes dos pasos, solo para no tener idea una vez más cuando llegué al paso final. Sin embargo, desde mi comprensión de las interrupciones, los dos pasos que no entendí se relacionan con las interrupciones de hardware del controlador PIC y no deberían afectar las interrupciones generadas por el PIT en IRQ 0. Por lo tanto, ignoré este paso también.
Cuando ejecuté mi código, se compiló bien e incluso se ejecutó en una máquina virtual, pero la interrupción pareció activarse solo una vez. Entonces me di cuenta de que no estaba enviando EOI al PIC, evitando que genere más interrupciones. Sin embargo, agregandomov al, 0x20
yout 0x20, al
justo antes deliret
la instrucción hace que la máquina virtual se bloquee.
Aquí está mi IDT:
; idt
idt_start :
dw 0x00 ; The interrupt handler is located at absolute address 0x00
dw CODE_SEG ; CODE_SEG points to the GDT entry for code
db 0x0 ; The unused byte
db 0b11101001 ; 1110 Defines a 32 bit Interrupt gate, 0 is mandatory, privilege level = 0 (0b00), the last bit is one so that the CPU knows that the interrupt will be used
dw 0x00 ; The higher part of the offset (0x00) is 0x00
idt_end:
idt_descriptor :
dw idt_end - idt_start - 1 ; Size of our idt, always one less than the actual size
dd idt_start ; Start address of our idt
Aquí está mi controlador de interrupciones (ubicado en la ubicación absoluta 0x00 en la memoria):
ISR_0:
push eax
add [0x300], byte
mov al, 0x20
out 0x20, al
pop eax
iret
times 512-($-$) db 0
Este es el código que uso para ingresar al modo protegido y cargar el GDT y el IDT en la memoria:
[bits 16]
switch_to_pm:
cli
lgdt [gdt_descriptor]
lidt [idt_descriptor]
mov eax, cr0
or eax, 1
mov cr0,eax
jmp CODE_SEG:init_pm
[bits 32]
init_pm :
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000
mov esp, ebp
sti
call BEGIN_PM
Mi función principal (que verifica el valor de 0x300) es la siguiente:
void main() {
char iii[15];
int * aa = (int *)0x300;
for (;;)
{
setCursor(0, 0);
print(itoab(*aa, iii));
}
}
Por cierto, he verificado mediante un volcado de memoria que todo se carga en la dirección correcta y que todo está exactamente donde se espera. Por ejemplo, 0x300 es una parte libre de memoria utilizada simplemente para simplificar mi código.