Hello Worldのプログラムを動かしながら、RISC-V Spikeシミュレータのログを追っていき、RISC-Vのブートシーケンスを追っていく、その2。 今回はRISC-Vプログラムのロード部分。
parse_args()
により関数をコールを行う。
riscv-pk/pk/pk.c
static size_t parse_args(arg_buf* args) { long r = frontend_syscall(SYS_getmainvars, va2pa(args), sizeof(*args), 0, 0, 0, 0, 0); kassert(r == 0); uint64_t* pk_argv = &args->buf[1]; // pk_argv[0] is the proxy kernel itself. skip it and any flags. size_t pk_argc = args->buf[0], arg = 1; for ( ; arg < pk_argc && *(char*)(uintptr_t)pk_argv[arg] == '-'; arg++) handle_option((const char*)(uintptr_t)pk_argv[arg]); for (size_t i = 0; arg + i < pk_argc; i++) args->argv[i] = (char*)(uintptr_t)pk_argv[arg + i]; return pk_argc - arg; }
frontend_syscall()
は、フロントエンドに対してシステムコールを要求する。
- riscv-pk/pk/frondend.c
long frontend_syscall(long n, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) { static volatile uint64_t magic_mem[8]; static spinlock_t lock = SPINLOCK_INIT; spinlock_lock(&lock); magic_mem[0] = n; magic_mem[1] = a0; magic_mem[2] = a1; magic_mem[3] = a2; magic_mem[4] = a3; magic_mem[5] = a4; magic_mem[6] = a5; magic_mem[7] = a6; htif_syscall((uintptr_t)magic_mem); long ret = magic_mem[0]; spinlock_unlock(&lock); return ret; }
htif_syscall
はフロントエンドに対してシステムコールを行う。
- riscv-pk/machine/htif.c
void htif_syscall(uintptr_t arg) { do_tohost_fromhost(0, 0, arg); } static void do_tohost_fromhost(uintptr_t dev, uintptr_t cmd, uintptr_t data) { spinlock_lock(&htif_lock); __set_tohost(dev, cmd, data); while (1) { uint64_t fh = fromhost; if (fh) { if (FROMHOST_DEV(fh) == dev && FROMHOST_CMD(fh) == cmd) { fromhost = 0; break; } __check_fromhost(); } } spinlock_unlock(&htif_lock); }
tohost
とfromhost
はpk.dmp
に以下のアドレスで定義されている。
- pk.dmp
000000008000a000 <fromhost>: 000000008000a008 <tohost>:
SpikeシミュレータにおけるHTIFの扱い
riscv-fesvr/fesvr/htif.cc
に、Spikeでインスタンスされるhtifの型が定義されている。
htif.cc
において、プログラムをロードする時にtohost
とfromhost
のアドレスを定義している。
riscv-fesvr/fesvr/htif.cc
void htif_t::load_program() { ... std::map<std::string, uint64_t> symbols = load_elf(path.c_str(), &mem, &entry); if (symbols.count("tohost") && symbols.count("fromhost")) { tohost_addr = symbols["tohost"]; fromhost_addr = symbols["fromhost"]; } else { fprintf(stderr, "warning: tohost and fromhost symbols not in ELF; can't communicate with target\n"); }
load_elf()
load_elf()
の実装はriscv-fesvr/fesvr/elfloader.cc
に実装されている。これは特に特筆するものはなく、ひたすらelfをロードしているだけである。
riscv-fesvr/fesvr/elfloader.cc
std::map<std::string, uint64_t> load_elf(const char* fn, memif_t* memif, reg_t* entry) { int fd = open(fn, O_RDONLY); struct stat s; assert(fd != -1); if (fstat(fd, &s) < 0) abort(); size_t size = s.st_size; char* buf = (char*)mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); assert(buf != MAP_FAILED); close(fd); assert(size >= sizeof(Elf64_Ehdr)); const Elf64_Ehdr* eh64 = (const Elf64_Ehdr*)buf; assert(IS_ELF32(*eh64) || IS_ELF64(*eh64)); ... if (IS_ELF32(*eh64)) LOAD_ELF(Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Sym); else LOAD_ELF(Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Sym); munmap(buf, size); return symbols; }