LoginSignup
0
1

More than 1 year has passed since last update.

QEMUにマシンを追加する(BL1動作)

Last updated at Posted at 2023-05-10

QEMUにマシンを追加する(枠の追加)に引き続き
マシンの中身を実装していきます。

今回はBL1を動作させます。

BL1とは

BL1とはブートローダの一種になります。
RZ/A3ULではBL1⇒BL2⇒(BL31⇒BL33⇒)アプリケーションプログラム
という風に実際にユーザが起動させたいプログラムが起動するまでに、
複数のブートローダが起動します。
今回は評価ボード用に無料公開されている
サンプルプログラムを動作させることが目的のため
BL1⇒BL2⇒サンプルプログラムという順に動作させます。

作成するマシンは事前準備編で記載した通りRZ/A3ULとします。

起動パラメータ

実行コマンドは以下の通りとします。

./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin -semihosting

cpu はa55なのですが、qemu側で実装が無いためa57で一旦代用します。

bl1.binは事前準備編で作成した
ipl配下のqemu用bl1.binを使用します。

また、qemu用のbl1はセミホスティング機能を使ってbl2をロードしています。
実機用のBL1を作る方法は一般公開されていないようなので
そのまま流用させてもらいます。

以下コマンド等でbuildディレクトリ内に該当ファイルへのシンボリックリンクを作成してください。

ln -s ../../ipl/build/qemu/debug/bl2.bin

qemu-system-aarch64の実行ディレクトリ内にbl2.binがあると
bl1はbl2をセミホスティング機能を使ってロードしてくれます。
詳細はこの辺に記載してあります。

ドキュメントでは-semihosting-config enable,target=nativeを使用するようになっていますが
単なる-semihostingだけでも動くようです。
違いについてはよくわかりません。

BL1の修正

用意されているBL1はvirtマシン用になるため、
今回作成するRZ/A3UL用だと多少の修正が必要になります。

IPLをダウンロードしたディレクトリ内で以下を修正します。

IPLのqemu用bl1に加える変更
diff --git a/bl1/bl1_main.c b/bl1/bl1_main.c
index fd60232..2939617 100644
--- a/bl1/bl1_main.c
+++ b/bl1/bl1_main.c
@@ -99,9 +99,9 @@ void bl1_main(void)
 #else
 	val = read_sctlr();
 #endif
-	assert((val & SCTLR_M_BIT) != 0);
-	assert((val & SCTLR_C_BIT) != 0);
-	assert((val & SCTLR_I_BIT) != 0);
+//	assert((val & SCTLR_M_BIT) != 0);
+//	assert((val & SCTLR_C_BIT) != 0);
+//	assert((val & SCTLR_I_BIT) != 0);
 	/*
 	 * Check that Cache Writeback Granule (CWG) in CTR_EL0 matches the
 	 * provided platform value
diff --git a/drivers/arm/pl011/aarch64/pl011_console.S b/drivers/arm/pl011/aarch64/pl011_console.S
index 861d2ed..95e1824 100644
--- a/drivers/arm/pl011/aarch64/pl011_console.S
+++ b/drivers/arm/pl011/aarch64/pl011_console.S
@@ -93,20 +93,20 @@ endfunc console_pl011_core_init
 	 * -----------------------------------------------
 	 */
 func console_pl011_register
-	mov	x7, x30
-	mov	x6, x3
-	cbz	x6, register_fail
-	str	x0, [x6, #CONSOLE_T_BASE]
-
-	bl	console_pl011_core_init
-	cbz	x0, register_fail
-
-	mov	x0, x6
-	mov	x30, x7
-	finish_console_register pl011 putc=1, getc=1, flush=1
-
-register_fail:
-	ret	x7
+#	mov	x7, x30
+#	mov	x6, x3
+#	cbz	x6, register_fail
+#	str	x0, [x6, #CONSOLE_T_BASE]
+#
+#	bl	console_pl011_core_init
+#	cbz	x0, register_fail
+#
+#	mov	x0, x6
+#	mov	x30, x7
+#	finish_console_register pl011 putc=1, getc=1, flush=1
+#
+#register_fail:
+#	ret	x7
 endfunc console_pl011_register

 	/* --------------------------------------------------------
@@ -153,12 +153,12 @@ endfunc console_pl011_core_putc
 	 * --------------------------------------------------------
 	 */
 func console_pl011_putc
-#if ENABLE_ASSERTIONS
-	cmp	x1, #0
-	ASM_ASSERT(ne)
-#endif /* ENABLE_ASSERTIONS */
-	ldr	x1, [x1, #CONSOLE_T_BASE]
-	b	console_pl011_core_putc
+##if ENABLE_ASSERTIONS
+#	cmp	x1, #0
+#	ASM_ASSERT(ne)
+##endif /* ENABLE_ASSERTIONS */
+#	ldr	x1, [x1, #CONSOLE_T_BASE]
+#	b	console_pl011_core_putc
 endfunc console_pl011_putc

 	/* ---------------------------------------------
diff --git a/lib/el3_runtime/aarch64/context.S b/lib/el3_runtime/aarch64/context.S
index 7daf30d..0f5d93d 100644
--- a/lib/el3_runtime/aarch64/context.S
+++ b/lib/el3_runtime/aarch64/context.S
@@ -872,6 +872,24 @@ endfunc save_and_update_ptw_el1_sys_regs
  * ------------------------------------------------------------------
  */
 func el3_exit
+#ifdef IMAGE_BL1
+	bl disable_mmu_icache_el3
+	tlbi alle3
+	mov x0, #0x2000
+	movk x0, #1, LSL #16
+	mov x1, #0x3cd
+	msr elr_el3, x0
+	msr spsr_el3, x1
+	mov x0, #0xf000
+	movk x0, #2, LSL #16
+	mov x1, #0
+	mov x2, #0
+	mov x3, #0
+	mov x4, #0
+	mov x5, #0
+	mov x6, #0
+	mov x7, #0
+#else
 #if ENABLE_ASSERTIONS
 	/* el3_exit assumes SP_EL0 on entry */
 	mrs	x17, spsel
@@ -934,6 +952,7 @@ func el3_exit
 #endif
 #ifdef IMAGE_BL31
 	str	xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_IS_IN_EL3]
+#endif
 #endif
 	exception_return

diff --git a/plat/qemu/common/qemu_bl1_setup.c b/plat/qemu/common/qemu_bl1_setup.c
index 67f3327..63a0d0c 100644
--- a/plat/qemu/common/qemu_bl1_setup.c
+++ b/plat/qemu/common/qemu_bl1_setup.c
@@ -49,11 +49,11 @@ void bl1_early_platform_setup(void)

 void bl1_plat_arch_setup(void)
 {
-	QEMU_CONFIGURE_BL1_MMU(bl1_tzram_layout.total_base,
-				bl1_tzram_layout.total_size,
-				BL_CODE_BASE, BL1_CODE_END,
-				BL1_RO_DATA_BASE, BL1_RO_DATA_END,
-				BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
+//	QEMU_CONFIGURE_BL1_MMU(bl1_tzram_layout.total_base,
+//				bl1_tzram_layout.total_size,
+//				BL_CODE_BASE, BL1_CODE_END,
+//				BL1_RO_DATA_BASE, BL1_RO_DATA_END,
+//				BL_COHERENT_RAM_BASE, BL_COHERENT_RAM_END);
 }

 void bl1_platform_setup(void)
diff --git a/plat/qemu/common/qemu_console.c b/plat/qemu/common/qemu_console.c
index 1f00f8a..85cfebf 100644
--- a/plat/qemu/common/qemu_console.c
+++ b/plat/qemu/common/qemu_console.c
@@ -9,15 +9,15 @@
 #include <drivers/console.h>
 #include <drivers/arm/pl011.h>

-static console_t console;
+//static console_t console;

 void qemu_console_init(void)
 {
-	(void)console_pl011_register(PLAT_QEMU_BOOT_UART_BASE,
-			       PLAT_QEMU_BOOT_UART_CLK_IN_HZ,
-			       PLAT_QEMU_CONSOLE_BAUDRATE, &console);
-
-	console_set_scope(&console, CONSOLE_FLAG_BOOT |
-			  CONSOLE_FLAG_RUNTIME);
+//	(void)console_pl011_register(PLAT_QEMU_BOOT_UART_BASE,
+//			       PLAT_QEMU_BOOT_UART_CLK_IN_HZ,
+//			       PLAT_QEMU_CONSOLE_BAUDRATE, &console);
+//
+//	console_set_scope(&console, CONSOLE_FLAG_BOOT |
+//			  CONSOLE_FLAG_RUNTIME);
 }

diff --git a/plat/qemu/qemu/include/platform_def.h b/plat/qemu/qemu/include/platform_def.h
index fbcaa63..76deee1 100644
--- a/plat/qemu/qemu/include/platform_def.h
+++ b/plat/qemu/qemu/include/platform_def.h
@@ -83,7 +83,8 @@
 #define NS_DRAM0_BASE			0x40000000
 #define NS_DRAM0_SIZE			0x3de00000

-#define SEC_SRAM_BASE			0x0e000000
+//#define SEC_SRAM_BASE			0x0e000000
+#define SEC_SRAM_BASE			0x00000000
 #define SEC_SRAM_SIZE			0x00060000

 #define SEC_DRAM_BASE			0x0e100000
@@ -128,8 +129,10 @@
  */
 #define BL1_RO_BASE			SEC_ROM_BASE
 #define BL1_RO_LIMIT			(SEC_ROM_BASE + SEC_ROM_SIZE)
-#define BL1_RW_BASE			(BL1_RW_LIMIT - 0x12000)
-#define BL1_RW_LIMIT			(BL_RAM_BASE + BL_RAM_SIZE)
+//#define BL1_RW_BASE			(BL1_RW_LIMIT - 0x12000)
+//#define BL1_RW_LIMIT			(BL_RAM_BASE + BL_RAM_SIZE)
+#define BL1_RW_BASE			0x8000
+#define BL1_RW_LIMIT	    0x30000

 /*
  * BL2 specific defines.
@@ -137,7 +140,8 @@
  * Put BL2 just below BL3-1. BL2_BASE is calculated using the current BL2 debug
  * size plus a little space for growth.
  */
-#define BL2_BASE			(BL31_BASE - 0x25000)
+//#define BL2_BASE			(BL31_BASE - 0x25000)
+#define BL2_BASE			0x12000
 #define BL2_LIMIT			BL31_BASE

 /*
@@ -148,7 +152,8 @@
  */
 #define BL31_BASE			(BL31_LIMIT - 0x20000)
 #define BL31_LIMIT			(BL_RAM_BASE + BL_RAM_SIZE)
-#define BL31_PROGBITS_LIMIT		BL1_RW_BASE
+//#define BL31_PROGBITS_LIMIT		BL1_RW_BASE
+#define BL31_PROGBITS_LIMIT		0x40000000


 /*
@@ -191,7 +196,8 @@
 /*
  * PL011 related constants
  */
-#define UART0_BASE			0x09000000
+//#define UART0_BASE			0x09000000
+#define UART0_BASE			0x1004B800
 #define UART1_BASE			0x09040000
 #define UART0_CLK_IN_HZ			1
 #define UART1_CLK_IN_HZ			1
diff --git a/plat/renesas/rz/common/rz_common.mk b/plat/renesas/rz/common/rz_common.mk
index 50b7678..8146f5d 100644
--- a/plat/renesas/rz/common/rz_common.mk
+++ b/plat/renesas/rz/common/rz_common.mk
@@ -54,7 +54,9 @@ ifneq (${ARCH_TYPE}, 0)
 MMU_SOURCE		:= plat/renesas/rz/common/drivers/rza_mmu/ARMv8A/rza_mmu.c
 endif

-BL2_SOURCES		+=	lib/cpus/aarch64/cortex_a55.S						\
+#BL2_SOURCES		+=	lib/cpus/aarch64/cortex_a55.S						\
+
+BL2_SOURCES		+=	lib/cpus/aarch64/cortex_a57.S						\
 					${RZ_TIMER_SOURCES}									\
 					${DYN_CFG_SOURCES}									\
 					common/desc_image_load.c							\
修正の意味

上から順に説明していきます。

  • sctlrのassert回避
    実機の状態と一致していないため、除外します。
    共通部分ですがa3ulのbl1はこのコードで作成することは無いため削除で構いません。

  • console_pl011_register
    virt用のコンソール出力コマンドなので削除します。

  • console_pl011_putc
    上記に同じです。

  • el3_exit
    BL2はel3で動作するのですが、このコードではel1で動作してしまいます。
    従って、BL2を抜けるときのコードを持ってきました。
    つまり、FSP(サンプルのアプリケーションプログラム)もel3で動作します。
    elについての説明はこちらなどを参考にしてください。

  • bl1_plat_arch_setup
    メモリマップはハードウェア側で実装するのでここで設定の必要はありません。

  • qemu_console_init
    virt用のコンソール処理は削除していきます。

  • SEC_SRAM_BASE
    実機に併せます。
    ハードウェアユーザマニュアル5.2 Area Mapsの通りです。

  • BL1_RW_BASE
    BL1のサイズが128kb未満のためです。

  • BL1_RW_LIMIT
    実機の通りです。

  • BL2_BASE
    実機の通りです。BL2をビルドすると分かるのですが、
    BL2のエントリポイントは12000番地になります。
    展開先はSPIの0x20000000番地に展開された後、11E00番地へ転送されるのですが、
    0x200はプログラムヘッダとなっており、12000番地から開始します。

  • BL31_PROGBITS_LIMIT
    こちらはビルドエラー回避です。
    上手い方法が見つからなかったのでとりあえず大きな値を入れています。

  • UART0_BASE
    実機の通りです。マニュアルだと5.2 Area MapsTable 5.1 Detailed Address Space (3/3)22.2 Register Descriptionsなどに記載があります。

  • BL2_SOURCES
    これはQEMUがcortex-a55に対応していないための暫定処置になります。
    正直大して変わらないので問題ないと思います。

修正後は再度ビルドを行ってください。

CROSS_COMPILE=aarch64-none-elf- make PLAT=qemu DEBUG=1

SRAMの実装

RZ/A3ULは
ハードウェアユーザマニュアル5.2 Area Maps
にある通りのメモリマップとなっているため
そのように実装していきます。

qemuディレクトリ内でrza3ul.cを修正します。

ヘッダも一応作っておきます。

rza3ul.h

ここでマシン管理用の構造体を作ってobjectタイプを宣言しておくと
後々楽になるので作っておきます。

現時点では中身は空です。

ファイルの作成場所は既存のコードに倣ってinclude/hw/armにします。

#ifndef __RZA3UL_H__
#define __RZA3UL_H__

#include "target/arm/cpu.h"

#define TYPE_RZA3UL "rza3ul"
#define RZ_A3UL_NUM_CPUS (1)

OBJECT_DECLARE_SIMPLE_TYPE(RzA3ulState, RZA3UL)

struct RzA3ulState {
};
#endif

rza3ul.c

前回作成した
rza3ul_init()の中にハードを実装していきます。
と言っても今回はcpuとメモリのみです。

#include "qemu/osdep.h"
#include "qemu/datadir.h"
#include "hw/loader.h"
#include "qemu/units.h"
#include "qemu/error-report.h"
#include "exec/address-spaces.h"
#include "qapi/error.h"
#include "hw/boards.h"
#include "hw/arm/rza3ul.h"

static void rza3ul_init(MachineState *machine)
{
    char *sysboot_filename;
    MemoryRegion *sysmem;
    MemoryRegion *sram;

    Object *cpuobj = object_new(machine->cpu_type);

    if (object_property_find(cpuobj, "has_el3"))
        object_property_set_bool(cpuobj, "has_el3", true, &error_fatal);
    if (object_property_find(cpuobj, "has_el2"))
        object_property_set_bool(cpuobj, "has_el2", true, &error_fatal);

    qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);

    sysmem = get_system_memory();
    sram = g_new(MemoryRegion, 1);

    memory_region_init_ram(sram, NULL, "rza3ul.sram", 0x0100000, &error_fatal);
    memory_region_add_subregion(sysmem, 0, sram);

    if (machine->firmware) {
        sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware);
        if (sysboot_filename != NULL) {
            if (load_image_targphys(sysboot_filename, 0, 0x8000) < 0) {
                error_report("%s load failed", sysboot_filename);
                exit(1);
            }
            g_free(sysboot_filename);
        } else {
            error_report("%s load failed", sysboot_filename);
            exit(1);
        }
    }
}

static void rza3ul_machine_init(MachineClass *mc)
{
    mc->desc = "Renesas RZ/A3UL";
    mc->init = &rza3ul_init;
    mc->ignore_memory_transaction_failures = true;
    mc->default_ram_size = 2048 * MiB;
    mc->default_ram_id = "ram";
}

DEFINE_MACHINE("rza3ul", rza3ul_machine_init)

ビルドと実行

cd ../build
# 1回やれば何度もconfigureする必要はありません。
# ../qemu/configure --target-list=aarch64-softmmu --enable-debug
make
./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin 

おそらく VNC server running on 127.0.0.1:5900などと表示されて何も表示されていないと思います。

元々のbl1ではvirtマシン用のコンソール出力を行っていたのですが、今回削除しているからです。
というのもbl2でrza3ulマシン用のコンソール出力を行いたかったためです。

このままでは動いているのかどうかわからないので、int命令を表示するようにして実行します。

実行コマンドは以下です。

./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin -d int

Exception return from AArch64 EL3 to AArch64 EL3 PC 0x12000という文字列が表示されると思います。
これはbl2 のエントリポイントになります。
12000以降の処理に対応していないので
その後例外が発生しているのが見て取れると思いますが
これで、bl1が正常に動作していることが確認出来ました。

デバッグ

うまく動作しない場合は以下でデバッグを行います。
target/arm/tcg/translate-a64.caarch64_tr_translate_insn
insn取得後あたりにg_printなどを仕込むとステップ毎の進捗がわかってデバッグしやすいと思います。

$ gdb qemu-system-aarch64
(gdb) r -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin

変更点の説明

cpuの作成

見たままだと思います。

Object *cpuobj = object_new(machine->cpu_type);

elの設定

bl1やbl2、また公開されているサンプルプログラムも全てEL3で動作するようになっています。
また、-biosのオプションを指定すると該当ファイルがEL3で動作するようになります。
cpu側でEL3に対応するように設定してあげます。
EL2は今回は不要なのですがついでに設定しています。

    if (object_property_find(cpuobj, "has_el3"))
        object_property_set_bool(cpuobj, "has_el3", true, &error_fatal); 
    if (object_property_find(cpuobj, "has_el2"))
        object_property_set_bool(cpuobj, "has_el2", true, &error_fatal); 

realize

デバイスの作成です。
これをすることで実際にqemuにデバイスとして認識されます。

    qdev_realize(DEVICE(cpuobj), NULL, &error_fatal);

メモリ空間にSRAMを設定

get_system_memoryで全体の仮想メモリ空間を取得します。
g_newで作成した後、0x100000バイトサイズのsram領域を
memory_region_init_ramで作成します。
本当はマニュアルにある通り、0x30000のサイズなのですが
qemuのセミホスティング機能で余分に確保する必要があり
仕方なくサイズを増やしています。

memory_region_add_subregionでsramを0番地にマッピングしています。
つまり0~0x100000がsramエリアとして作成されます。

    sysmem = get_system_memory();
    sram = g_new(MemoryRegion, 1);

    memory_region_init_ram(sram, NULL, "rza3ul.sram", 0x0100000, &error_fatal);
    memory_region_add_subregion(sysmem, 0, sram);

bl1のロード

qemu特有の機能です。
qemuのAPIをそのまま用いてbl1をロードします。
load_image_targphysの第2引数が実際にロードするアドレスである0番地
第3引数は読み出すサイズです。
ここは厳密である必要は無く、それっぽい感じで大丈夫です。

    if (machine->firmware) {
        sysboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware);
        if (sysboot_filename != NULL) {
            if (load_image_targphys(sysboot_filename, 0, 0x8000) < 0) {
                error_report("%s load failed", sysboot_filename);
                exit(1);
            }
            g_free(sysboot_filename);
        } else {
            error_report("%s load failed", sysboot_filename);
            exit(1);
        }
    }

QEMUにマシンを追加する(BL2動作 FSPロード失敗まで)に続きます。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1