RISC-Vのハイパーバイザについて調べているとき登場したが、Supervisor Binary Interface(SBI)というものが定義されている。
図. RISC-Vのソフトウェアスタック (https://riscv.org/wp-content/uploads/2015/01/riscv-software-stack-bootcamp-jan2015.pdf より抜粋)
詳細はRISC-V SBI specificationに説明がされている。
SBIというのはスーパバイザ実行環境(Supervisor Execution Environment)で実行できる機能群を示している。 SBIはBerkeley Boot Loader(BBL)上で実装されており、以下のSBIコールが定義されている。
Type | Function | Function ID |
---|---|---|
Timer | sbi_set_timer | 0 |
IPI | sbi_clear_ipi sbi_send_ipi |
3 4 |
Memory Model | sbi_remote_fence_i sbi_remote_sfence_vma sbi_remote_sfence_vma_asid |
5 6 7 |
Console | sbi_console_putchar sbi_console_getchar |
1 2 |
Shutdown | sbi_shutdown | 8 |
ここからは、それぞれのSBIコールの中身を、riscv-pk/machine/mtrap.c
から観測していく。
タイマー
void sbi_set_timer(uint64_t stime_value)
mtrap.c
void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) { ... case SBI_SET_TIMER: #if __riscv_xlen == 32 retval = mcall_set_timer(arg0 + ((uint64_t)arg1 << 32)); #else retval = mcall_set_timer(arg0); #endif break; ... } static uintptr_t mcall_set_timer(uint64_t when) { *HLS()->timecmp = when; clear_csr(mip, MIP_STIP); set_csr(mie, MIP_MTIP); return 0; }
IPI (Inter processor Interrupt)
他のプロセッサへの割り込み処理は、HLSというメモリ空間を使用して実行するらしい。
void sbi_send_ipi(const unsigned long *hart_mask)
mtrap.c
void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc) { ... case SBI_SEND_IPI: ipi_type = IPI_SOFT; goto send_ipi; ... send_ipi: send_ipi_many((uintptr_t*)arg0, ipi_type); ... } static void send_ipi(uintptr_t recipient, int event) { if (((disabled_hart_mask >> recipient) & 1)) return; atomic_or(&OTHER_HLS(recipient)->mipi_pending, event); mb(); *OTHER_HLS(recipient)->ipi = 1; }
mtrap.h
// hart-local storage, at top of stack #define HLS() ((hls_t*)(MACHINE_STACK_TOP() - HLS_SIZE)) #define OTHER_HLS(id) ((hls_t*)((void*)HLS() + RISCV_PGSIZE * ((id) - read_const_csr(mhartid))))
メモリモデル
sfenceによるメモリのバリアも、マルチコアで同期をとる必要があるためIPIを使って割り込みを出している。
mtrap.c
void sbi_remote_fence_i(const unsigned long *hart_mask) void sbi_remote_sfence_vma(const unsigned long *hart_mask, unsigned long start, unsigned long size) void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask, unsigned long start, unsigned long size, unsigned long asid)
コンソール
putchar()
とgetchar()
である。これは普通にメモリストアして文字を書き出したりメモリリードして文字を読み取る。
static uintptr_t mcall_console_putchar(uint8_t ch) { if (uart) { uart_putchar(ch); } else if (htif) { htif_console_putchar(ch); } return 0; } static uintptr_t mcall_console_getchar() { if (uart) { return uart_getchar(); } else if (htif) { return htif_console_getchar(); } else { return '\0'; } }
Shutdown
シャットダウン用のSBIも定義されている。これも単純にfinisherとして定義されたメモリ位置を操作するだけである。
void poweroff(uint16_t code) { printm("Power off\n"); finisher_exit(code); if (htif) { htif_poweroff(); } else { while (1); } } void finisher_exit(uint16_t code) { if (!finisher) return; if (code == 0) { *finisher = FINISHER_PASS; } else { *finisher = code << 16 | FINISHER_FAIL; } }