x86_64内核执行STI指令时陷入启动循环问题求助
问题描述
我正在为考试编写一个x86_64内核,每次执行STI指令系统就会重启,该指令在启动时必然运行。我已配置GDT、IDT和ICWs,屏蔽了除键盘输入对应的IRQ1之外的所有IRQ。不执行STI时内核运行正常,但无法使用键盘输入。
main.asm
global start extern long_mode_start bits 32 section .text start: mov esp, stack_top call check_multiboot call check_cpuid call check_long_mode call setup_page_tables call enable_paging lgdt [gdt64.pointer] jmp gdt64.code_segment:long_mode_start hlt check_multiboot: cmp eax, 0x36d76289 jne .no_multiboot ret .no_multiboot: mov al, "M" jmp error check_cpuid: pushfd pop eax mov ecx, eax xor eax, 1 << 21 push eax popfd pushfd pop eax push ecx popfd cmp eax, ecx je .no_cpuid ret .no_cpuid: mov al, "C" jmp error check_long_mode: mov eax, 0x80000000 cpuid cmp eax, 0x80000001 jb .no_long_mode mov eax, 0x80000001 cpuid test edx, 1 << 29 jz .no_long_mode ret .no_long_mode: mov al, "L" jmp error setup_page_tables: mov eax, page_table_l3 or eax, 0b11 ; present, writable mov [page_table_l4], eax mov eax, page_table_l2 or eax, 0b11 ; present, writable mov [page_table_l3], eax mov ecx, 0 ; counter .loop: mov eax, 0x200000 ; 2MiB mul ecx or eax, 0b10000011 ; present, writable, huge page mov [page_table_l2 + ecx * 8], eax inc ecx ; increment counter cmp ecx, 512 ; checks if the whole table is mapped jne .loop ; if not, continue ret enable_paging: ; pass page table location to cpu mov eax, page_table_l4 mov cr3, eax ; enable PAE mov eax, cr4 or eax, 1 << 5 mov cr4, eax ; enable long mode mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr ; enable paging mov eax, cr0 or eax, 1 << 31 mov cr0, eax ret error: ; print "ERR: X" where X is the error code mov dword [0xb8000], 0x4f524f45 mov dword [0xb8004], 0x4f3a4f52 mov dword [0xb8008], 0x4f204f20 mov byte [0xb800a], al hlt section .bss align 4096 page_table_l4: resb 4096 page_table_l3: resb 4096 page_table_l2: resb 4096 stack_bottom: resb 4096 * 4 stack_top: section .rodata gdt64: dq 0 ; zero entry .code_segment: equ $ - gdt64 dq (1 << 43) | (1 << 44) | (1 << 47) | (1 << 53) ; code segment .pointer: dw $ - gdt64 - 1 ; length dq gdt64 ; address
main64.asm
global long_mode_start global load_gdt global load_idt global keyboard_handler global ioport_in global ioport_out global enable_interrupts extern main extern handle_keyboard_interrupt section .text bits 64 long_mode_start: ; load null into all data segment registers mov ax, 0 mov ss, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax call main hlt bits 32 load_idt: mov edx, [esp + 4] lidt [edx] ret keyboard_handler: pushad cld call handle_keyboard_interrupt popad iretd ioport_in: mov edx, [esp + 4] in al, dx ret ioport_out: mov edx, [esp + 4] mov eax, [esp + 8] out dx, al ret bits 16 enable_interrupts: sti ret
内核代码
main.c
#include "io/print.h" #include "io/input.h" void print_prompt(){ print_str("> "); } void kernel_main() { print_clear(); print_set_color(PRINT_COLOR_YELLOW, PRINT_COLOR_BLACK); print_str("Welcome to vgOS v0.1!!"); print_newline(); print_newline(); print_prompt(); } int main(){ kernel_main(); init_idt(); enable_interrupts(); init_kb(); print_str("here"); print_newline(); while(1); return 0; }
input.h
#pragma once #include <stdint.h> #define IDT_SIZE 256 #define KERNEL_CODE_SEGMENT_OFFSET 0x8 #define IDT_INTERRUPT_GATE_64BIT 0x0e #define PIC1_COMMAND_PORT 0x20 #define PIC1_DATA_PORT 0x21 #define PIC2_COMMAND_PORT 0xA0 #define PIC2_DATA_PORT 0xA1 #define KEYBOARD_DATA_PORT 0x60 #define KEYBOARD_STATUS_PORT 0x64 extern void load_gdt(); extern void load_idt(unsigned int idt_address); extern void keyboard_handler(); extern char ioport_in(unsigned short port); extern void ioport_out(unsigned short port, unsigned char data); extern void enable_interrupts(); struct IDTPointer{ uint16_t limit; unsigned long long base; } __attribute__((packed)); struct IDTEntry{ uint16_t offset_1; // Offset bits 0-15 uint16_t selector; // Code segment selector uint8_t ist; // Interrupt Stack Table offset uint32_t zero; uint8_t type_attr; // Gate, type, dpl and p fields uint16_t offset_2; // Offset bits 16-31 uint32_t offset_3; // Offset bits 32-63 } __attribute__((packed)); void init_idt(); void init_kb();
input.c
#include "input.h" #include "print.h" // Declare IDT struct IDTEntry IDT[IDT_SIZE]; void init_idt(){ // Set IDT keyboard entry uint64_t offset = (uint64_t)keyboard_handler; IDT[0x21].offset_1 = offset & 0x000000000000FFFF; IDT[0x21].selector = KERNEL_CODE_SEGMENT_OFFSET; IDT[0x21].ist = 0xE; // Set gate type to 'Interrupt' IDT[0x21].zero = 0; // 0 for testing purposes IDT[0x21].type_attr = IDT_INTERRUPT_GATE_64BIT; IDT[0x21].offset_2 = (offset & 0x00000000FFFF0000) >> 16; IDT[0x21].offset_3 = (offset & 0xFFFFFFFF00000000) >> 32; // Setup ICWs // ICW1 ioport_out(PIC1_COMMAND_PORT, 0x11); ioport_out(PIC2_COMMAND_PORT, 0x11); // ICW2 ioport_out(PIC1_DATA_PORT, 0x20); ioport_out(PIC2_DATA_PORT, 0x28); // ICW3 ioport_out(PIC1_DATA_PORT, 0x4); ioport_out(PIC2_DATA_PORT, 0x2); // ICW4 ioport_out(PIC1_DATA_PORT, 0x01); ioport_out(PIC2_DATA_PORT, 0x01); // Mask all interrupts ioport_out(PIC1_DATA_PORT, 0xff); ioport_out(PIC2_DATA_PORT, 0xff); // Load IDT data structure struct IDTPointer idt_ptr; idt_ptr.limit = (sizeof(struct IDTEntry) * IDT_SIZE) - 1; idt_ptr.base = (unsigned long long)(&IDT); load_idt(&idt_ptr); } void init_kb(){ // 0xFD = 1111 1101 - Unmask IRQ1 ioport_out(PIC1_DATA_PORT, 0xFD); } void handle_keyboard_interrupt(){ ioport_out(PIC1_COMMAND_PORT, 0x20); unsigned char status = ioport_in(KEYBOARD_STATUS_PORT); if(status & 0x1){ char keycode = ioport_in(KEYBOARD_DATA_PORT); if(keycode < 0) return; print_char(keycode); } }
内容的提问来源于stack exchange,提问作者VGenesis
相关产品推荐
相关产品推荐

