@MichaelPetchは質問全体を書き直して、簡単に再現できる特定の問題にそれを減らしました。 元の質問 は、64ビットロングモードでのOS開発で発生した問題に焦点を当てています。コードは8042 PS/2コントローラーを使用してマシンを再起動しようとしましたが、BOCHSでは機能しましたが、QEMUでは機能しませんでした。元のコードはこの Githubプロジェクト にあります。
マイケルは、問題はロングモードに固有のものではないと判断しました。中心的な問題をわかりやすく説明するために、問題スペースが大幅に削減されました。
このデモでは、私は次のとおりです。
このデモのコードは次のとおりです。
bootloader.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
link.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は座って何もしないようです。
追加のオプション-d int
を使用してQEMUを実行すると、マシンが無効なオペコード例外(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を再度起動するようにするにはどうすれば修正できますか?
元のオペレーティングシステムコード この問題の根本的な原因を特定するために必要な作業。この例により、犯人を簡単に特定できるようになります。元のコードでこの問題を見つけるには、少し手間がかかりました。
主な問題は、次のようにして、リンカスクリプトの仮想メモリアドレス(VMA)0x500にマルチブートセクション(Multiboot2ヘッダーを含む)を配置したことです。
SECTIONS
{
. = 0x500;
.boot :
{
*(*.mboot*)
}
. = kern_vma;
.text ALIGN(4K) :
{
*(*.text*)
}
.bss ALIGN(4K) :
{
*(.bss)
}
}
VMAとロードメモリアドレス(LMA)は、.boot
セクションはリンカによって発行されます。問題は、GRUBがELF実行可能ファイルを読み取るときにこのセクションをメモリアドレス0x500にロードしようとすることです。0x100000未満のコードとデータを配置することは非常に悪い考えです。GRUBは、メモリのこの領域を使用して、カーネルのロードを含むすべての起動関連タスクを実行します。GRUBが使用しているメモリを誤って上書きして、マシンが未定義の状態になる可能性があります。 GRUBを実行している一部のマシンでは機能する可能性があります。他のマシンでは問題が発生する可能性があります。
Multiboot2ヘッダーの位置に関するMultiboot2仕様の唯一のルールは、それがクワッドワードワード境界(64ビット)に揃えられ、ELFファイルの最初の32,768バイトに収まることです。 Multiboot2ヘッダーを0x100000の下に配置する必要はありません。それは不要です。コードとデータの前に0x100000を超えて配置します。これはうまくいくはずです:
ENTRY(_start);
kern_vma = 0x100000;
SECTIONS
{
. = kern_vma;
.boot :
{
*(*.mboot*)
}
.text ALIGN(4K) :
{
*(*.text*)
}
.bss ALIGN(4K) :
{
*(.bss)
}
}
あなたの場合、0x500で0x100000未満のメモリに書き込むと、QEMUが正しく再起動しなくなり、その結果GRUB=が再起動しなくなります。失敗の正確な性質はわかりませんが、結果はかなり良好です。この場合は明らかです。
元の マルチブート仕様 でブートする場合、同じことが当てはまりますが、マルチブートヘッダーはダブルワード境界(32ビット)に揃えられていて、ELF実行可能ファイルの最初の8192バイト以内にある必要があります。