コグノスケ


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

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

2024年7月24日

OpenSBIを調べる - デバイスツリーの扱い(genericプラットフォーム)

目次: Linux

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

今回はOpenSBIのデバイスツリー処理について調べます。対象はOpenSBI 1.5 PLATFORM=generic、実行環境はQEMU RV64 virt machine(qemu-system-riscv64 -machine virt)です。

  • デバイスツリーの先頭アドレスの決め方
  • デバイスツリーを配置する方法

の順で説明したいと思います。

デバイスツリーの先頭アドレス

OpenSBIがLinuxを起動するときは、どんなHWなのか伝えるためデバイスツリーというデータ(正確に言うとFlattened Devicetree Blob、*.dtbというファイル名)のアドレスを渡します。デバイスツリーのアドレスを渡している箇所のコードはこんな感じです。

ペイロード(今回はLinux)にデバイスツリーのアドレスを渡す処理のコード

//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)
{
	//...略...

	//★各引数に入っている値は下記の通り★
	//arg0 = hartid
	//arg1 = scratch->next_arg1      -> デバイスツリーのアドレス
	//next_addr = scratch->next_addr -> mretのリターンアドレス
	//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();
}

起動ログからもLinuxに渡されている情報が確認できます。

OpenSBI 1.5の起動ログ(一部抜粋)
(...略...)

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    ★scratch->next_addr: mretのリターンアドレス★
Domain0 Next Arg1         : 0x0000000082200000    ★scratch->next_arg1: デバイスツリーのアドレス★
Domain0 Next Mode         : S-mode    ★scratch->next_mode: Supervisorモードで開始★
Domain0 SysReset          : yes
Domain0 SysSuspend        : yes

次はscratch->next_arg1にデバイスツリーのアドレスを設定する仕組みです。このメンバにはfw_next_arg1()関数が返す値が格納されます。以前(2024年7月19日の日記参照)説明したペイロードの先頭アドレスを取得するfw_next_addr()と仕組みも名前も似ていますね。処理の概要は下記のとおりです。

ペイロード先頭アドレスを取得する処理の概要
  _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に格納

プラットフォームgenericのMakefileを確認するとFW_PAYLOAD_FDT_OFFSETの定義があるので、2番目の実装が選択されます。

FW_PAYLOAD_FDT_OFFSETを定義している箇所

# opensbi/platform/generic/objects.mk

FW_JUMP_FDT_OFFSET=0x2200000
FW_PAYLOAD=y
ifeq ($(PLATFORM_RISCV_XLEN), 32)
  # This needs to be 4MB aligned for 32-bit system
  FW_PAYLOAD_OFFSET=0x400000
else
  # This needs to be 2MB aligned for 64-bit system
  FW_PAYLOAD_OFFSET=0x200000
endif
FW_PAYLOAD_FDT_OFFSET=$(FW_JUMP_FDT_OFFSET)    ★これ★


# opensbi/firmware/objects.mk

ifdef FW_PAYLOAD_FDT_OFFSET
firmware-genflags-$(FW_PAYLOAD) += -DFW_PAYLOAD_FDT_OFFSET=$(FW_PAYLOAD_FDT_OFFSET)
endif

FW_PAYLOAD_FDT_OFFSETの定義は以上のとおりです。デバイスツリーの先頭アドレスを求めるコードはこんな感じです。

デバイスツリーの先頭アドレスを求めるコード

//opensbi/firmware/fw_payload.S

fw_next_arg1:
#ifdef FW_PAYLOAD_FDT_ADDR
	li	a0, FW_PAYLOAD_FDT_ADDR
#elif defined(FW_PAYLOAD_FDT_OFFSET)
	lla	a0, _fw_start
	li	a1, FW_PAYLOAD_FDT_OFFSET
	add	a0, a0, a1    //★_fw_start + FW_PAYLOAD_FDT_OFFSETの値を返すだけ★
#else
	add	a0, a1, zero
#endif
	ret

ちなみに_fw_startは0x80000000、FW_PAYLOAD_FDT_OFFSETは0x2200000ですから、デバイスツリーの先頭アドレスは0x82200000となります。


OpenSBIのgeneric向け、payload付きバイナリのメモリマップ

OpenSBI全体のメモリマップを示すとこんな感じです。

デバイスツリーを配置する方法

結論からいうとOpenSBIがQEMUが配置したデバイスツリーを0x82200000にコピーします。コピーする理由が気になりますが、ざっと見た限り見当たりませんでした。PMPで保護をかけたいのかなあ?

QEMUのデバイスツリーについては前回(2024年7月23日の日記参照)説明したとおりです。QEMUは自動的にデバイスツリーを作成&配置する機能を持っていてアドレス0x87e00000に配置します(-m 128MBの場合、メモリ量に依存してアドレスが変わる)。メモリマップはこんな感じですね。


デバイスツリーに関連するQEMUのメモリマップ

プラットフォームgeneric向けにおいて、デバイスツリーの実体は2つあります。デバイスツリーを配置する主体と方法を時系列順に並べると下記のようになります。

  • QEMU: QEMU起動時に0x87e00000にデバイスツリーを配置、ブートROMコードにてレジスタa1にアドレスセット(コピーしたあとは使用しない)
  • OpenSBI: 0x82200000にデバイスツリーをコピー、scratch->next_arg1にアドレスセット(Linuxに渡すのはこちらのデバイスツリー)

コピーを行うコードはこんな感じです。

デバイスツリーのコピーを行うコード

//opensbi/firmware/fw_base.S

	/*
	 * Relocate Flatened Device Tree (FDT)
	 * source FDT address = previous arg1
	 * destination FDT address = next arg1
	 *
	 * Note: We will preserve a0 and a1 passed by
	 * previous booting stage.
	 */
	beqz	a1, _fdt_reloc_done
	/* Mask values in a4 */
	li	a4, 0xff
	/* t1 = destination FDT start address */
	MOV_3R	s0, a0, s1, a1, s2, a2
	call	fw_next_arg1    //★コピー先アドレスを得る(a0に格納される)★
	add	t1, a0, zero    //★t1: コピー先アドレス★
	MOV_3R	a0, s0, a1, s1, a2, s2
	beqz	t1, _fdt_reloc_done        //★コピー先アドレスが0ならコピー処理をスキップ★
	beq	t1, a1, _fdt_reloc_done    //★アドレスがコピー元 = コピー先ならコピー処理をスキップ★
	/* t0 = source FDT start address */
	add	t0, a1, zero    //★t0: コピー元アドレス★
	/* t2 = source FDT size in big-endian */
#if __riscv_xlen > 32
	lwu	t2, 4(t0)
#else
	lw	t2, 4(t0)
#endif
	/* t3 = bit[15:8] of FDT size */
	add	t3, t2, zero
	srli	t3, t3, 16
	and	t3, t3, a4
	slli	t3, t3, 8
	/* t4 = bit[23:16] of FDT size */
	add	t4, t2, zero
	srli	t4, t4, 8
	and	t4, t4, a4
	slli	t4, t4, 16
	/* t5 = bit[31:24] of FDT size */
	add	t5, t2, zero
	and	t5, t5, a4
	slli	t5, t5, 24
	/* t2 = bit[7:0] of FDT size */
	srli	t2, t2, 24
	and	t2, t2, a4
	/* t2 = FDT size in little-endian */
	or	t2, t2, t3
	or	t2, t2, t4
	or	t2, t2, t5
	/* t2 = destination FDT end address */
	add	t2, t1, t2
	/* FDT copy loop */
	ble	t2, t1, _fdt_reloc_done
_fdt_reloc_again:
	REG_L	t3, 0(t0)    //★デバイスツリーコピーのメインループ★
	REG_S	t3, 0(t1)
	add	t0, t0, __SIZEOF_POINTER__
	add	t1, t1, __SIZEOF_POINTER__
	blt	t1, t2, _fdt_reloc_again
_fdt_reloc_done:

ラベル_fdt_reloc_againの手前にある処理で、レジスタt0がコピー元アドレス(0x87e00000)、レジスタt1がコピー先アドレス(0x82200000)に設定され、_fdt_reloc_againのループにてコピーします。

デバイスツリーのアドレスを使う箇所

OpenSBIのプラットフォームgenericがデバイスツリーのアドレス(レジスタa0)を参照する方法は少なくとも2つあって、レジスタa1の参照と、scratch->next_arg1を参照する方法です。時系列順に並べると、

  • PLATFORM=genericのfw_platform_init()関数: レジスタa1を直接参照する
  • デバイスツリーのコピー処理: レジスタa1を直接参照する
  • fw_next_arg1()関数: プラットフォーム依存、genericの場合は固定値(後述)
  • C言語実装部分: scratch->next_arg1(fw_next_arg1()関数が返す結果のコピー)

プラットフォームによっても処理が違います。読んでいてややこしい部分の1つですね……。

デバイスツリーがどこからきてどこへ行くのか?がわかりました。次はデバイスツリーのアドレスを決めるもう1つの実装について調べます。

編集者:すずき(2024/07/26 02:02)

コメント一覧

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



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

管理用メニュー

link 記事を新規作成

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

最近のコメント5件

  • link 14年6月13日
    2048player...さん (09/26 01:04)
    「最後に、この式を出すのに紙4枚(A4)も...」
  • link 14年6月13日
    2048playerさん (09/26 01:00)
    「今のところ最も簡略化した式です。\n--...」
  • link 14年6月13日
    2048playerさん (09/16 01:00)
    「返信ありがとうございます。\nコメントが...」
  • link 14年6月13日
    すずきさん (09/12 21:19)
    「コメントありがとうございます。同じ結果に...」
  • link 14年6月13日
    2048playerさん (09/08 17:30)
    「私も2048の最高スコアを求めたのですが...」

最近の記事3件

  • link 24年9月14日
    すずき (09/22 11:23)
    「[OpenSBIを調べる - scratch領域の詳細] 目次: Linux今回はOpenSBIのコード内に頻出するscrat...」
  • link 21年8月11日
    すずき (09/22 00:15)
    「[Kindle - まとめリンク] 目次: Kindle初代Kindle Fire HDの話。Kindle Fire HDのカ...」
  • link 24年8月11日
    すずき (09/22 00:14)
    「[Amazonマイリストへの問い合わせの返事がきた] 目次: Kindle先日(2024年8月4日の日記参照)Amazonへ問...」
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

最終更新: 09/26 01:04