FPGA開発日記

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

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

xv6はx86をベースに書かれている。当然、プリミティブな部分はx86の命令を使って記述されているのだが、その部分をしっかり理解すれば、他のOSへの移植も簡単に行うことができるのではないだろうか。 今回は、xv6をRISC-Vに移植することを前提に、xv6のどの部分を書き換える必要があるのか調査した。

xv6のプリミティブが格納されているファイル群

まずは調査する必要があるのが、x86.hだ。

github.com

ここには、いくつか移植の必要な関数群が存在する。一つ一つ調査していく。

inb(ushort port)

inbは、デバイスアクセス用の関数、x86では独自にデバイスアクセス用の命令が用意されており、それがin命令ということになる。このあたりのx86の動作については、下記の書籍にも言及があり、参考になる。

自作エミュレータで学ぶx86アーキテクチャ-コンピュータが動く仕組みを徹底理解!

自作エミュレータで学ぶx86アーキテクチャ-コンピュータが動く仕組みを徹底理解!

X86 Assembly/Other Instructions - Wikibooks, open books for an open world

これは、移植するターゲットによるのだが、通常はメモリマップドI/Oになると考えると、単なるメモリアクセスに置き換えても良い気がする。 ただし、ここではuncachedなアクセスにする必要があるだろう。ここではそこまで深く考えずに、実装の観点でそこはカバーできるものとした。 そうすると、単純なメモリアクセスに変更できる。

static inline uchar
inb(ushort port)
{
  uchar data;

  data = (*(volatile uchar *)(RISCV_IO_BASE + port));
  return data;
}

insl(int port, void *addr, int cnt)

inslは最初はよく分からなかったが、xv6のマニュアルを読むとしっかり書いてある。

http://www.cs.columbia.edu/~junfeng/11sp-w4118/lectures/boot.pdf

The implementation of insl (0412) is worth looking at more closely. Rep insl is
actually a tight loop masquerading as a single instruction. The rep prefix executes the
following instruction %ecx times, decrementing %ecx after each iteration. The insl
instruction reads a 32-bit value from port %dx into memory at address %edi and then
increments %edi by 4. Thus rep insl copies 4×%ecx bytes, in 32-bit chunks, from
port %dx into memory starting at address %edi.

x86にはrepという怪しげな記述子があり、これはECXレジスタが0になるまでデクリメントを繰り返し、当該命令を繰り返すらしい。xv6の実装は下記のようになっているので、

static inline void
insl(int port, void *addr, int cnt)
{
  asm volatile("cld; rep insl" :
               "=D" (addr), "=c" (cnt) :
               "d" (port), "0" (addr), "1" (cnt) :
               "memory", "cc");
}

こんな感じに変換できるのだろうか?

static inline void
insl(int port, void *addr, int cnt)
{
  int *addr_sl = (int *)addr;
  while ((cnt--) > 0) {
    *addr_sl = (*(volatile int *)(RISCV_IO_BASE + port));
    addr_sl++;
  }
}

参考になるサンプルプログラムがなかったので、よくわからない。ただし、この記述は結構いろんなところで利用されているようだ。ちょっと検索しただけでも、いろんなコードが出てきた。

80386 Programmer's Reference Manual -- Opcode REP

src/arch/x86/include/arch/io.h - chromiumos/third_party/coreboot - Git at Google

割り込み許可、不許可

x86計では、cli命令、sti命令というものが存在する。また、MIPSならばEI,DI命令というものが存在し、割り込みの許可、禁止を設定できる。

softwaretechnique.jp

今回、これをRISC-Vで実現しようとなれば、どのように書けばよいだろう?RISC-Vには、mie, mip, sie, sipというレジスタが存在しており、それぞれタイマー割り込みや、外部割込みの発生の許可、禁止を制御できる。

f:id:msyksphinz:20160327020029p:plain RISC-Vは、EI, DIというような、マスクや、割り込みの設定を維持しながら許可、禁止を制御できるようにはなっていないらしい。そうすると、これらのレジスタを直接書き換えると元に戻せなくなってしまうので、何らかのバックアップする手段を持っていたほうがよさそうだ。これには、mstatusのスタック型の割り込み許可、禁止機能が利用できそうだ。

f:id:msyksphinz:20160327020059p:plain