xv6-rev6 読み (11)

何故に 0x10000 なのか、な謎に現実トウヒ。
とりあえず Makefile から、ということで xv6.img のルールが以下。

xv6.img: bootblock kernel fs.img
	dd if=/dev/zero of=xv6.img count=10000
	dd if=bootblock of=xv6.img conv=notrunc
	dd if=kernel of=xv6.img seek=1 conv=notrunc

先頭セクタに bootblock がいて、以降に kernel が、ということなはず。bootblock のルールが以下。

bootblock: bootasm.S bootmain.c
	$(CC) $(CFLAGS) -fno-pic -O -nostdinc -I. -c bootmain.c
	$(CC) $(CFLAGS) -fno-pic -nostdinc -I. -c bootasm.S
	$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o
	$(OBJDUMP) -S bootblock.o > bootblock.asm
	$(OBJCOPY) -S -O binary -j .text bootblock.o bootblock
	./sign.pl bootblock

start がエントリポイントで 0x7c00 番地がテキストセグメントの先頭らしい。あと、sign.pl 使ってマジックナンバーを末端に書きだしているはず。また、kernel のルールが以下。

kernel: $(OBJS) entry.o entryother initcode kernel.ld
	$(LD) $(LDFLAGS) -T kernel.ld -o kernel entry.o $(OBJS) -b binary initcode entryother
	$(OBJDUMP) -S kernel > kernel.asm
	$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym

エントリポイントは initcode らしくルールが以下。

initcode: initcode.S
	$(CC) $(CFLAGS) -nostdinc -I. -c initcode.S
	$(LD) $(LDFLAGS) -N -e start -Ttext 0 -o initcode.out initcode.o
	$(OBJCOPY) -S -O binary initcode.out initcode
	$(OBJDUMP) -S initcode.o > initcode.asm

start がエントリポイントで 0 番地が開始位置らしい。
あと kernel.ld の先頭部分を以下に引用。

ENTRY(_start)

SECTIONS
{
	/* Link the kernel at this address: "." means the current address */
        /* Must be equal to KERNLINK */
	. = 0x80100000;

	.text : AT(0x100000) {
		*(.text .stub .text.* .gnu.linkonce.t.*)
	}

xv6 では objdump 使って *.asm という非常に参考になるアウトプットが出力されるんですが、例えば bootblock.asm の先頭部分が以下になってます。

bootblock.o:     file format elf32-i386


Disassembly of section .text:

00007c00 <start>:
# with %cs=0 %ip=7c00.

.code16                       # Assemble for 16-bit mode
.globl start
start:
  cli                         # BIOS enabled interrupts; disable
    7c00:	fa                   	cli    

start から開始で 0x7c00 になってます。あるいは kernel.asm の先頭部分は以下。

kernel:     file format elf32-i386


Disassembly of section .text:

80100000 <multiboot_header>:
80100000:	02 b0 ad 1b 00 00    	add    0x1bad(%eax),%dh
80100006:	00 00                	add    %al,(%eax)
80100008:	fe 4f 52             	decb   0x52(%edi)
8010000b:	e4 0f                	in     $0xf,%al

8010000c <entry>:

# Entering xv6 on boot processor, with paging off.
.globl entry
entry:
  # Turn on page size extension for 4Mbyte pages
  movl    %cr4, %eax
8010000c:	0f 20 e0             	mov    %cr4,%eax

ソース (entry.S) の上記な定義部分は以下になってまして

.globl _start
_start = V2P_WO(entry)

# Entering xv6 on boot processor, with paging off.
.globl entry
entry:
  # Turn on page size extension for 4Mbyte pages
  movl    %cr4, %eax

ちょっとこのあたり詳細不明。で、真のエントリポイントである 0x7c00 な定義は bootasm.S で定義されていて protected mode に移行して bootmain という手続きに jump します。bootmain 手続きでは 0x10000 番地から elf ヘッダを読み込んで、ということをしている模様。

void
bootmain(void)
{
  struct elfhdr *elf;
  struct proghdr *ph, *eph;
  void (*entry)(void);
  uchar* pa;

  elf = (struct elfhdr*)0x10000;  // scratch space

  // Read 1st page off disk
  readseg((uchar*)elf, 4096, 0);

で、諸々のチェックをした後に jump してます。

  // Call the entry point from the ELF header.
  // Does not return!
  entry = (void(*)(void))(elf->entry);
  entry();

この 0x10000 が課題。xv6 なディレクトリ配下には根拠は皆無。

結論

とりあえず x86 なソレの仕様として先頭セクタが 0x7c00 に展開されて以降のセクタは 0x10000 以降に、という事なのだろうと無理矢理な理解。
おそらく qemu はカーネルのイメージ直接渡されるのでイメージを展開してる、ということにします。mbr なセクタは 0x7c00 に展開して、それ以降は 0x10000 に展開、という理解で勘弁して下さひ。