RISC-V Berkeley Boot Loader & Proxy Kernel のソースコード解析 @Vengineer 2017/1/9
RISC-VBerkeley Boot Loader & Proxy Kernel
のソースコード解析
@Vengineer2017/1/9
Vengineer DEATH
無限ゲームのなか
@Vengineer に居ますRISC-Vのブートについて調べてみました。
よろしくお願いします。
自己紹介
RISC-V:https://riscv.org/The Free and Open RISC ISA
Google / Microsoft / HPE / IBM / AORACLE / MD / NVIDIA /
Qualcomm / NXP / Rambus / IDT / Micron /Western Digital / HUAWEI / Mellanox /
Microsemi / LATTICE / SiFive / Esperanto Technologies / lowRISC /
ultraSoC / SH CONSULTING / bluespec / Codasip
RISC-V : github.com/riscv各種ソフトウェア
riscv-gnu-toolchain riscv-gcc / riscv-glibc / riscv-dejagnu riscv-binutils-gdb / riscv-newlibriscv-pkriscv-linux/riscv-pokyriscv-qemu/riscv-isa-sim/riscv-fesvrriscv-angleriscv-llvm/riscv-clang/riscv-go
GNU Compiler Toolchainhttps://github.com/riscv/riscv-gnu-toolchain
$ git clone --recursive \ https://github.com/riscv/riscv-gnu-toolchain
$ ./configure --prefix=/opt/riscv $ make
で、ツールチェインをビルドする
RISC-V Proxy Kernelhttps://github.com/riscv/riscv-pk
2つのパッケージ
RISC−V Proxy Kernel (pk) Baremetal Applicationを実行
Berkeley Boot Loader (bbl) Linux Kernelを実行
が実装されている
RISC-Vの権限階層
Supervisor Mode
User Mode
Hypervisor Mode
Machine Mode
mret
sret
hret
リセット解除
参考資料) : https://riscv.org/specifications/privileged-isa
RISC-Vの権限階層
Supervisor Mode
User Mode
Machine Mode
mret
sret
リセット解除
bblは、ここでLinuxを実行
pkは、ここでユーザアプリを実行
リセット解除後
リセット解除後、
下記のコードをMahine Modeにて実行する
・reset_vector (machine/mentry.S) ・do_reset (machine/mentyr.S) ・init_first_hart (machine/minit.c) ・boot_loader
pk用 (pk/pk.c) bbl用 (bbl/bbl.c)
Proxy Kernelの場合
boot_loader(pk用)void boot_loader(){ extern char trap_entry; write_csr(stvec, &trap_entry); write_csr(sscratch, 0); write_csr(sie, 0);
file_init(); // Supervisor modeで rest_of_boot_loader 関数を実行
enter_supervisor_mode(rest_of_boot_loader, pk_vm_init());}
enter_supervisor_modemchine/minit.cvoid enter_supervisor_mode(void (*fn)(uintptr_t), uintptr_t stack){ uintptr_t mstatus = read_csr(mstatus); mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_S); mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0); write_csr(mstatus, mstatus); write_csr(mscratch, MACHINE_STACK_TOP() - MENTRY_FRAME_SIZE); write_csr(mepc, fn); write_csr(sptbr, (uintptr_t)root_page_table >> RISCV_PGSHIFT); asm volatile ("mv a0, %0; mv sp, %0; mret" : : "r" (stack)); __builtin_unreachable();}
enter_supervisor_mode intptr_t mstatus = read_csr(mstatus); mstatus = INSERT_FIELD(mstatus, MSTATUS_MPP, PRV_S); mstatus = INSERT_FIELD(mstatus, MSTATUS_MPIE, 0); write_csr(mstatus, mstatus); write_csr(mscratch, MACHINE_STACK_TOP() - MENTRY_FRAME_SIZE); write_csr(mepc, fn); write_csr(sptbr, (uintptr_t)root_page_table >> RISCV_PGSHIFT); // mstatus / mscratch / mepc / sptbr を設定し、
asm volatile ("mv a0, %0; mv sp, %0; mret" : : "r" (stack)); // mret で Supervisor mode でプログラム(fn) を実行
参考資料) : http://csg.csail.mit.edu/F6.175/labs/exception.pdf
Supervisor modeでの実行?void enter_supervisor_mode(void (*fn)(uintptr_t), uintptr_t stack)
enter_supervisor_mode(rest_of_boot_loader, pk_vm_init());
参考) : http://csg.csail.mit.edu/F6.175/labs/exception.pdf
rest_of_boot_loaderstatic void rest_of_boot_loader(uintptr_t kstack_top){ arg_buf args;
size_t argc = parse_args(&args); if (!argc) panic("tell me what ELF to load!");
// load program named by argv[0] long phdrs[128]; current.phdr = (uintptr_t)phdrs; current.phdr_size = sizeof(phdrs);
// アプリケーション(args.argv[0])をDRAMにロードする
load_elf(args.argv[0], ¤t); // ロードしたアプリケーションを実行する
run_loaded_program(argc, args.argv, kstack_top);}
run_loaded_programstatic void run_loaded_program(size_t argc, char** argv, uintptr_t kstack_top){ // 途中略
trapframe_t tf; init_tf(&tf, current.entry, stack_top); __clear_cache(0, 0); write_csr(sscratch, kstack_top);
// ここでユーザーアプリを起動
start_user(&tf);}
start_userpk/entry.S .globl start_userstart_user: LOAD t0, 32*REGBYTES(a0) // a0はスタック
LOAD t1, 33*REGBYTES(a0) csrw sstatus, t0 csrw sepc, t1 // sscratch <= kstack_top 済み # restore x registers LOAD x1,1*REGBYTES(a0) // 途中略
# restore a0 last LOAD x10,10*REGBYTES(a0) # gtfo
sret // Supervisor mode => User modeへ
tf : trapframe_tpk/pk.htypedef struct{ long gpr[32]; long status; // LOAD t0, 32*REGBYTES(a0) : t0 => sstatus
long epc; // LOAD t1, 33*REGBYTES(a0) : t1 => sepc
long badvaddr; long cause; long insn;} trapframe_t;
Berkeley Boot Loaderの場合
boot_loader(bbl用)void boot_loader(){ extern char _payload_start, _payload_end;
// ここでカーネルをDRAMにロードする
load_kernel_elf(&_payload_start, &_payload_end - &_payload_start, &info); supervisor_vm_init();#ifdef PK_ENABLE_LOGO print_logo();#endif
mb(); elf_loaded = 1; enter_supervisor_mode((void *)info.entry, 0);}
load_kernel_elfbbl/kernel_load.cvoid load_kernel_elf(void* blob, size_t size, kernel_elf_info* info){ Elf_Ehdr* eh = blob; // 途中略
info->entry = eh->e_entry; // KernelのELF info->load_offset = bias; info->first_user_vaddr = min_vaddr; info->first_vaddr_after_user = ROUNDUP(max_vaddr - bias, RISCV_PGSIZE);
return;}
_payload_startbbl/payload.S
.section ".payload","a",@progbits
.align 3
.globl _payload_start, _payload_end_payload_start:.incbin BBL_PAYLOAD // --with-payload=で指定したバイナリ
_payload_end:
dummy_payloaddummy_payload/dummy_payload.c
asm (".globl _start\n\ _start: la sp, stack\n\ j entry\n\ .pushsection .rodata\n\ .align 4\n\ .skip 4096\n\ stack:\n\ .popsection");
dummy_payloaddummy_payload/dummy_payload.c
void entry(){ const char* message =
"This is bbl's dummy_payload. To boot a real kernel, reconfigure\n\bbl with the flag --with-payload=PATH, then rebuild bbl.\n"; while (*message) sbi_console_putchar(*message++); sbi_shutdown();
}
$ ./configure --with-payload=linux-kernel.elf
Machine-modeで例外が発生すると、
mtvec(アドレス)に飛ぶ
mtvecMachine Trap-Vector Base-Address Register (mtvec)
do_reset: // 途中略
# write mtvec and make sure it sticks la t0, trap_vector csrw mtvec, t0 csrr t1, mtvec1:bne t0, t1, 1b
trap_vectormachine/mentry.S
trap_vector: csrrw sp, mscratch, sp beqz sp, .Ltrap_from_machine_mode
STORE a0, 10*REGBYTES(sp) STORE a1, 11*REGBYTES(sp)
csrr a1, mcause bgez a1, .Lhandle_trap_in_machine_mode
trap_vector # This is an interrupt. Discard the mcause MSB and decode the rest. sll a1, a1, 1
# Is it a machine timer interrupt? li a0, IRQ_M_TIMER * 2 bne a0, a1, 1f li a1, TIMER_INTERRUPT_VECTOR j .Lhandle_trap_in_machine_mode
.Lhandle_trap_in_machine_mode.Lhandle_trap_in_machine_mode: // 途中略
1:auipc t0, %pcrel_hi(trap_table) # t0 <- %hi(trap_table) STORE t1, 6*REGBYTES(sp) sll t1, a1, 2 # t1 <- mcause << 2 STORE t2, 7*REGBYTES(sp) add t1, t0, t1 # t1 <- %hi(trap_table)[mcause] STORE s0, 8*REGBYTES(sp) LWU t1, %pcrel_lo(1b)(t1) # t1 <- trap_table[mcause] STORE s1, 9*REGBYTES(sp) mv a0, sp # a0 <- regs STORE a2,12*REGBYTES(sp) csrr a2, mepc # a2 <- mepc STORE a3,13*REGBYTES(sp) csrrw t0, mscratch, x0 # t0 <- user sp
.Lhandle_trap_in_machine_mode
STORE a4,14*REGBYTES(sp) // 途中略
STORE t0, 2*REGBYTES(sp) # sp
#ifndef __riscv_flen lw tp, (sp) # Move the emulated FCSR from x0's save slot into tp.#endif STORE x0, (sp) # Zero x0's save slot.
# Invoke the handler. jalr t1 // 要因ハンドラ(trap_vector)にジャンプ
#ifndef __riscv_flen sw tp, (sp) # Move the emulated FCSR from tp into x0's save slot.#endif
.Lhandle_trap_in_machine_mode
restore_mscratch: # Restore mscratch, so future traps will know they didn't come from M-mode. csrw mscratch, sp
restore_regs: # Restore all of the registers. LOAD ra, 1*REGBYTES(sp)
// 途中略
LOAD sp, 2*REGBYTES(sp) mret // ここでMachine-modeから抜ける
.Ltrap_from_machine_mode
.Ltrap_from_machine_mode: csrr sp, mscratch addi sp, sp, -INTEGER_CONTEXT_SIZE STORE a0,10*REGBYTES(sp) STORE a1,11*REGBYTES(sp) li a1, TRAP_FROM_MACHINE_MODE_VECTOR j .Lhandle_trap_in_machine_mode
TRAP_FROM_MACHINE_MODE_VECTOR
#define TRAP_FROM_MACHINE_MODE_VECTOR 14 .word __trap_from_machine_mode
__trap_from_machine_mode: jal trap_from_machine_mode j restore_regs // ここでMachie-modeから抜ける
trap_from_machine_modemachine/mtrap.cvoid trap_from_machine_mode(uintptr_t* regs, uintptr_t dummy, uintptr_t mepc){ uintptr_t mcause = read_csr(mcause); switch (mcause) { case CAUSE_FAULT_LOAD: case CAUSE_FAULT_STORE: return machine_page_fault(regs, mepc); default: bad_trap(); }}
trap_table (その1).data .align 6trap_table: .word bad_trap // Instruction address misaligned
.word bad_trap // Instruction access fault
.word illegal_insn_trap // Illegal instruction
.word bad_trap // Breakpoint
.word misaligned_load_trap // Load address misaligned
.word bad_trap // Load access fault
.word misaligned_store_trap// Store/AMO address misaligned
.word bad_trap // Store/AMO access fault
.word bad_trap // Environment call from U-mode
trap_table (その2)
.word mcall_trap // Environment call from S-mode
.word bad_trap // Environment call from H-mode
.word bad_trap // Environment call from M-mode
#define SOFTWARE_INTERRUPT_VECTOR 12 .word software_interrupt#define TIMER_INTERRUPT_VECTOR 13 .word timer_interrupt#define TRAP_FROM_MACHINE_MODE_VECTOR 14 .word __trap_from_machine_mode
mcall_trapmachine/mtrap.c
Supervisor modeからのコール( pk用として用意されている)void mcall_trap(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc){ uintptr_t n = regs[17], arg0 = regs[10], arg1 = regs[11], retval; switch (n) { case MCALL_HART_ID: retval = mcall_hart_id(); break;
// case が続く
おしまい