You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 06:27:37