FPGA開発日記

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

xv6を移植するときに書き換えるルーチンについて調査(6)

msyksphinz.hatenablog.com

xv6を移植するときに書き換えるルーチンについて調査(5) の続き。

コンパイルはできるようになったのだが、imgファイルを確認すると、まだうまくいっていないところがある。 RISC-Vはリセットベクタが0x0200となっているのだが、imgをダンプするとそこにはよくわからない値が入っていた。

$ hexdump xv6.img | head -n 20
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0000200 457f 464c 0101 0001 0000 0000 0000 0000
0000210 0002 00f3 0001 0000 0000 8010 0034 0000
0000220 3448 0003 0000 0000 0034 0020 0001 0028
0000230 0014 0011 0001 0000 1000 0000 0000 8010
0000240 0000 0010 d0f8 0000 6bac 0001 0007 0000
0000250 1000 0000 0000 0000 0000 0000 0000 0000
0000260 0000 0000 0000 0000 0000 0000 0000 0000
*
0001200 f073 3000 e117 0000 0113 18c1 5297 0000
0001210 8293 e882 8067 0002 0013 0000 0113 fe01
0001220 2e23 0011 2c23 0081 0413 0201 a7b7 8010

ちなみに、オリジナルのx86版のxv6でも同様の値が挿入されていた。どうやら、オブジェクトファイルをそのままimgに変換するとこれが挿入されるらしい。x86ではシステムがリセットされると0番地からブートするので特に問題ないのだが、RISC-Vの場合はちょうど0x200番地に邪魔者が入っており、ブートできない、というわけだ。

  • x86版xv6のimgファイルダンプ結果
$ hexdump xv6.img | head -n 50
...
0000140 e800 ffa3 ffff c483 810c 003d 0100 7f00
0000150 4c45 7546 a150 001c 0001 988d 0000 0001
0000160 b70f 2c35 0100 c100 05e6 de01 f339 2f73
0000170 7b8b ff0c 0473 73ff 5710 6ae8 ffff 8bff
0000180 144b 438b 8310 0cc4 c139 0c76 c701 c129
0000190 00b8 0000 fc00 aaf3 c383 3920 77de ffd1
00001a0 1815 0100 8d00 f465 5e5b 5d5f 00c3 0000
00001b0 0000 0000 0000 0000 0000 0000 0000 0000
*
00001f0 0000 0000 0000 0000 0000 0000 0000 aa55
0000200 457f 464c 0101 0001 0000 0000 0000 0000
0000210 0002 0003 0001 0000 000c 0010 0034 0000
0000220 5cc0 0002 0000 0000 0034 0020 0002 0028
0000230 0011 000e 0001 0000 1000 0000 0000 8010
0000240 0000 0010 b5b6 0000 525c 0001 0007 0000

よくよく見てみると、そういえば面倒くさがってbootasm.Sを省略していたのだった。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

上から順に、

  1. 10000バイト分0で埋めた、xv6.imgのひな型を作成する。
  2. bootblock オブジェクトファイルを先頭から挿入する
  3. kernelオブジェクトファイルを1ブロック分シークした場所から挿入する

となっている。bootblockは、bootasm.Sとbootmain.cから構成されているので、これを作成しないと最初のブートができないというわけだ。

まずはbootasm.Sだが、RISC-Vは先ほども言った通り0x200からブートするので、先頭の0x200バイトは埋めておく必要がある。これにはgasのrept疑似コードを使ってnopを挿入しておく。実際には割り込みルーチンが入ってくるはずなのだが、とりあえず今回は保留だ。

.rept 0x80
        nop
.endr

.globl start
start:
        add     x1,  x0, x0
        add     x2,  x0, x0
...

あとはとりあえずレジスタフォーマットの記述でも入れておこう。x86ではブートのための複雑な処理が入っているが、これはRISC-Vでは関係ないので無視。

.globl start
start:
        add     x1,  x0, x0
        add     x2,  x0, x0
        add     x3,  x0, x0
        add     x4,  x0, x0
        add     x5,  x0, x0
        add     x6,  x0, x0
        add     x7,  x0, x0
        add     x8,  x0, x0
        add     x9,  x0, x0
        add     x10, x0, x0
        add     x11, x0, x0
        add     x12, x0, x0
        add     x13, x0, x0
        add     x14, x0, x0
        add     x15, x0, x0
        add     x16, x0, x0
        add     x17, x0, x0
        add     x18, x0, x0
        add     x19, x0, x0
        add     x20, x0, x0
        add     x21, x0, x0
        add     x22, x0, x0
        add     x23, x0, x0
        add     x24, x0, x0
        add     x25, x0, x0
        add     x26, x0, x0
        add     x27, x0, x0
        add     x28, x0, x0
        add     x29, x0, x0
        add     x30, x0, x0
        add     x31, x0, x0

        # Set up the stack pointer and call into C.
            la      t0, bootmain
            jr      t0

これで、ISSでブート可能なxv6バイナリが完成した。ちゃんとRISC-V ISSでも最初の命令を実行できた!あとはデバッグあるのみ。。。

$ ../swimmer_iss/build_riscv/swimmer_riscv --imgfile xv6.img --debug --max 59
Swimmer-RISCV
  Version 20160410 Revision b2f1940
  developed by Masayuki Kimura <masayuki.kimura.1986@gmail.com>
<Load Image xv6.img>
<Info: NewMemory Region 00000000 is defined.>
<Info: NewMemory Region 00001000 is defined.>
StartDebug max_step=59 <= curr_step(0) + stepCount(59)
Result Max_step= 59
         0:M:MBar:[00000200] 000000b3 : add        r01,r00,r00          r00=>00000000 r00=>00000000 r01<=00000000
         1:M:MBar:[00000204] 00000133 : add        r02,r00,r00          r00=>00000000 r00=>00000000 r02<=00000000
         2:M:MBar:[00000208] 000001b3 : add        r03,r00,r00          r00=>00000000 r00=>00000000 r03<=00000000
         3:M:MBar:[0000020c] 00000233 : add        r04,r00,r00          r00=>00000000 r00=>00000000 r04<=00000000
         4:M:MBar:[00000210] 000002b3 : add        r05,r00,r00          r00=>00000000 r00=>00000000 r05<=00000000
         5:M:MBar:[00000214] 00000333 : add        r06,r00,r00          r00=>00000000 r00=>00000000 r06<=00000000
         6:M:MBar:[00000218] 000003b3 : add        r07,r00,r00          r00=>00000000 r00=>00000000 r07<=00000000
         7:M:MBar:[0000021c] 00000433 : add        r08,r00,r00          r00=>00000000 r00=>00000000 r08<=00000000
         8:M:MBar:[00000220] 000004b3 : add        r09,r00,r00          r00=>00000000 r00=>00000000 r09<=00000000
         9:M:MBar:[00000224] 00000533 : add        r10,r00,r00          r00=>00000000 r00=>00000000 r10<=00000000
        10:M:MBar:[00000228] 000005b3 : add        r11,r00,r00          r00=>00000000 r00=>00000000 r11<=00000000
        11:M:MBar:[0000022c] 00000633 : add        r12,r00,r00          r00=>00000000 r00=>00000000 r12<=00000000
        12:M:MBar:[00000230] 000006b3 : add        r13,r00,r00          r00=>00000000 r00=>00000000 r13<=00000000
        13:M:MBar:[00000234] 00000733 : add        r14,r00,r00          r00=>00000000 r00=>00000000 r14<=00000000
        14:M:MBar:[00000238] 000007b3 : add        r15,r00,r00          r00=>00000000 r00=>00000000 r15<=00000000
        15:M:MBar:[0000023c] 00000833 : add        r16,r00,r00          r00=>00000000 r00=>00000000 r16<=00000000
        16:M:MBar:[00000240] 000008b3 : add        r17,r00,r00          r00=>00000000 r00=>00000000 r17<=00000000
        17:M:MBar:[00000244] 00000933 : add        r18,r00,r00          r00=>00000000 r00=>00000000 r18<=00000000
        18:M:MBar:[00000248] 000009b3 : add        r19,r00,r00          r00=>00000000 r00=>00000000 r19<=00000000
        19:M:MBar:[0000024c] 00000a33 : add        r20,r00,r00          r00=>00000000 r00=>00000000 r20<=00000000
        20:M:MBar:[00000250] 00000ab3 : add        r21,r00,r00          r00=>00000000 r00=>00000000 r21<=00000000
        21:M:MBar:[00000254] 00000b33 : add        r22,r00,r00          r00=>00000000 r00=>00000000 r22<=00000000
        22:M:MBar:[00000258] 00000bb3 : add        r23,r00,r00          r00=>00000000 r00=>00000000 r23<=00000000
        23:M:MBar:[0000025c] 00000c33 : add        r24,r00,r00          r00=>00000000 r00=>00000000 r24<=00000000
        24:M:MBar:[00000260] 00000cb3 : add        r25,r00,r00          r00=>00000000 r00=>00000000 r25<=00000000
        25:M:MBar:[00000264] 00000d33 : add        r26,r00,r00          r00=>00000000 r00=>00000000 r26<=00000000
        26:M:MBar:[00000268] 00000db3 : add        r27,r00,r00          r00=>00000000 r00=>00000000 r27<=00000000
        27:M:MBar:[0000026c] 00000e33 : add        r28,r00,r00          r00=>00000000 r00=>00000000 r28<=00000000
        28:M:MBar:[00000270] 00000eb3 : add        r29,r00,r00          r00=>00000000 r00=>00000000 r29<=00000000
        29:M:MBar:[00000274] 00000f33 : add        r30,r00,r00          r00=>00000000 r00=>00000000 r30<=00000000
        30:M:MBar:[00000278] 00000fb3 : add        r31,r00,r00          r00=>00000000 r00=>00000000 r31<=00000000
        31:M:MBar:[0000027c] 00000297 : auipc      r05,0x00000          r05<=0000027c
        32:M:MBar:[00000280] 10028293 : addi       r05,r05,0x100        r05=>0000027c r05<=0000037c
        33:M:MBar:[00000284] 00028067 : jalr       r00,r05,0x067        r05=>0000037c pc<=0000037c
        34:M:MBar:[0000037c] ff010113 : addi       r02,r02,0xff0        r02=>00000000 r02<=fffffffffffffff0
        35:M:MBar:[00000380] 00112623 : sw         r02,r01,0x00|r12     r02=>fffffffffffffff0 r01=>00000000 (fffffffffffffffc)<=00000000
        36:M:MBar:[00000384] 00812423 : sw         r02,r08,0x00|r08     r02=>fffffffffffffff0 r08=>00000000 (fffffffffffffff8)<=00000000
        37:M:MBar:[00000388] 00912223 : sw         r02,r09,0x00|r04     r02=>fffffffffffffff0 r09=>00000000 (fffffffffffffff4)<=00000000
        38:M:MBar:[0000038c] 01212023 : sw         r02,r18,0x00|r00     r02=>fffffffffffffff0 r18=>00000000 (fffffffffffffff0)<=00000000
        39:M:MBar:[00000390] 01010413 : addi       r08,r02,0x010        r02=>fffffffffffffff0 r08<=00000000
        40:M:MBar:[00000394] 00000613 : addi       r12,r00,0x000        r00=>00000000 r12<=00000000

x86版xv6のブートコードは何をしているのか

実装の際には一切無視したが、x86番の場合、まずCPUを立ち上げるために何が必要なのかを確認しておこう。

  1. まずは16bitモードから立ち上がる
.code16                       # Assemble for 16-bit mode
.globl start

この状態で、まずは割り込みを禁止し、余計な処理が入るのを防ぐ。さらに各種必要なレジスタの初期化が入る。

次にinbで何かを取得しているのだが、調べてみるとこれはPS/2のステータスを待っているらしい。 キーボードが認識されるのを待っているのか。wait for not busyと書いてあるから、PS/2のステータスが安定するのを待っているのね。

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

次にキーボードに対していくつかコマンドを発行する。なぜキーボードに対して発行するのかがいまいちなぞだが、アドレスのモードをこれで変更するらしい。xv6の教科書にも以下のように書いてある。

If the
second bit of the keyboard controller’s output port is low, the 21st physical address bit
is always cleared; if high, the 21st bit acts normally. The boot loader must enable the
21st address bit using I/O to the keyboard controller on ports 0x64 and 0x60 (8920-
8936).

次にプロテクトモードに入り、仮想アドレスを32ビットに増加させる。この時はまだ、仮想アドレスと物理アドレスが同一のまま?

  # Switch from real to protected mode.  Use a bootstrap GDT that makes
  # virtual addresses map directly to physical addresses so that the
  # effective memory map doesn't change during the transition.
  lgdt    gdtdesc
  movl    %cr0, %eax
  orl     $CR0_PE, %eax
  movl    %eax, %cr0

32bitモードに入ると、アクセスできるようになる各種レジスタを初期化していく。

.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

最終的に、bootmainのアドレスを設定してそこにジャンプしている。

  # Set up the stack pointer and call into C.
  movl    $start, %esp
  call    bootmain