保护模式下VGA打印导致系统崩溃问题求助
第二阶段引导加载程序VGA打印导致启动循环问题解决
问题描述
在第二阶段引导加载程序末尾实现VGA打印功能(输出“Starting kernel...”)时,系统陷入启动循环,需要将该字符串打印在屏幕第8行(首行计为第1行)后让系统停机。
原代码
ORG 0x7E00 BITS 16 stage2: cli xor ax, ax mov ds, ax mov ss, ax mov sp, 0x7C00 sti mov [BootDrive], dl mov ah, 0x02 mov bh, 0x00 mov dh, 0x02 mov dl, 0x00 int 0x10 push ds mov si, load_gdt call print lgdt [gdtinfo] mov si, enter_pmode call print mov eax, cr0 or eax, 1 mov cr0, eax jmp 0x08:pmode unreal: pop ds xor ax, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax sti mov si, enter_unreal call print jmp boot print: lodsb or al, al jz .done mov ah, 0x0E mov bh, 0x00 mov bl, 0x07 int 0x10 jmp print .done: ret boot: mov si, load_disk call print mov dl, [BootDrive] xor ax, ax int 0x13 jc disk_error mov ax, 0x1000 mov es, ax mov bx, 0 mov dl, [BootDrive] mov dh, 0 mov ch, 0 mov cl, 4 mov al, 16 mov ah, 0x02 int 0x13 jc disk_error cmp al, 0 je disk_error mov ax, [es:0] cmp ax, 0 je no_kernel jmp kernel disk_error: mov si, error_disk_read call print jmp halt no_kernel: mov si, error_no_kernel call print jmp halt kernel: mov si, enter_pmode call print lgdt [gdtinfo] mov eax, cr0 or eax, 1 mov cr0, eax jmp 0x08:kernel32 halt: cli hlt jmp halt load_disk db "Loading kernel...",0x0D, 0x0A, 0 load_gdt db "Loading GDT...", 0x0D, 0x0A, 0 enter_pmode db "Entering protected mode...", 0x0D, 0x0A, 0 enter_unreal db "Entering unREAL mode...", 0x0D, 0x0A, 0 error_disk_read db "Disk read error!", 0x0D, 0x0A, 0 error_no_kernel db "No kernel found!", 0x0D, 0x0A, 0 BootDrive db 0 align 8 gdt: dd 0,0 flatcode: db 0xff,0xff,0,0,0,10011010b,10001111b,0 flatdata: db 0xff,0xff,0,0,0,10010010b,11001111b,0 gdt_end: gdtinfo: dw gdt_end - gdt - 1 dd gdt [BITS 32] pmode: mov bx, 0x10 mov ds, bx mov es, bx mov fs, bx mov gs, bx mov ss, bx mov eax, cr0 and eax, 0xFFFFFFFE mov cr0, eax jmp 0x0:unreal kernel32: mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x90000 mov esi, start_kernel mov edi, 0xB8000 mov ah, 0x0F .print_loop: lodsb or al, al jz .done stosw jmp .print_loop .done: .hang: cli hlt jmp .hang start_kernel db "Starting kernel...", 0
问题分析与修复步骤
1. 启动循环的根源:进入保护模式未关闭中断
在kernel标签的代码中,进入保护模式前没有执行cli指令。切换到保护模式时若有中断触发,会导致CPU异常,进而引发系统重启循环。
2. 调整打印位置到第8行
VGA文本模式的显存起始地址为0xB8000,每行包含80个字符,每个字符占2字节(1字节字符+1字节属性)。第8行的起始偏移为:(8-1) * 80 * 2 = 1120,对应的显存地址为0xB8000 + 1120 = 0xB8460。
修复后的关键代码修改
kernel: mov si, enter_pmode call print ; 进入保护模式前关闭中断 cli lgdt [gdtinfo] mov eax, cr0 or eax, 1 mov cr0, eax jmp 0x08:kernel32 ; ... kernel32: mov ax, 0x10 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, 0x90000 mov esi, start_kernel ; 设置打印起始地址为第8行 mov edi, 0xB8460 mov ah, 0x0F .print_loop: lodsb or al, al jz .done stosw jmp .print_loop .done: .hang: cli hlt jmp .hang
其他注意事项
- 确保GDT的定义正确,32位模式下的段选择子(
0x08为代码段,0x10为数据段)配置无误。 kernel32中的停机代码cli+hlt+jmp .hang能确保系统稳定停机,避免意外唤醒。
内容的提问来源于stack exchange,提问作者Connor Thomson
相关产品推荐
相关产品推荐

