web-dev-qa-db-ja.com

QEMUが8042 PS / 2コントローラー経由でリセットした後、OSを再起動しません

@MichaelPetchは質問全体を書き直して、簡単に再現できる特定の問題にそれを減らしました。 元の質問 は、64ビットロングモードでのOS開発で発生した問題に焦点を当てています。コードは8042 PS/2コントローラーを使用してマシンを再起動しようとしましたが、BOCHSでは機能しましたが、QEMUでは機能しませんでした。元のコードはこの Githubプロジェクト にあります。

マイケルは、問題はロングモードに固有のものではないと判断しました。中心的な問題をわかりやすく説明するために、問題スペースが大幅に削減されました。


このデモでは、私は次のとおりです。

  • GRUB Multiboot2仕様 を使用して32ビットカーネルを起動します。
  • 8042 PS/2コントローラを使用して、キーボードから マシンを再起動 します。
  • ELF実行可能ファイルを作成してISOイメージに配置し、CDROMとして起動できるようにします。
  • このコードが、ターゲットマシン/環境が8042 PS/2コントローラを介した再起動をサポートしていると仮定します

このデモのコードは次のとおりです。

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を再度起動するようにするにはどうすれば修正できますか?

3
alberinfo

元のオペレーティングシステムコード この問題の根本的な原因を特定するために必要な作業。この例により、犯人を簡単に特定できるようになります。元のコードでこの問題を見つけるには、少し手間がかかりました。


主な問題は、次のようにして、リンカスクリプトの仮想メモリアドレス(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バイト以内にある必要があります。

4
Michael Petch