コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2024年7月20日 >>> 2024年7月7日
link もっと後

2024年7月20日

OpenSBIを調べる - メモリマップ

目次: Linux

OpenSBIのブート部分を調べます。OpenSBIはいくつか動作モードがあるのですが、payloadを持ったタイプを調べます。対象のファイル名はbuild/platform/generic/firmware/fw_payload.binもしくは.elfです。

今回はメモリマップについて調べます。調べている途中で気づいたのですがOpenSBI 1.4と1.5で変わっている部分があるので、その話もしたいと思います。

OpenSBIのメモリマップを決めるリンカースクリプト

/* opensbi/firmware/fw_payload.elf.ldS */

OUTPUT_ARCH(riscv)
ENTRY(_start)

SECTIONS
{
	#include "fw_base.ldS"

...


/* opensbi/firmware/fw_base.ldS */

	. = FW_TEXT_START;
	/* Don't add any section between FW_TEXT_START and _fw_start */
	PROVIDE(_fw_start = .);

...

バイナリのメモリマップを決めるのはリンカースクリプトfw_payload.elf.ldSです。fw_base.ldSは各モード共通で使われるリンカースクリプトです。FW_TEXT_STARTがメモリマップの開始アドレスとなります。

FW_TEXT_STARTが定義されている場所はMakefileで、

FW_TEXT_STARTの定義

# opensbi/firmware/objects.mk

ifdef FW_TEXT_START
firmware-genflags-y += -DFW_TEXT_START=$(FW_TEXT_START)
else
firmware-genflags-y += -DFW_TEXT_START=0x0
endif

FW_TEXT_STARTが定義されていればその値を使い、未定義ならば0x0です。

QEMU向けにビルドする場合はPLATFORM=genericと指定しました。このときMakefileはplatform/generic/objects.mkが使用されますが、FW_TEXT_STARTの定義がOpenSBI 1.4と1.5で違います。

OpenSBI 1.4と1.5の差分

# platform/generic/objects.mk (OpenSBI 1.4)

# Blobs to build
FW_TEXT_START=0x80000000
FW_DYNAMIC=y
FW_JUMP=y

# platform/generic/objects.mk (OpenSBI 1.5)

# Blobs to build
FW_DYNAMIC=y
FW_JUMP=y

OpenSBI 1.4ではplatform/generic/objects.mkにてFW_TEXT_START=0x80000000が定義されていました。firmware-genflags-yに追加されるオプションは前者(-DFW_TEXT_START=0x80000000)です。

OpenSBI 1.5ではplatform/generic/objects.mkにてFW_TEXT_STARTが定義されないため、firmware-genflags-yに追加されるオプションは後者(-DFW_TEXT_START=0x0)です。

QEMUのアドレス0x0にはメモリがないのになぜこの設定で動作するか?ですが、OpenSBI 1.4ではアドレス固定でしたが、OpenSBI 1.5ではPIE(Position Independent Executable、位置独立実行形式)でビルドされるため、OpenSBI 1.5はロードアドレスがどこであっても動作します。

実験してみるとわかりますが、FW_TEXT_STARTを変な値に変えても動作します。OpenSBI 1.4同様にFW_TEXT_START=0x80000000にしても動作します。

が、変更する意味はあまりないでしょう。私が思いつく範囲だと、gdbでPIEのシンボルを読みたい場合はsymbol-file (file) -o 0x80000000のようにしますが、FW_TEXT_START=0x80000000にしておくとこのコマンドを一々入力せず済む、くらいです。他にも何かあれば教えていただけると嬉しいです。

メモリマップと言いつつエントリアドレスしか説明していなかったので、次回はエントリアドレス以降のメモリマップも調べたいと思います。

編集者:すずき(2024/08/09 12:07)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年7月19日

OpenSBIを調べる - ブート処理とペイロード

目次: Linux

OpenSBIのブート部分を調べます。OpenSBIはいくつか動作モードがあるのですが、今回はペイロードを持ったタイプを調べます。対象のファイル名はbuild/platform/generic/firmware/fw_payload.binもしくは.elfです。

OpenSBIのエントリポイントは_startで、アセンブラ実装のコードです。

OpenSBIのエントリと処理の概要

//opensbi/firmware/fw_base.S

_start
  _try_lottery
  _bss_zero
  fw_save_info()     // firmware/fw_payload.S
    //何もしない
  fw_platform_init() // platform/generic/platform.cだが独自の実装もある(k210など)
  _scratch_init
    //scratch領域を確保する、先頭アドレスはtpレジスタに格納される
    fw_next_arg1()     // firmware/fw_payload.S
      // FW_PAYLOAD_FDT_ADDRが定義済みならFW_PAYLOAD_FDT_ADDRを返す
      // FW_PAYLOAD_FDT_OFFSETが定義済みなら_fw_start + FW_PAYLOAD_FDT_OFFSETを返す
      // いずれも未定義ならばa1レジスタの値を返す
      // 結果はscratch->next_arg1に格納
    fw_next_addr()     // firmware/fw_payload.S
      // payload_binのアドレスを返す
      // FW_PAYLOAD_PATHが定義済みなら.incbin FW_PAYLOAD_PATH
      // 未定義ならwfi命令の無限ループ
      // 結果はscratch->next_addrに格納
    fw_next_mode()     // firmware/fw_payload.S
      // PRV_Sを返す
      // 結果はscratch->next_modeに格納
    fw_options()
      // 0を返す
      // 結果はscratch->optionsに格納
  _start_warm
    // tpの値をmscratchレジスタへ
    _reset_regs
    // mscratchレジスタを第一引数(a0レジスタ)にしてsbi_init()を呼ぶ
    sbi_init(struct sbi_scratch *scratch)

結構長いですが、ブートに必要な情報を扱っている部分を抜粋してコメントを付け加えました。

プラットフォーム領域

ブート処理で重要なデータがstruct sbi_platform platformです。これは各platformごとに定義されていて、QEMU用のバイナリであればgeneric/platform.cの定義が使われるはずです。fw_platform_init()関数にて初期化されます。

プラットフォーム領域の定義

//opensbi/platform/generic/platform.c

struct sbi_platform platform = {
	.opensbi_version	= OPENSBI_VERSION,
	.platform_version	=
		SBI_PLATFORM_VERSION(CONFIG_PLATFORM_GENERIC_MAJOR_VER,
				     CONFIG_PLATFORM_GENERIC_MINOR_VER),
	.name			= CONFIG_PLATFORM_GENERIC_NAME,
	.features		= SBI_PLATFORM_DEFAULT_FEATURES,
	.hart_count		= SBI_HARTMASK_MAX_BITS,
	.hart_index2id		= generic_hart_index2id,
	.hart_stack_size	= SBI_PLATFORM_DEFAULT_HART_STACK_SIZE,
	.heap_size		= SBI_PLATFORM_DEFAULT_HEAP_SIZE(0),
	.platform_ops_addr	= (unsigned long)&platform_ops
};

Cの構造体をアセンブラのコードから参照するときはちょっと面倒で、

  • platformの先頭アドレスをレジスタにロード、例えばa4
  • SBI_PLATFORM_AAAA(a4)こんな名前のマクロで先頭アドレス+オフセットを計算する
  • 構造体の各メンバーのアドレスを使ってロードストアする

こんな処理を行う必要があります。Cの構造体を書き換えるとアセンブラ側で定義しているオフセットとずれてしまう問題があるため、

オフセットマクロのチェックをしているコード

//opensbi/include/sbi/sbi_platform.h

/**
 * Prevent modification of struct sbi_platform from affecting
 * SBI_PLATFORM_xxx_OFFSET
 */
_Static_assert(
	offsetof(struct sbi_platform, opensbi_version)
		== SBI_PLATFORM_OPENSBI_VERSION_OFFSET,
	"struct sbi_platform definition has changed, please redefine "
	"SBI_PLATFORM_OPENSBI_VERSION_OFFSET");

(...以下同様に続く...)

全てのオフセットマクロに対してアサーションが書かれています。sbi_platform構造体が頻繁に変わることはないでしょうし、メンバもせいぜい10個くらいなので、手書き&気合で乗り切れます。

こういうオフセットマクロがあまりにも多い場合は、Cのコードからオフセットマクロを自動生成する場合もあります。OSのコードでたまに見かけますね。

スクラッチ領域

もうひとつ重要なデータはスクラッチ領域(データ型はstruct sbi_scratch)です。アセンブラのコード_scratch_initにて領域の確保と値の設定が行われ、Cのコードではscratch->aaaaのような形で参照します。

まず領域の確保ですが、OpenSBIのバイナリの終端部分から下記のようにスタックとヒープ領域が確保されます。

  • バイナリの終端: _fw_end
  • スタック領域: (hart数:platform.hart_count) x (hartごとのスタックサイズ:platform.hart_stack_size)バイト
  • ヒープ領域: platform.heap_sizeバイト

スクラッチ領域はスタック領域の末尾に確保されます。またtpレジスタはスクラッチ領域の先頭アドレスとなります。この値は後でsbi_init()関数の第一引数に渡すために使います。

ブート処理

OpenSBIからペイロード(今回はLinuxをペイロードにしています)へ制御を移す部分のコードを調べます。

sbi_init()関数とペイロードに制御を移す処理

sbi_init()
  init_coldboot()      //1コアだけ : lib/sbi/sbi_init.c
    sbi_AAAAAA_init() //初期化関数の名前はこんな感じ
    //
    //初期化、メッセージ表示など
    //
    sbi_hsm_hart_start_finish()
      hsm_start_ticket_release()
      sbi_hart_switch_mode()
        // mepc = next_addr : payload_binのアドレス
        // a0 = arg0 = hartid
        // a1 = arg1 = next_arg1 : FDTのアドレス
        // mretでmepcに飛ばす、つまりペイロードであるLinuxに飛ぶ
  init_warmboot() //他のコア   : lib/sbi/sbi_init.c
    ...

いろんな処理を行ないますが、ペイロードへ制御を移す部分はsbi_hsm_hart_start_finish()とsbi_hart_switch_mode()関数です。

sbi_hsm_hart_start_finish()とsbi_hart_switch_mode()関数

//opensbi/lib/sbi/sbi_hsm.c

void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch,
					  u32 hartid)
{
	unsigned long next_arg1;
	unsigned long next_addr;
	unsigned long next_mode;
	struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
							    hart_data_offset);

	if (!__sbi_hsm_hart_change_state(hdata, SBI_HSM_STATE_START_PENDING,
					 SBI_HSM_STATE_STARTED))
		sbi_hart_hang();

	next_arg1 = scratch->next_arg1;
	next_addr = scratch->next_addr;
	next_mode = scratch->next_mode;
	hsm_start_ticket_release(hdata);

	sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false);    //★制御を移す関数★
}


//opensbi/lib/sbi/sbi_hart.c

void __attribute__((noreturn))
sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
		     unsigned long next_addr, unsigned long next_mode,
		     bool next_virt)
{
#if __riscv_xlen == 32
	unsigned long val, valH;
#else
	unsigned long val;
#endif

	switch (next_mode) {
	case PRV_M:
		break;
	case PRV_S:
		if (!misa_extension('S'))
			sbi_hart_hang();
		break;
	case PRV_U:
		if (!misa_extension('U'))
			sbi_hart_hang();
		break;
	default:
		sbi_hart_hang();
	}

	val = csr_read(CSR_MSTATUS);
	val = INSERT_FIELD(val, MSTATUS_MPP, next_mode);
	val = INSERT_FIELD(val, MSTATUS_MPIE, 0);
#if __riscv_xlen == 32
	if (misa_extension('H')) {
		valH = csr_read(CSR_MSTATUSH);
		valH = INSERT_FIELD(valH, MSTATUSH_MPV, next_virt);
		csr_write(CSR_MSTATUSH, valH);
	}
#else
	if (misa_extension('H'))
		val = INSERT_FIELD(val, MSTATUS_MPV, next_virt);
#endif
	csr_write(CSR_MSTATUS, val);
	csr_write(CSR_MEPC, next_addr);

	if (next_mode == PRV_S) {
		if (next_virt) {
			csr_write(CSR_VSTVEC, next_addr);
			csr_write(CSR_VSSCRATCH, 0);
			csr_write(CSR_VSIE, 0);
			csr_write(CSR_VSATP, 0);
		} else {
			csr_write(CSR_STVEC, next_addr);
			csr_write(CSR_SSCRATCH, 0);
			csr_write(CSR_SIE, 0);
			csr_write(CSR_SATP, 0);
		}
	} else if (next_mode == PRV_U) {
		if (misa_extension('N')) {
			csr_write(CSR_UTVEC, next_addr);
			csr_write(CSR_USCRATCH, 0);
			csr_write(CSR_UIE, 0);
		}
	}

	//★各引数に入っている値は下記の通り★
	//arg0 = hartid
	//arg1 = scratch->next_arg1
	//next_addr = scratch->next_addr
	//next_mode = scratch->next_mode
	//next_virt = false

	register unsigned long a0 asm("a0") = arg0;
	register unsigned long a1 asm("a1") = arg1;
	__asm__ __volatile__("mret" : : "r"(a0), "r"(a1));
	__builtin_unreachable();
}

レジスタa0とa1に引数を入れ、mepcレジスタにペイロード先頭のアドレス(scratch->next_addr)を設定してmretします。

ペイロード先頭のアドレス(scratch->next_addr)を取得する処理概要

    fw_next_addr()     // firmware/fw_payload.S
      // payload_binのアドレスを返す
      // FW_PAYLOAD_PATHが定義済みなら.incbin FW_PAYLOAD_PATH
      // 未定義ならwfi命令の無限ループ
      // 結果はscratch->next_addrに格納

なぜscratch->next_addrにペイロード先頭のアドレスが入っているのか?については再掲するとともに、該当部分のソースコードを掲載します。

ペイロード先頭のアドレス(scratch->next_addr)を取得するコード

//opensbi/firmware/fw_payload.S

	.section .entry, "ax", %progbits
	.align 3
	.global fw_next_addr
	/*
	 * We can only use a0, a1, and a2 registers here.
	 * The next address should be returned in 'a0'.
	 */
fw_next_addr:
	lla	a0, payload_bin    //★payload_binのアドレスを返す★
	ret

...

	.section .payload, "ax", %progbits
	.align 4
	.globl payload_bin
payload_bin:
#ifndef FW_PAYLOAD_PATH    //★FW_PAYLOAD_PATHが未定義ならwfi命令の無限ループ★
	wfi
	j	payload_bin
#else
	.incbin	FW_PAYLOAD_PATH    //★FW_PAYLOAD_PATHが定義済みならFW_PAYLOAD_PATHで指定されたファイルをこの場所に展開★
#endif


//opensbi/firmware/fw_base.S

	/* Store next address in scratch space */
	MOV_3R	s0, a0, s1, a1, s2, a2
	call	fw_next_addr
	REG_S	a0, SBI_SCRATCH_NEXT_ADDR_OFFSET(tp)    //★結果はscratch->next_addrに格納★
	MOV_3R	a0, s0, a1, s1, a2, s2

最後にFW_PAYLOAD_PATHマクロはどこから来るかを紹介しておきます。

FW_PAYLOAD_PATHマクロの定義元

#opensbi/firmware/objects.mk

firmware-bins-$(FW_PAYLOAD) += fw_payload.bin
ifdef FW_PAYLOAD_PATH
FW_PAYLOAD_PATH_FINAL=$(FW_PAYLOAD_PATH)
else
FW_PAYLOAD_PATH_FINAL=$(platform_build_dir)/firmware/payloads/test.bin
endif
firmware-genflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_PATH=\"$(FW_PAYLOAD_PATH_FINAL)\"

ビルド時に指定したFW_PAYLOAD_PATH変数が、Makefile内で-Dオプションとしてコンパイラに渡されてマクロとして定義されます。シンプルですね。

次回以降はメモリマップやデバイスツリーについて調べようと思います。

編集者:すずき(2024/10/25 02:09)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年7月15日

KiCadが動かなくなったのでビルド(Debian Trixie編)

目次: Arduino

Debian Testingをアップデートしたところ先日ビルドしたKiCad 7.0が動かなくなり、今回はビルドすらできなくなりました。cmakeを実行するとTKIGESライブラリが見つからないと言われてしまいます。

KiCadのビルドエラー
(略)
-- Found the following HarfBuzz libraries:
--  HarfBuzz (required): /usr/lib/x86_64-linux-gnu/libharfbuzz.so
-- Found HarfBuzz: /usr/include/harfbuzz (found version "8.3.0")
-- Found Fontconfig: /usr/lib/x86_64-linux-gnu/libfontconfig.so (found version "2.15.0")
-- Checking for module 'ngspice'
--   Found ngspice, version 42
-- Found ngspice: /usr/include
-- Found OCC: /usr/include/opencascade (found version "7.8.1")

*** OpenCascade library missing ***
Could not find a library for TKIGES at /usr/lib/x86_64-linux-gnu
Verify your OpenCascade installation or pass CMake
  the library directory as '-DOCC_LIBRARY_DIR=<path>'

CMake Error at cmake/FindOCC.cmake:192 (message):
Call Stack (most recent call first):
  CMakeLists.txt:807 (find_package)

-- Configuring incomplete, errors occurred!

エラーメッセージさん曰く、OpenCascadeのライブラリが見つからないとのこと。バージョンを確認するとDebian StableはOpenCascad 7.6.3、Debian TestingはOpenCascade 7.8.1でした。この間にlibTKIGES.soのライブラリ名が変わったようです。

OpenCascadeのソースコード(リポジトリへのリンク)のコミットログを見ていると、ライブラリ名が一気に変わった瞬間がありました。

OpenCascadeの参考となるコミットメッセージ
commit bd651bbbd9e30fc5a73d32d11e0ea1a1821afd76
Author: dpasukhi <dpasukhi@opencascade.com>
Date:   Sun Nov 19 11:09:33 2023 +0000

    0033531: Configuration - Rework DataExchange ToolKits organization

    Integrated DE plugin functionality.
    Reworked DE components:
     - TKDESTEP: Handling STEP file format.
     - TKDEOBJ: Handling OBJ file format.
     - TKDEIGES: Handling IGES file format.
     - TKDEGLTF: Handling GLTF file format.
     - TKDEVRML: Handling VRML file format.
     - TKDEPLY: Handling PLY file format.
     - TKDESTL: Handling STL file format.
    Reworked DE DRAW components:
      TKXSDRAWSTEP: Container for DE command to work with STEP.
      TKXSDRAWOBJ: Container for DE command to work with OBJ.
      TKXSDRAWIGES: Container for DE command to work with IGES.
      TKXSDRAWGLTF: Container for DE command to work with GLTF.
      TKXSDRAWVRML: Container for DE command to work with VRML.
      TKXSDRAWPLY: Container for DE command to work with PLY.
      TKXSDRAWSTL: Container for DE command to work with STL.
    TKXSDRAW rework to be base DRAW plugin to keep DE session and utils.
    Updated documentation
    Updated samples

TKIGES以外にも盛大に変わっていて、OpenCascadeを使っている人たちは全滅しているんじゃないかな?と思います。どこで変わったか調べるとv7.7.2とv7.8.0の間で変わったようですね。

OpenCascadeの変更が入ったバージョン探し
$ git log V7_7_2..V7_8_0 | grep bd651b

commit bd651bbbd9e30fc5a73d32d11e0ea1a1821afd76

コミットの中身を参考にしつつKiCadを修正します。

KiCadの修正内容

diff --git a/cmake/FindOCC.cmake b/cmake/FindOCC.cmake
index af249c9ce2..923ccb4c7a 100644
--- a/cmake/FindOCC.cmake
+++ b/cmake/FindOCC.cmake
@@ -45,7 +45,6 @@ set( OCC_LIBS
     TKGeomAlgo
     TKGeomBase
     TKHLR
-    TKIGES
     TKLCAF
     TKMath
     TKMesh
@@ -55,18 +54,16 @@ set( OCC_LIBS
     TKPrim
     TKService
     TKShHealing
-    TKSTEP209
-    TKSTEPAttr
-    TKSTEPBase
-    TKSTEP
-    TKSTL
     TKTObj
     TKTopAlgo
     TKV3d
-    TKVRML
+    TKDESTL
+    TKDEVRML
     TKXCAF
-    TKXDEIGES
-    TKXDESTEP
+    TKDE
+    TKDECascade
+    TKDEIGES
+    TKDESTEP
     TKXMesh
     TKXmlL
     TKXml

OpenCascadeやKiCadの中身を全くわかっていないので、APIや実装が大きく変わっていたらお手上げでした。しかし幸いなことにライブラリ名が変わっただけなので、OpenCascadeのライブラリを探しているcmake/FindOCC.cmakeの修正だけで乗り切れました。

Debian TestingのKiCadは8.0系に移行しましたし、そろそろKiCad 7.0系を引きずるのをやめてバージョンアップを考えないといけないかなあ……?

編集者:すずき(2024/07/16 16:36)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年7月13日

RISC-V 64向けLinuxブートローダー(OpenSBI)の構築

目次: Linux

以前、Berkeley Bootloader(riskv-pkのbbl)を使用してRISC-V Linuxを立ち上げました(2019年2月26日の日記参照)が、今回はOpenSBIを使ってLinuxの起動を試みようと思います。

  • クロスコンパイラ: crosstool-NG
  • カーネル: linux
  • ブートローダ: OpenSBI
  • ルートファイルシステム: busybox + 手作業
  • エミュレータ: qemu

前回との違いはブートローダをbblからOpenSBIに変更したことです。

クロスコンパイラ

前回同様なので省略します。

ルートファイルシステム

前回同様にbusyboxと手作業でinitramfsを作りますが、ビルドディレクトリを使うように手順を変更しています。バージョンは1.35.0を使います(タグ名: 1_35_0)。

busyboxコード取得、セットアップ、ビルド
$ git clone https://git.busybox.net/busybox
$ cd busybox
$ mkdir build

$ make \
  O=build \
  ARCH=riscv \
  CROSS_COMPILE=riscv64-unknown-linux-gnu- \
  menuconfig

- Settings  --->
  [*] Build static binary (no shared libs)

$ make \
  O=build \
  ARCH=riscv \
  CROSS_COMPILE=riscv64-unknown-linux-gnu- \
  all

ビルドに成功すると実行ファイルbusyboxが生成されるはずです。次にinitramfsを作成します。基本的な流れはディレクトリに必要なファイルを配置し、cpioで固めるだけです。

initramfs作成
$ mkdir initramfs-work-riscv
$ mkdir initramfs-work-riscv/root
$ cd initramfs-work-riscv/root

$ mkdir bin
$ cp ../../build/busybox bin/
$ ln -s busybox bin/sh
$ ln -s bin/busybox init

$ mkdir dev
$ sudo cp -a /dev/tty* dev/

$ find . | cpio --format=newc -o > ../initramfs.cpio

横着してホストマシンのデバイスファイルをコピーしてます。RISC-V Linux上で何か操作したければinit, sh以外のシンボリックリンクも作った方が便利です。

カーネル

前回との違いは下記のとおりです。linux-nextでも手順は同じですが、linux-nextはたまにデグレして動作せず話がややこしくなるので、linux本線の少し古めのバージョンを使います。

  • linux-nextではなくlinux本線を使うように変更
  • ビルドディレクトリを使うように変更
  • initramfsを内蔵するように変更

バージョンは6.9を使います(タグ名: v6.9)。

Linuxのビルド
$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
$ cd linux
$ mkdir build

$ make \
  O=build \
  ARCH=riscv \
  CROSS_COMPILE=riscv64-unknown-linux-gnu- \
  defconfig

$ make \
  O=build \
  ARCH=riscv \
  CROSS_COMPILE=riscv64-unknown-linux-gnu- \
  menuconfig

- General setup  --->
  (../busybox/initramfs-work-riscv/initramfs.cpio) Initramfs source file(s)


$ make \
  O=build \
  ARCH=riscv \
  CROSS_COMPILE=riscv64-unknown-linux-gnu- \
  all

前回のようにexportで環境変数を設定しても良いですし、makeに逐一指定しても良いです。

ブートローダー

ブートローダーにはOpenSBIを使います。バージョンは1.5を使います(タグ名: v1.5)。

OpenSBIのビルド
$ git clone https://github.com/riscv-software-src/opensbi.git
$ cd opensbi

$ make \
  CROSS_COMPILE=riscv64-unknown-linux-gnu- \
  PLATFORM=generic \
  FW_PAYLOAD_PATH=../linux/build/arch/riscv/boot/Image \
  clean all

BBLもそうでしたけど、ブートローダーとカーネルは1つのバイナリになります。FW_PAYLOAD_PATHにはカーネルのバイナリ(vmlinuxではない)のパスを指定します。PLATFORMはSoCやボードの種類を指定します、QEMU向けの場合はgenericです。

エミュレータ

エミュレータにはQEMUを使います。システムにインストールされているバージョンが古い場合があるので、ソースコードからビルドする方法も紹介します。QEMU実行時にオプション--netdev userを使うのであれば、QEMUをビルドする前にlibslirp-devをインストールしておく必要があります。

QEMUのビルド
$ cd qemu
$ mkdir build
$ cd build

$ ../configure \
  --target-list=riscv32-softmmu,riscv32-linux-user,riscv64-softmmu,riscv64-linux-user \
  --enable-debug \
  --disable-docs
$ ninja

バージョンはv9.0.2を使いました。target-listやdisable-docsはビルド時間の短縮のためで指定しなくても良いです。使わないアーキテクチャ用(ARMとか)のエミュレータがビルドされるためビルドに若干時間がかかります。enable-debugはQEMUをデバッグする可能性があれば指定してください。

ブートローダーの動作確認

ここまでの準備でブートローダーとLinuxカーネルの初期部分は確認できるはずですので、動作確認します。

OpenSBI+Linuxカーネルの起動
$ cd qemu/build

$ ./qemu-system-riscv64 \
  -machine virt \
  -nographic \
  -bios none \
  -chardev stdio,id=con,mux=on \
  -serial chardev:con \
  -mon chardev=con,mode=readline \
  -smp 4 \
  -kernel ../../opensbi/build/platform/generic/firmware/fw_payload.bin

うまくいっていればこんな感じになるはずです。

OpenSBIのログ
OpenSBI v1.5
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : medeleg
Platform HART Count       : 4
Platform IPI Device       : aclint-mswi
Platform Timer Device     : aclint-mtimer @ 10000000Hz
Platform Console Device   : uart8250
Platform HSM Device       : ---
Platform PMU Device       : ---
Platform Reboot Device    : syscon-reboot
Platform Shutdown Device  : syscon-poweroff
Platform Suspend Device   : ---
Platform CPPC Device      : ---
Firmware Base             : 0x80000000
Firmware Size             : 357 KB
Firmware RW Offset        : 0x40000
Firmware RW Size          : 101 KB
Firmware Heap Offset      : 0x4f000
Firmware Heap Size        : 41 KB (total), 2 KB (reserved), 11 KB (used), 27 KB (free)
Firmware Scratch Size     : 4096 B (total), 416 B (used), 3680 B (free)
Runtime SBI Version       : 2.0

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*,1*,2*,3*
Domain0 Region00          : 0x0000000000100000-0x0000000000100fff M: (I,R,W) S/U: (R,W)
Domain0 Region01          : 0x0000000010000000-0x0000000010000fff M: (I,R,W) S/U: (R,W)
Domain0 Region02          : 0x0000000002000000-0x000000000200ffff M: (I,R,W) S/U: ()
Domain0 Region03          : 0x0000000080040000-0x000000008005ffff M: (R,W) S/U: ()
Domain0 Region04          : 0x0000000080000000-0x000000008003ffff M: (R,X) S/U: ()
Domain0 Region05          : 0x000000000c400000-0x000000000c5fffff M: (I,R,W) S/U: (R,W)
Domain0 Region06          : 0x000000000c000000-0x000000000c3fffff M: (I,R,W) S/U: (R,W)
Domain0 Region07          : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes
Domain0 SysSuspend        : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART Priv Version    : v1.12
Boot HART Base ISA        : rv64imafdch
Boot HART ISA Extensions  : sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 2 bits
Boot HART PMP Address Bits: 54
Boot HART MHPM Info       : 16 (0x0007fff8)
Boot HART Debug Triggers  : 2 triggers
Boot HART MIDELEG         : 0x0000000000001666
Boot HART MEDELEG         : 0x0000000000f0b509
[    0.000000] Linux version 6.9.0 (katsuhiro@blackbird) (riscv64-unknown-linux-gnu-gcc (GCC) 14.1.0, GNU ld (GNU Binutils) 2.42.50.20240622) #1 SMP Tue Jul 16 18:44:37 JST 2024
[    0.000000] random: crng init done
[    0.000000] Machine model: riscv-virtio,qemu
[    0.000000] SBI specification v2.0 detected
[    0.000000] SBI implementation ID=0x1 Version=0x10005
[    0.000000] SBI TIME extension detected
[    0.000000] SBI IPI extension detected
[    0.000000] SBI RFENCE extension detected
[    0.000000] SBI SRST extension detected
[    0.000000] SBI DBCN extension detected
[    0.000000] efi: UEFI not found.
...

今後はOpenSBIやLinuxカーネルの動作を調べる予定です。

編集者:すずき(2024/10/25 02:10)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする




link もっと前
2024年7月20日 >>> 2024年7月7日
link もっと後

管理用メニュー

link 記事を新規作成

<2024>
<<<07>>>
-123456
78910111213
14151617181920
21222324252627
28293031---

最近のコメント5件

  • link 21年9月20日
    すずきさん (11/19 01:04)
    「It was my pleasure.」
  • link 21年9月20日
    whtさん (11/17 23:41)
    「This blog solves my ...」
  • link 24年10月1日
    すずきさん (10/06 03:41)
    「xrdpで十分動作しているので、Wayl...」
  • link 24年10月1日
    hdkさん (10/03 19:05)
    「GNOMEをお使いでしたら今はWayla...」
  • link 24年10月1日
    すずきさん (10/03 10:12)
    「私は逆にVNCサーバーに繋ぐ使い方をした...」

最近の記事3件

  • link 23年4月10日
    すずき (12/04 01:40)
    「[Linux - まとめリンク] 目次: Linux関係の深いまとめリンク。目次: RISC-V目次: ROCK64/ROCK...」
  • link 24年11月18日
    すずき (12/04 01:39)
    「[nvJPEGとNVJPGとJetson APIその1 - nvJPEG decoupled API] 目次: Linux半年...」
  • link 24年11月28日
    すずき (12/01 00:53)
    「[BIOS/UEFI画面に入る方法] PCは起動時にあるキーを押すとBIOS/UEFIの設定画面に遷移します。良く見るパターン...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 12/04 01:40