UCBが開発しているRISC-VのシミュレータSpikeや、Rocket-ChipのRTLデザインは通常はシステムコールを持っていない。
つまり、当然ながらC言語でprintf("Hello World\n");
などと書いても動作しないのだが、そこはコンパイラとフロントエンドサーバfesvr、pk(Proxy Kernel)によって肩代わりすることでこれらのシステムコールを実現している。
Proxy KernelというのはRISC-Vのシステムコールにつながる処理をフロントエンドサーバに受け渡すインタフェース、そしてフロントエンドサーバはPKからのシステムコールリクエストを受け取ってRISC-Vコアの代わりに実現する機能を持っている。
これらの機能をRISC-V自作シミュレータに実装して、システムコールを実現した。
例えば、syscall::sys_read
システムコールは、fesvrでは以下のように実装されている。
riscv-fesvr/fesvr/syscall.cc
reg_t syscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6) { std::vector<char> buf(len); ssize_t ret = read(fds.lookup(fd), &buf[0], len); reg_t ret_errno = sysret_errno(ret); if (ret > 0) memif->write(pbuf, ret, &buf[0]); return ret_errno; }
やっていることは単純だ。fds.lookup
というのはファイルディスクリプタのテーブルで、stdin, stdout, stderr
の扱い方を格納したテーブルだ。そこからread()
(これはx86で実装されたホストのread()関数)を呼び出し、ホストのシステム上でread()を実行する。
その結果を、RISC-Vシミュレータ上のメモリ空間に書き込む。それをmemif->write()
で実現している。
実行していることは単純なのだ。
これを、自作RISC-Vシミュレータ上で実装しているAPIに置き換える。
reg_t RiscvSyscall_t::sys_read(reg_t fd, reg_t pbuf, reg_t len, reg_t a3, reg_t a4, reg_t a5, reg_t a6) { Byte_t *buf = new Byte_t[len]; ssize_t ret = read(fds.lookup(fd), &buf[0], len); reg_t ret_errno = sysret_errno(ret); if (ret > 0) { for (unsigned int idx = 0; idx < len; idx++) { m_pe_thread->StoreMemoryDebug (pbuf + idx, Size_Byte, &buf[idx]); } } return ret_errno; }
ホストの実装は同じで、memif
インタフェースへのアクセスをオリジナルのインタフェースに変更しただけだ。
バースト転送のAPIを持っていなかったので、1バイトずつ書き込んでいる。
これをすべてのAPIにおいて実装し(というか単純にコピーして関数を置き換えた)、RISC-V自作シミュレータに組み込んで実行した。
printf()
、fopen()
などで構成された以下のプログラムをRISC-V向けのGCCでコンパイルし、Proxy Kernelを組み込んでシミュレーションした。
- file_access.c
#include <stdio.h> #include <stdlib.h> int main () { printf("Hello World\n"); FILE *fp; if ((fp = fopen("sample_hello.txt", "w")) == NULL) { perror ("sample_hello.txt"); exit(EXIT_FAILURE); } fprintf (fp, "Hello World\n"); fclose (fp); if ((fp = fopen("sample_hello.txt", "r")) == NULL) { perror ("sample_hello.txt"); exit(EXIT_FAILURE); } char cin[100]; fscanf (fp, "%s", cin); printf ("%s\n", cin); return 0; }
$ riscv64-unknown-elf-gcc file_access.c -o file_access_riscv $ riscv_iss --bit-mode 64 --use-pk /home/msyksphinz/riscv64//riscv64-unknown-elf/bin/pk --binfile file_access_riscv --debug --out file_access_riscv.forest --trace-hier --trace-out file_access_riscv.trace --max 500000 ... <Loading section Code .htif 0x0000000000000123> <Loading section ... .data 0x0000000000000123> <Loading section ... .data 0x0000000000000123> <Loading section Code .data 0x0000000000000123> <Loading section ... .sdata 0x0000000000000123> <Loading section ... .sdata 0x0000000000000123> <Loading section Code .sdata 0x0000000000000123> <Loading section ... .bss 0x0000000000000001> <Loading section ... .bss 0x0000000000000001> <Loading section Code .bss 0x0000000000000001> <Info: Set Entry Point as 1000> <Info: Set Debug Module> <Info: Set Debug Module Done> Hello World Hello $ cat sample_hello.txt Hello World
やった!成功だ。Hello Worldが正しく実行され、またファイルにも正しく書き込まれている。すばらしい。