xv6はx86をベースに書かれている。当然、プリミティブな部分はx86の命令を使って記述されているのだが、その部分をしっかり理解すれば、他のOSへの移植も簡単に行うことができるのではないだろうか。 今回は、xv6をRISC-Vに移植することを前提に、xv6のどの部分を書き換える必要があるのか調査した。
xv6のプリミティブが格納されているファイル群
まずは調査する必要があるのが、x86.hだ。
ここには、いくつか移植の必要な関数群が存在する。一つ一つ調査していく。
inb(ushort port)
inbは、デバイスアクセス用の関数、x86では独自にデバイスアクセス用の命令が用意されており、それがin命令ということになる。このあたりのx86の動作については、下記の書籍にも言及があり、参考になる。
自作エミュレータで学ぶx86アーキテクチャ-コンピュータが動く仕組みを徹底理解!
- 作者: 内田公太,上川大介
- 出版社/メーカー: マイナビ
- 発売日: 2015/08/28
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (1件) を見る
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命令というものが存在し、割り込みの許可、禁止を設定できる。
今回、これをRISC-Vで実現しようとなれば、どのように書けばよいだろう?RISC-Vには、mie, mip, sie, sipというレジスタが存在しており、それぞれタイマー割り込みや、外部割込みの発生の許可、禁止を制御できる。
RISC-Vは、EI, DIというような、マスクや、割り込みの設定を維持しながら許可、禁止を制御できるようにはなっていないらしい。そうすると、これらのレジスタを直接書き換えると元に戻せなくなってしまうので、何らかのバックアップする手段を持っていたほうがよさそうだ。これには、mstatusのスタック型の割り込み許可、禁止機能が利用できそうだ。