らんだむな記憶

blogというものを体験してみようか!的なー

みかん日記 (4)

第 3 章前半。QEMU モニタを使ってみる。run_image.sh 内で qemu-system-x86_64 のオプションとして -monitor stdio を渡していることで QEMU モニタとして標準入出力を使う形になるっぽい。
さて、起動して書籍に倣って RIP レジスタ周辺のアセンブラのインストラクションを 2 つ表示する。詳細は QEMU Monitor とかを見たら良いのかもしれない。

(qemu) info registers
...
RIP=000000003e665416 RFL=...
...
(qemu) x /4xb 03e665416
000000003e665416: 0xeb 0xfe 0x55 0x41
(qemu) x /2i 03e665416
000000003e665416:   jmp     0x3e665416
000000003e665418:   push    %rbp

となっていて、値は異なるが 同じアドレスにジャンプするということで while (1); が実行されているらしいことが分かった。Busy loop のせいかは分からないけど、QEMU のプロセスが CPU の 80% を持っていっている。

カーネルのビルド。面倒くさいので Makefile にやらせる。

CXX = clang++
LD = ld.lld

TARGET = kernel.elf
SRCS = main.cpp
OBJS = $(SRCS:%.cpp=%.o)

all: $(TARGET)

$(TARGET): $(OBJS)
  $(LD) --entry KernelMain -z norelro --image-base 0x100000 --static -o $@ $(OBJS)

.cpp.o:
  $(CXX) -O2 -Wall -g --target=x86_64-elf -ffreestanding -mno-red-zone -fno-exceptions -fno-rtti -std=c++17 -c $<

.PHONY: clean
clean:
   @rm -rf $(TARGET) $(OBJS)

相変わらずブートローダのところは写経しようもなくて MikanLoaderPkg をコピー。

ページ数 = (kernel_file_size + 0xfff) / 0x1000

0xfff = 0x1000 - 1 に注意すると (x + n - 1) / n の切り上げの計算とかでよく使うやつであることが分かる。
個人的には関数ポインタの typedef

typedef void EntryPointType(void);

ではなく、

typedef void (*EntryPointType)(void);

としてより気持ち悪さを表に出してしまいたい・・・。この場合にはキャストするところも (EntryPointType*) ではなく (EntryPointType) にしないとビルドエラーになる。まぁこの辺は趣味の問題だろうか。

source edksetup.sh
build

ブートローダをビルド。QEMU モニタから確認するとアドレス類は書籍と同じで以下のような感じだった。

(qemu) info registers
...
RIP=0000000000101011 RFL=...
...
(qemu) x /2i 0x101011
0x0000000000101011:  jmp    0x101010
0x0000000000101013:  int3   
(qemu) x /2i 0x101010
0x0000000000101010:  hlt    
0x0000000000101011:  jmp    0x101010

これで第 3 章前半(〜p.81)は終わり。

第 3 章真ん中はブートローダだけビルドして差し替えたら終わりなので、前半のカーネルと組み合わせて起動して画面が真っ白になることを見たら終わり。これで第 3 章真ん中(〜p.83)は終わり。