FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

xv6のブートプロセスで学ぶコンピュータの起動(まだ勉強中2)

さて、次はbootasm.S とbootmain.cの勉強だ。

※ この記事はまだ勉強中のため、いろいろ間違いがあるかもしれません。

http://pdos.csail.mit.edu/6.828/2014/xv6/book-rev8.pdf

http://pdos.csail.mit.edu/6.828/2014/xv6/xv6-rev8.pdf

github.com

  # Physical address line A20 is tied to zero so that the first PCs
  # with 2 MB would run software that assumed 1 MB.  Undo that.
seta20.1:
  inb     $0x64,%al               # Wait for not busy
  testb   $0x2,%al
  jnz     seta20.1

  movb    $0xd1,%al               # 0xd1 -> port 0x64
  outb    %al,$0x64

seta20.2:
  inb     $0x64,%al               # Wait for not busy
  testb   $0x2,%al
  jnz     seta20.2

  movb    $0xdf,%al               # 0xdf -> port 0x60
  outb    %al,$0x60

このあたり、何をしているんだろうと思ったら、キーボードの設定か。詳細はこちらを検索して見つけた。 inbで呼ばれるアドレスの中で0x00から0xffまではマザーボードの仕様に委ねられている。さらに検索すると、0x64はキーボードのステータスを示しているのか。

Keyboard scancodes: The AT keyboard controller

まずは、キーボードコントローラレジスタの2ビット目をチェックして、何らかのキーが入力されていないかをチェックする。 次に、0xd1を出力する。これはどういう意味だろう。パリティエラーとかが出力されるけど大丈夫なんかいな。

次は0x60に0xdfを出力している。これもキーボードコントローラバイトだ。

.code32  # Tell assembler to generate 32-bit code now.
start32:
  # Set up the protected-mode data segment registers
  movw    $(SEG_KDATA<<3), %ax    # Our data segment selector
  movw    %ax, %ds                # -> DS: Data Segment
  movw    %ax, %es                # -> ES: Extra Segment
  movw    %ax, %ss                # -> SS: Stack Segment
  movw    $0, %ax                 # Zero segments not ready for use
  movw    %ax, %fs                # -> FS
  movw    %ax, %gs                # -> GS

これは、32ビットモードに設定するコードかな。詳細は勉強していないけど、コメントからそういう事だと思う。

次は、bootmainを呼び出している。いよいよ、bootmain.cに移る。

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);

  // Is this an ELF executable?
  if(elf->magic != ELF_MAGIC)
    return;  // let bootasm.S handle error

  // Load each program segment (ignores ph flags).
  ph = (struct proghdr*)((uchar*)elf + elf->phoff);
  eph = ph + elf->phnum;
  for(; ph < eph; ph++){
    pa = (uchar*)ph->paddr;
    readseg(pa, ph->filesz, ph->off);
    if(ph->memsz > ph->filesz)
      stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
  }

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

セクタのロードか。readsegは、回り回って、最終的にreadsectを読んでいる。

// Read a single sector at offset into dst.
void
readsect(void *dst, uint offset)
{
  // Issue command.
  waitdisk();
  outb(0x1F2, 1);   // count = 1
  outb(0x1F3, offset);
  outb(0x1F4, offset >> 8);
  outb(0x1F5, offset >> 16);
  outb(0x1F6, (offset >> 24) | 0xE0);
  outb(0x1F7, 0x20);  // cmd 0x20 - read sectors

  // Read data.
  waitdisk();
  insl(0x1F0, dst, SECTSIZE/4);
}

これでデータを読み込んでいる。途中については省略するが、プログラムセグメントを全て読みこんだら、elfのエントリポイントを調査して、ジャンプする、という仕組みらしい。

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

ここから先も、さらに調査を進める。