38
RISC-V Berkeley Boot Loader & Proxy Kernel のソースコード解析 @Vengineer 2017/1/9

RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

Embed Size (px)

Citation preview

Page 1: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

RISC-VBerkeley Boot Loader & Proxy Kernel

のソースコード解析

@Vengineer2017/1/9

Page 2: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

Vengineer DEATH

無限ゲームのなか

@Vengineer に居ますRISC-Vのブートについて調べてみました。

よろしくお願いします。

自己紹介

Page 3: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 4: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 5: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

GNU Compiler Toolchainhttps://github.com/riscv/riscv-gnu-toolchain

$ git clone --recursive \ https://github.com/riscv/riscv-gnu-toolchain

$ ./configure --prefix=/opt/riscv $ make

で、ツールチェインをビルドする

Page 6: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

RISC-V Proxy Kernelhttps://github.com/riscv/riscv-pk

2つのパッケージ

 RISC−V Proxy Kernel (pk)  Baremetal Applicationを実行

 Berkeley Boot Loader (bbl)  Linux Kernelを実行

が実装されている

Page 7: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

RISC-Vの権限階層

Supervisor Mode

User Mode

Hypervisor Mode

Machine Mode

mret

sret

hret

リセット解除

参考資料) : https://riscv.org/specifications/privileged-isa

Page 8: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

RISC-Vの権限階層

Supervisor Mode

User Mode

Machine Mode

mret

sret

リセット解除

bblは、ここでLinuxを実行

pkは、ここでユーザアプリを実行

Page 9: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

リセット解除後

リセット解除後、

下記のコードを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)

Page 10: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

Proxy Kernelの場合

Page 11: RISC-V : Berkeley Boot Loader & 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());}

Page 12: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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();}

Page 13: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 14: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 15: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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], &current); // ロードしたアプリケーションを実行する

run_loaded_program(argc, args.argv, kstack_top);}

Page 16: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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);}

Page 17: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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へ

Page 18: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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;

Page 19: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

Berkeley Boot Loaderの場合

Page 20: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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);}

Page 21: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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;}

Page 22: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

_payload_startbbl/payload.S

.section ".payload","a",@progbits

.align 3

.globl _payload_start, _payload_end_payload_start:.incbin BBL_PAYLOAD // --with-payload=で指定したバイナリ

_payload_end:

Page 23: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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");

Page 24: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 25: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

Machine-modeで例外が発生すると、

mtvec(アドレス)に飛ぶ

Page 26: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 27: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 28: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 29: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

.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

Page 30: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

.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

Page 31: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

.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から抜ける

Page 32: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

.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

Page 33: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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から抜ける

Page 34: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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(); }}

Page 35: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 36: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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

Page 37: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

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 が続く

Page 38: RISC-V : Berkeley Boot Loader & Proxy Kernelのソースコード解析

おしまい