Reading OpenSBI part3

Wednesday, March 17, 2021

前回 は、_relocate_doneまで読んだ。 続きから読み進めていく。

fw_base.S

firmware/fw_base.S

...
	li	ra, 0
	call	_reset_regs

	/* Zero-out BSS */
	la	s4, _bss_start
	la	s5, _bss_end
_bss_zero:
	REG_S	zero, (s4)
	add	s4, s4, __SIZEOF_POINTER__
	blt	s4, s5, _bss_zero

	/* Setup temporary trap handler */
	la	s4, _start_hang
	csrw	CSR_MTVEC, s4

	/* Setup temporary stack */
	la	s4, _fw_end
	li	s5, (SBI_SCRATCH_SIZE * 2)
	add	sp, s4, s5

	/* Allow main firmware to save info */
	MOV_5R	s0, a0, s1, a1, s2, a2, s3, a3, s4, a4
	call	fw_save_info
	MOV_5R	a0, s0, a1, s1, a2, s2, a3, s3, a4, s4

#ifdef FW_FDT_PATH
	/* Override previous arg1 */
	la	a1, fw_fdt_bin
#endif
...
...
_reset_regs:
	fence.i
	li sp, 0
	li gp, 0
	li tp, 0
	li t0, 0
	li t1, 0
	li t2, 0
	li s0, 0
	li s1, 0
	li a3, 0
	li a4, 0
	li a5, 0
	li a6, 0
	li a7, 0
	li s2, 0
	li s3, 0
	li s4, 0
	li s5, 0
	li s6, 0
	li s7, 0
	li s8, 0
	li s9, 0
	li s10, 0
	li s11, 0
	li t3, 0
	li t4, 0
	li t5, 0
	li t6, 0
	csrw CSR_MSCRATCH, 0
	ret
...

まず、_reset_regsを実行する。 _reset_regsでは、まず、命令ストリームをメモリと同期する。 これは、リロケーション先にジャンプしているので、必要である。リロケーションはメモリ書き込み命令にて行うため、命令キャッシュと同期していない場合がある。 その後、ra, a0, a1, a2レジスタ以外を0に初期化する。 加えて、mscratchレジスタも初期化する。 次に、bssセクションを0で初期化する。 _bss_startはページ境界配置されている。 _bss_endは8バイト境界配置されている。 __SIZEOF_POINTER__サイズ(4 byte for RV32, 8 byte for RV64)でストアを行う。 次に、一時的なトラップハンドラーを設定する。  _start_hangは8バイト境界配置されている。このアドレスをそのままmtvecレジスタへ代入しているため、下位2ビットは常に00である。 よって、Direct modeとして割込み時トラップを行う。 例外のトラップについては常にDirect modeである。

firmware/fw_base.S

...
	.section .entry, "ax", %progbits
	.align 3
	.globl _start_hang
_start_hang:
	wfi
	j	_start_hang
...

次に、作業用の一時的なスタックを用意する。 _fw_endより、SBI_SCRATCH_SIZE*2(8KB)分確保する。 計算したアドレスをspレジスタへ入れる。 Stack grows downwardなので、スタックの終わりアドレスである。

include/sbi/sbi_scratch.h

#define SBI_SCRATCH_SIZE	(0x1000)

次に、レジスタの退避を行う。 (s0, s1, s2, s3, s4) <- (a0, a1, a2, a3, a4)
fw_save_info関数を呼び出す。(ファームウェアの種類によって実装が違う。今回はFW_PAYLOAD)

firmware/fw_payload.S

...
fw_save_info:
	ret
...

レジスタの復帰を行う。 (a0, a1, a2, a3, a4) <- (s0, s1, s2, s3, s4)\

FW_FDT_PATHによってFDT(Flattend Device Tree)が指定されている場合は、a1レジスタをそのポインタ(fw_fdt_bin)で置き換える。

firmware/fw_base.S

...
#ifdef FW_FDT_PATH
	.section .rodata
	.align 4
	.globl fw_fdt_bin
fw_fdt_bin:
	.incbin FW_FDT_PATH
#ifdef FW_FDT_PADDING
	.fill FW_FDT_PADDING, 1, 0
#endif
#endif
...

.incbinディレクティブで、FDTのバイナリを配置している。 FW_FDT_PADDINGが指定されているときは、FW_FDT_PADDING回、1byteの0で埋める。

今回はここまで。次はプラットフォームのイニシャライズから。

OpenSBIOpenSBILinuxRISC-V

Reading OpenSBI part4

Reading OpenSBI part2