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 Maps
のTable 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.c
のaarch64_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);
}
}