6.828: Operating System Engineering (6)

台風な週末は勉強三昧になるのかどうなのか。
それは良いとして 0x7c00 って見覚えあるなってことで自分ブログを検索してみたら

; boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself
; out of the way to address 0x90000, and jumps there.

カーネルで上記のコメントがあるのを控えてました。成程。あと、テキストの残りががっつりあるのでマキで進めたいのですが、直前エントリで出てきたナニが以下。

  • boot の LD なオプションの確認
  • boot/main.c のコメント確認
  • 0x7c00 な根拠

0x7c00 な根拠は Google 先生に聞いてみます。以下なナニを案内されました。

このドキュメント、ざくっと確認しといた方が良いですが、後回し。
以下が核心な記述か。

Booting from a hard disk is very similar to booting from a diskette. The first sector of a hard disk (often called the Master Boot Record) is loaded at 0000:7C00 and next the BIOS jumps to it. The MBR program must move itself to an address that is different from 0000:7C00 as it is supposed to load a different boot sector from a partition to address 0000:7C00 and jump to that.

あと、boot/main.c の冒頭コメントが以下です。

/**********************************************************************
 * This a dirt simple boot loader, whose sole job is to boot
 * an ELF kernel image from the first IDE hard disk.
 *
 * DISK LAYOUT
 *  * This program(boot.S and main.c) is the bootloader.  It should
 *    be stored in the first sector of the disk.
 * 
 *  * The 2nd sector onward holds the kernel image.
 *	
 *  * The kernel image must be in ELF format.
 *
 * BOOT UP STEPS	
 *  * when the CPU boots it loads the BIOS into memory and executes it
 *
 *  * the BIOS intializes devices, sets of the interrupt routines, and
 *    reads the first sector of the boot device(e.g., hard-drive) 
 *    into memory and jumps to it.
 *
 *  * Assuming this boot loader is stored in the first sector of the
 *    hard-drive, this code takes over...
 *
 *  * control starts in boot.S -- which sets up protected mode,
 *    and a stack so C code then run, then calls bootmain()
 *
 *  * bootmain() in this file takes over, reads in the kernel and jumps to it.
 **********************************************************************/

この実装、面白いですね。Linux では色々とコネクり回した後に protected mode に移行するんですが、ここではいきなり protected mode で開始するらしい。boot/boot.S の記述が以下。

# Start the CPU: switch to 32-bit protected mode, jump into C.
# The BIOS loads this code from the first sector of the hard disk into
# memory at physical address 0x7c00 and starts executing in real mode
# with %cs=0 %ip=7c00.

.set PROT_MODE_CSEG, 0x8         # kernel code segment selector
.set PROT_MODE_DSEG, 0x10        # kernel data segment selector
.set CR0_PE_ON,      0x1         # protected mode enable flag

.globl start
start:
  .code16                     # Assemble for 16-bit mode
  cli                         # Disable interrupts

あら? 違うのかな? 違うか。

  • ES、ES、SS 設定して
  • A20 enable にして
  • gdt を load して
  • cr0 をセットして
  • longjmp しとるな

確かに上記引用でも 16-bit mode とあります。上記の準備ができて longjmp するのは 32-bit mode なソレですね。これ、最小限なコードサンプルという意味で凄いぞ。
このあたりはおそらくテキストに出てくるはずなのでこのあたりでスルーしておいた方が良さげ。あとは boot/Makefrag の以下な記述について。

	$(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o $@.out $^
	$(V)$(OBJDUMP) -S $@.out >$@.asm
	$(V)$(OBJCOPY) -S -O binary $@.out $@
	$(V)perl boot/sign.pl $(OBJDIR)/boot/boot

ええと man によれば -e entry は以下

Use entry as the explicit symbol for beginning execution of your program, rather than the default entry point. If there is no symbol named entry, the linker will try to parse entry as a number, and use that as the entry address (the number will be interpreted in base 10; you may use a leading 0x for base 16, or a leading 0 for base 8).

  • N は以下

Set the text and data sections to be readable and writable. Also, do not page-align the data segment, and disable linking against shared libraries. If the output format supports Unix style magic numbers, mark the output as "OMAGIC". Note: Although a writable text section is allowed for PE-COFF targets, it does not conform to the format specification published by Microsoft.

  • Ttext 0x7c00 は .text なセグメントが 0x7c00 開始、という意味かなぁ。で、objdump で ld の出力を dump して、objcopy で何してるのかな。
  • -S で strip している模様
  • -O binary が謎

ということで Google 先生に確認してみたのですが、GNU Binary Utilities によれば以下な記述あり。

objcopyは,`binary'の出力ターゲットを使用することで,生 のバイナリファイルを生成するために使用することが可能です(例えば, `-O binary'を使用してください).objcopyが生のバイナリ ファイルを生成するとき,それは本質的に,入力オブジェクトファイルの内容 のメモリダンプを生成します.すべてのシンボルと再配置の情報は削除されま す.メモリダンプは,出力ファイルにコピーされる最抵位のセクションのロー ドアドレスから開始します.

凄く厳しく strip してるという理解で良いかな (を
あと、sign.pl も見ておきます。

boot/sign.pl

boot/Makefrag の記述が以下。

	$(V)perl boot/sign.pl $(OBJDIR)/boot/boot

基本的に boot/boot のサイズを 512 にしているだけです。ソースが以下。

#!/usr/bin/perl

open(BB, $ARGV[0]) || die "open $ARGV[0]: $!";

binmode BB;
my $buf;
read(BB, $buf, 1000);
$n = length($buf);

if($n > 510){
	print STDERR "boot block too large: $n bytes (max 510)\n";
	exit 1;
}

print STDERR "boot block is $n bytes (max 510)\n";

$buf .= "\0" x (510-$n);
$buf .= "\x55\xAA";

open(BB, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
binmode BB;
print BB $buf;
close BB;

はい、ということでエントリを改めてテキストを読みつつ exercise をナニします。