@MichaelPetch 重写了整个问题,以将其简化为应该易于复制的特定问题。最初的问题集中在以 64 位长模式进行 OS 开发时遇到的问题。该代码试图使用 8042 PS/2 控制器重新启动机器,但它无法在 QEMU 上运行,尽管它在 BOCHS 中运行。原始代码可以在这个Github 项目中找到。
Michael 确定问题不是特定于长模式的。问题空间大大缩小,以更好地说明核心问题。
对于这个演示,我是:
- 使用 GRUB Multiboot2 规范引导 32 位内核。
- 使用 8042 PS/2 控制器通过键盘重新启动机器。
- 创建一个 ELF 可执行文件并将其放置在 ISO 映像中,以便它可以作为 CDROM 引导。
- 假设此代码目标机器/环境支持通过 8042 PS/2 控制器重新启动
本次演示的代码如下:
引导加载程序.asm:
[BITS 32]
section .mboot
mboot_header_start:
dd 0xe85250d6
dd 0
dd mboot_header_end - mboot_header_start
dd 0x100000000 - (0xe85250d6 + 0 +(mboot_header_end - mboot_header_start))
align 8
mboot_inforeq_start:
dw 1
dw 0
dd mboot_inforeq_end - mboot_inforeq_start
dd 2
dd 6
dd 8
mboot_inforeq_end:
align 8
mboot_end_start:
dw 0
dw 0
dd mboot_end_end - mboot_end_start
mboot_end_end:
mboot_header_end:
section .text
global _start
_start:
mov word [0xb8000], (0x5f << 8) | 'B'
mov word [0xb8002], (0x5f << 8) | 'O'
mov word [0xb8004], (0x5f << 8) | 'O'
mov word [0xb8006], (0x5f << 8) | 'T'
; Delay after writing to the screen so it appears for a bit of time before reboot
mov ecx, 0xfffff
delay:
loop delay
; Wait until the 8042 PS/2 Controller is ready to be sent a command
wait_cmd_ready:
in al, 0x64
test al, 00000010b
jne wait_cmd_ready
; Use 8042 PS/2 Controller to reboot the machine
mov al, 0xfe
out 0x64, al
; If this is displayed the reboot wasn't successful. Shouldn't get this far
mov word [0xb8000+160], (0x5f << 8) | 'N'
mov word [0xb8002+160], (0x5f << 8) | 'O'
mov word [0xb8004+160], (0x5f << 8) | 'R'
mov word [0xb8006+160], (0x5f << 8) | 'E'
mov word [0xb8006+160], (0x5f << 8) | 'B'
; Infinite loop to end
hltloop:
hlt
jmp hltloop
链接.ld:
ENTRY(_start);
kern_vma = 0x100000;
SECTIONS
{
. = 0x500;
.boot :
{
*(*.mboot*)
}
. = kern_vma;
.text ALIGN(4K) :
{
*(*.text*)
}
.bss ALIGN(4K) :
{
*(.bss)
}
}
我的 Linux 构建脚本是:
#!/bin/sh
ISO_DIR="isodir"
ISO_NAME="myos"
GRUB_CFG="grub.cfg"
KERNEL_NAME="bootloader"
nasm -f elf32 bootloader.asm -o bootloader.o
ld -m elf_i386 -T link.ld bootloader.o -o $KERNEL_NAME.elf
mkdir -p $ISO_DIR/boot/grub
cp $KERNEL_NAME.elf $ISO_DIR/boot/
echo 'set timeout=2' > $ISO_DIR/boot/grub/$GRUB_CFG
echo 'set default=0' >> $ISO_DIR/boot/grub/$GRUB_CFG
echo 'menuentry "My Kernel" {' >> $ISO_DIR/boot/grub/$GRUB_CFG
echo ' multiboot2 /boot/'$KERNEL_NAME'.elf' >> $ISO_DIR/boot/grub/$GRUB_CFG
echo '}' >> $ISO_DIR/boot/grub/$GRUB_CFG
# build iso image
grub-mkrescue -o $ISO_NAME.iso $ISO_DIR/
问题
当我运行构建脚本并使用以下命令在 QEMU 中运行它时:
qemu-system-i386 -cdrom myos.iso
GRUB 引导内核并BOOT
在窗口左上角以洋红色属性正确显示为白色。它应该在重新启动机器之前短暂等待加载 GRUB 并重复循环。
它没有做预期的事情。它BOOT
按原样显示,但 QEMU 似乎坐着什么也不做,这不是我所期望的。
如果我使用附加选项运行 QEMU,-d int
我发现机器似乎处于由无效操作码异常 (v=06)、一般保护错误 (v=0d) 和双重错误(v =08)。输出通常类似于:
0: v=06 e=0000 i=0 cpl=0 IP=0008:000f0000 pc=000f0000 SP=0010:00000fc0 env->regs[R_EAX]=00000000 EAX=00000000 EBX=00000000 ECX=00000010 EDX=000f171d ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000fc0 EIP=000f0000 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0 ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA] CS =0008 00000000 ffffffff 00cf9b00 DPL=0 CS32 [-RA] 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= 000f6080 00000037 IDT= 000f60be 00000000 CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000 DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 DR6=ffff0ff0 DR7=00000400 CCS=00000000 CCD=0000007f CCO=ADDB EFER=0000000000000000 check_exception old: 0xffffffff new 0xd 1: v=0d e=0032 i=0 cpl=0 IP=0008:000f0000 pc=000f0000 SP=0010:00000fc0 env->regs[R_EAX]=0000 0000 EAX=00000000 EBX=00000000 ECX=00000010 EDX=000f171d ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000fc0 EIP=000f0000 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0 ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA] CS =0008 00000000 ffffffff 00cf9b00 DPL=0 CS32 [-RA] 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= 000f6080 00000037 IDT= 000f60be 00000000 CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000 DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 DR6=ffff0ff0 DR7=00000400 CCS=00000000 CCD=0000007f CCO=ADDB EFER=0000000000000000 check_exception old: 0xd new 0xd 2: v=08 e=0000 i=0 cpl=0 IP=0008:000f0000 pc=000f0000 SP=0010:00000fc0 env->regs[R_EAX]=0000 0000 EAX=00000000 EBX=00000000 ECX=00000010 EDX=000f171d ESI=00000000 EDI=00000000 EBP=00000000 ESP=00000fc0 EIP=000f0000 EFL=00000002 [-------] CPL=0 II=0 A20=1 SMM=0 HLT=0 ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS [-WA] CS =0008 00000000 ffffffff 00cf9b00 DPL=0 CS32 [-RA] 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= 000f6080 00000037 IDT= 000f60be 00000000 CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000 DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 DR6=ffff0ff0 DR7=00000400 CCS=00000000 CCD=0000007f CCO=ADDB EFER=0000000000000000 check_exception old: 0x8 new 0xd check_exception old: 0xffffffff new 0x6
它不断重复类似的模式。不同寻常的是,它似乎陷入了这个异常循环:
0: v=06 e=0000 i=0 cpl=0 IP=0008:000f0000 pc=000f0000 SP=0010:00000fc0 env->regs[R_EAX]=00000000 1: v=0d e=0032 i=0 cpl=0 IP=0008:000f0000 pc=000f0000 SP=0010:00000fc0 env->regs[R_EAX]=0000 2: v=08 e=0000 i=0 cpl=0 IP=0008:000f0000 pc=000f0000 SP=0010:00000fc0 env->regs[R_EAX]=0000
是什么导致了这个问题,我该如何解决它,以便 QEMU 能够正确重启并再次启动 GRUB?