前回に引き続きハードウェアを実装していきます。
サンプルプログラムのコンソール割り込みは大分ややこしいので
今回は一旦その手前までで区切ります。
実行コマンド
./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../renesas/ipl/build/qemu/debug/bl1.bin -kernel ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf
動作したコード
rza3ul.c
#include "qemu/osdep.h"
#include "qemu/datadir.h"
#include "qemu/log.h"
#include "kvm_arm.h"
#include "hw/loader.h"
#include "qemu/units.h"
#include "exec/address-spaces.h"
#include "qapi/error.h"
#include "target/arm/cpregs.h"
#include "hw/boards.h"
#include "hw/qdev-properties.h"
#include "hw/arm/rza3ul.h"
#include "elf.h"
#define RZA_MMIO_BASE (0x10000000)
#define RZG2L_SCIF0_BASE (0x1004B800)
#define RZG2L_SPIMULT_BASE (0x10060000)
#define RZG2L_SYC_BASE (0x11000000)
#define CPG_BASE (0x11010000) /* CPG base address */
#define RZG2L_SYSC_BASE (0x11020000)
#define RZG2L_TZC_SPI_BASE (0x11060000)
#define RZG2L_TZC_DDR_BASE (0x11070000)
#define RZG2L_DDR_PHY_BASE (0x11400000)
#define RZG2L_DDR_MEMC_BASE (0x11410000)
// SYC
#define CNTFID0 (0x020)
// TZC
#define PID0_OFF (0xfe0)
#define PID1_OFF (0xfe4)
#define GATE_KEEPER_OFF (0x008)
#define CPG_PLL4_STBY (CPG_BASE + 0x0010) /* PLL4 (SSCG) standby control register */
#define CPG_PLL4_CLK1 (CPG_BASE + 0x0014) /* PLL4 (SSCG) output clock setting register 1 */
#define CPG_PLL4_MON (CPG_BASE + 0x001C) /* PLL4 (SSCG) monitor register */
#define CPG_PLL6_STBY (CPG_BASE + 0x0020) /* PLL6 (SSCG) standby control register */
#define CPG_PLL6_CLK1 (CPG_BASE + 0x0024) /* PLL6 (SSCG) output clock setting register 1 */
#define CPG_PLL6_MON (CPG_BASE + 0x002C) /* PLL6 (SSCG) monitor register */
#define CPG_CLKON_CA55 (CPG_BASE + 0x0500) /* Clock ON / OFF register CA55 */
#define CPG_CLKON_OCTA (CPG_BASE + 0x05F4) /* Clock ON / OFF register OCTA */
#define CPG_CLKMON_CA55 (CPG_BASE + 0x0680) /* Clock monitor register CA55 */
#define CPG_RST_CA55 (CPG_BASE + 0x0800) /* Reset ONOFF register CA55 */
#define CPG_RST_OCTA (CPG_BASE + 0x08F4) /* Reset ONOFF register OCTA */
#define CPG_RSTMON_CA55 (CPG_BASE + 0x0980) /* Reset monitor register CA55 */
#define PLL4_STBY_RESETB (1 << 0)
#define PLL4_STBY_RESETB_WEN (1 << 16)
#define PLL4_MON_PLL4_RESETB (1 << 0)
#define PLL4_MON_PLL4_LOCK (1 << 4)
#define PLL6_STBY_RESETB (1 << 0)
#define PLL6_STBY_RESETB_WEN (1 << 16)
#define PLL6_MON_PLL6_RESETB (1 << 0)
#define PLL6_MON_PLL6_LOCK (1 << 4)
// DDR
#define DENALI_CTL_00 (0x0000)
#define DENALI_CTL_56 (0x00E0)
#define DENALI_CTL_59 (0x00EC)
#define DENALI_CTL_67 (0x010C)
#define DENALI_CTL_146 (0x0248)
#define DENALI_CTL_147 (0x024C)
#define DDRPHY_R18 (0x100)
#define DDRPHY_R27 (0x124)
#define DDRPHY_R36 (0x148)
#define DDRPHY_R37 (0x14c)
#define DDRPHY_R42 (0x160)
#define DDRPHY_R46 (0x170)
#define DDRMC_R000 DENALI_CTL_00
#define DDRMC_R004 DENALI_CTL_56
#define DDRMC_R005 DENALI_CTL_59
#define DDRMC_R008 DENALI_CTL_67
#define DDRMC_R021 DENALI_CTL_146
#define DDRMC_R022 DENALI_CTL_147
// spi
#define SPIM_CMNSR 0x0048
// scif
#define SCR (0x04)
#define FTDR (0x06)
#define FSR (0x08)
#define FDR (0x0E)
#define FSR_TEND_SHIFT (6)
#define FSR_TEND (1<<FSR_TEND_SHIFT)
#define SCR_TIE 0x80
#define SCR_TEIE 0x04
#define FSR_TDFE 0x20
static void create_gic(RzA3ulState *s);
static uint64_t a3ul_mmio_read(void *opaque, hwaddr addr, unsigned size);
static void a3ul_mmio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size);
static uint64_t rza3ul_uart(void* opaque, uint64_t addr, uint64_t value, unsigned size, int type);
static void tzc_init(void);
static void spi_init(void);
static MemoryRegionOps mmio_op = {
.read = a3ul_mmio_read,
.write = a3ul_mmio_write,
.endianness = DEVICE_LITTLE_ENDIAN
};
static uint32_t cpg[0x10000] = {};
static uint32_t syc[0x10000] = {};
static uint32_t tzcd[0x1000] = {};
static uint32_t tzcs[0x1000] = {};
static uint32_t ddrp[0x10000] = {};
static uint32_t ddrm[0x10000] = {};
static uint32_t spirega[0x10000] = {};
static volatile unsigned short fsr = 0;
static void rza3ul_init(MachineState *machine)
{
RzA3ulState *s = RZA3UL(machine);
ssize_t image_size;
char *sysboot_filename;
MemoryRegion *sysmem;
MemoryRegion *sram;
MemoryRegion *ram;
MemoryRegion *mmio;
MemoryRegion *spi;
AddressSpace *as;
CPUState *cs;
uint64_t entry;
char *kfn = 0;
if (machine->kernel_filename) kfn = strdup(machine->kernel_filename);
Object *cpuobj = object_new(machine->cpu_type);
MemoryRegionOps *ops = &mmio_op;
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);
s->cpus[0] = ARM_CPU(first_cpu);
create_gic(s);
sysmem = get_system_memory();
sram = g_new(MemoryRegion, 1);
mmio = g_new(MemoryRegion, 1);
spi = g_new(MemoryRegion, 1);
ram = g_new(MemoryRegion, 1);
memory_region_init_ram(sram, NULL, "rza3ul.sram", 0x0100000, &error_fatal);
memory_region_add_subregion(sysmem, 0, sram);
memory_region_init_io(mmio, OBJECT(s), ops, s, "rza3ul.mmio", 0x10000000);
memory_region_add_subregion(sysmem, RZA_MMIO_BASE, mmio);
memory_region_init_ram(spi, NULL, "rza3ul.spi", 0x10000000, &error_fatal);
memory_region_add_subregion(sysmem, 0x20000000, spi);
memory_region_init_ram(ram, NULL, "rza3ul.ram", 0x80000000, &error_fatal);
memory_region_add_subregion(sysmem, 0x40000000, ram);
tzc_init();
spi_init();
if (machine->firmware) {
g_print("has firmware %s\n", 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("BIOS not supported for this machine");
exit(1);
}
}
if (kfn) {
cs = CPU(ARM_CPU(first_cpu));
as = cpu_get_address_space(cs, ARMASIdx_S);
image_size = load_elf_as(kfn, NULL, NULL, NULL, &entry, NULL, NULL, NULL, false, EM_AARCH64, true, false, as);
if (image_size < 0) {
g_print("kernel load failed(%ld)\n", image_size);
} else {
g_print("kernel:%s\n", kfn);
}
free(kfn);
}
}
static void create_gic(RzA3ulState *s) {
uint32_t smp_cpus = MACHINE(s)->smp.cpus;
SysBusDevice *gicbusdev;
DeviceState *gicdev;
object_initialize_child(OBJECT(s), "gic", &s->gic, gicv3_class_name());
gicdev = DEVICE(&s->gic);
qdev_prop_set_uint32(gicdev, "revision", 3);
qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
qdev_prop_set_uint32(gicdev, "num-irq", 32 * 31);
qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1);
qdev_prop_set_uint32(gicdev, "redist-region-count[0]", smp_cpus);
gicbusdev = SYS_BUS_DEVICE(&s->gic);
sysbus_realize(gicbusdev, &error_fatal);
}
static uint64_t a3ul_mmio_read(void *opaque, hwaddr addr, unsigned size) {
hwaddr paddr = addr | RZA_MMIO_BASE;
uint32_t offset = paddr & 0xFFFF;
uint32_t *tzc = tzcs;
uint32_t *ddr = ddrm;
uint64_t value = 0;
switch(paddr & 0xFFFF0000) {
case RZG2L_SYC_BASE:
if (offset == CNTFID0)
return 24000000;
return 0;
case RZG2L_SYSC_BASE:
if (addr == 0x1020a00)
return 0x203;
return 0;
case CPG_BASE:
return cpg[offset];
case RZG2L_TZC_DDR_BASE:
tzc = tzcd;
/* Falls through. */
case RZG2L_TZC_SPI_BASE:
return tzc[offset];
case RZG2L_DDR_PHY_BASE:
ddr = ddrp;
/* Falls through. */
case RZG2L_DDR_MEMC_BASE:
return ddr[offset];
case RZG2L_SPIMULT_BASE:
return spirega[offset];
}
if ((paddr & 0xFFFFFF00) == RZG2L_SCIF0_BASE) {
value = rza3ul_uart(opaque, paddr, 0, size, 0);
}
return value;
}
static void a3ul_mmio_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) {
hwaddr paddr = addr | RZA_MMIO_BASE;
uint32_t offset = paddr & 0xFFFF;
uint32_t wen;
uint32_t proc;
uint32_t *tzc = tzcs;
uint32_t bef;
uint32_t *p;
uint32_t *ddr = ddrm;
switch(paddr & 0xFFFF0000) {
case CPG_BASE:
cpg[offset] = (uint32_t)value;
proc = 1;
switch (paddr) {
case CPG_PLL4_STBY:
if (value & PLL4_STBY_RESETB_WEN) {
if (value & PLL4_STBY_RESETB)
cpg[CPG_PLL4_MON & 0xffff] |= (PLL4_MON_PLL4_RESETB | PLL4_MON_PLL4_LOCK);
else
cpg[CPG_PLL4_MON & 0xffff] &= ~(PLL4_MON_PLL4_RESETB | PLL4_MON_PLL4_LOCK);
}
break;
case CPG_PLL6_STBY:
if (value & PLL6_STBY_RESETB_WEN) {
if (value & PLL6_STBY_RESETB)
cpg[CPG_PLL6_MON & 0xffff] |= (PLL6_MON_PLL6_RESETB | PLL6_MON_PLL6_LOCK);
else
cpg[CPG_PLL6_MON & 0xffff] &= ~(PLL6_MON_PLL6_RESETB | PLL6_MON_PLL6_LOCK);
}
break;
default:
break;
}
if ((CPG_CLKON_CA55 <= paddr) && (paddr <= CPG_CLKON_OCTA)) {
wen = ((value & 0xFFFF0000) >> 16);
if (wen) {
p = &cpg[(paddr + (CPG_CLKMON_CA55 - CPG_CLKON_CA55)) & 0xffff];
bef = *p;
*p = (wen & (value & 0xffff));
}
} else if ((CPG_RST_CA55 <= paddr) && (paddr <= CPG_RST_OCTA)) {
wen = ((value & 0xFFFF0000) >> 16);
if (wen) {
p = &cpg[(paddr + (CPG_RSTMON_CA55 - CPG_RST_CA55)) & 0xffff];
bef = *p;
*p = (wen ^ (value & 0xffff));
}
}
break;
case RZG2L_SYC_BASE:
syc[offset] = (uint32_t)value;
break;
case RZG2L_TZC_DDR_BASE:
tzc = tzcd;
/* Falls through. */
case RZG2L_TZC_SPI_BASE:
if (offset == GATE_KEEPER_OFF) {
if (value)
tzc[offset] |= (value << 16);
else
tzc[offset] = 0;
} else {
tzc[offset] = value;
}
break;
case RZG2L_DDR_PHY_BASE:
ddr = ddrp;
if (offset == DDRPHY_R46) {
ddr[offset] = ((uint32_t)value) << 2;
} else {
ddr[offset] = value;
if (offset == DDRPHY_R27 && (value & (~(uint32_t)0xFBFFFFFF)) == 0) {
ddr[DDRPHY_R42] = 3;
} else if ((offset == DDRPHY_R18) && ((value & 0x10000000) == 0x10000000)) {
ddr[DDRPHY_R18] &= ~(uint32_t)0x10000000;
if (value == 0x50200000) {
ddr[DDRPHY_R36] |= 3;
ddr[DDRPHY_R37] &= ~(uint32_t)3;
} else if (value == 0x34200000) {
ddr[DDRPHY_R18] |= 3;
}
}
}
break;
case RZG2L_DDR_MEMC_BASE:
ddr[offset] = (uint32_t)value;
if ((offset == DDRMC_R000) && ((value & 1) == 1)) {
ddr[DDRMC_R021] |= 0x02000000;
} else if ((offset == DDRMC_R008) && ((value & 0x02800000) == 0x02800000)) {
ddr[DDRMC_R022] |= (uint32_t)(1 << 3);
} else if (offset == DDRMC_R004) {
bef = ddr[DDRMC_R005];
if ((value & 0x11) == 0x11) {
ddr[DDRMC_R005] = (bef & ~(uint32_t)0x7f000000) | 0x48000000;
} else if ((value & 0x2) == 0x2) {
ddr[DDRMC_R005] = (bef & ~(uint32_t)0x7f000000) | 0x40000000;
}
}
break;
case RZG2L_SPIMULT_BASE:
spirega[offset] = (uint32_t)value;
break;
}
if ((paddr & 0xFFFFFF00) == RZG2L_SCIF0_BASE) {
rza3ul_uart(opaque, paddr, value, size, 1);
}
}
static uint64_t rza3ul_uart(void* opaque, uint64_t addr, uint64_t value, unsigned size, int type) {
uint32_t offset = addr & 0xFF;
char c = (char)value;
short vs = (short)value;
uint64_t ret = 0;
// CPUState *cpu = CPU(ARM_CPU(first_cpu));
// volatile uint64_t pc = cpu->env_ptr->pc;
if (type) {
if (offset == FTDR) {
if (c != 0xd)
g_print("%c", c);
} else if (offset == FSR) {
fsr = vs;
}
} else {
if (offset == FDR) {
ret = 0;
} else if (offset == FSR) {
ret = fsr | FSR_TEND;
}
}
return ret;
}
static void tzc_init(void) {
tzcd[PID0_OFF] = 0x60;
tzcd[PID1_OFF] = 0xb4;
tzcs[PID0_OFF] = 0x60;
tzcs[PID1_OFF] = 0xb4;
}
static void spi_init(void) {
spirega[SPIM_CMNSR] = 1;
}
static void rza3ul_machine_init(MachineClass *mc) {
mc->desc = "RZ/A3UL (Cortex-A57)";
mc->init = rza3ul_init;
mc->block_default_type = IF_SD;
mc->units_per_default_bus = 1;
mc->min_cpus = RZ_A3UL_NUM_CPUS;
mc->max_cpus = RZ_A3UL_NUM_CPUS;
mc->default_cpus = RZ_A3UL_NUM_CPUS;
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57");
}
DEFINE_MACHINE("rza3ul", rza3ul_machine_init)
rza3ul.h
変更なし
#ifndef __RZA3UL_H__
#define __RZA3UL_H__
#include "hw/intc/arm_gicv3.h"
#include "target/arm/cpu.h"
#include "hw/cpu/cluster.h"
#define TYPE_RZA3UL "rza3ul"
#define RZ_A3UL_NUM_CPUS (1)
OBJECT_DECLARE_SIMPLE_TYPE(RzA3ulState, RZA3UL)
struct RzA3ulState {
DeviceState parent_obj;
CPUClusterState cluster;
ARMCPU *cpus[1];
const hwaddr *memmap;
GICv3State gic;
MemoryRegion sram;
};
#endif
ビルドと実行
make
./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin -kernel ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf
おそらく前回と表示は変わらないと思います。
しかし、以下でデバッグしてみると、uart_print_user_msg
まで来ていることがわかります。
これはGICの設定をすることで、GIC固有レジスタへのアクセスで落ちることがなくなったからです。
./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin -kernel ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf -S -gdb tcp::10000
# FSPのDebugディレクトリ内で
$ aarch64-none-elf-gdb scif_uart_rza3ul_evk_ep.elf
(gdb) target remote localhost:10000
(gdb) c
しばらく待って
ctrl + c
(gdb) bt
デバッグ
QEMU自体をデバッグしたい場合は以下
$ gdb qemu-system-aarch64
(gdb) r -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin -kernel ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf
FSPをデバッグしたい場合は以下
$ ./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/debug/bl1.bin -kernel ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf -d int -nographic -S -gdb tcp::10000 2>&1 | tee qemu.log
別端末から
$ ./aarch64-none-lf-gdb ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf
(gdb) target remote localhost:10000
(gdb) c
... # 止まってると思ったら
(gdb) Ctrl + c
(gdb) bt
変更点
create_gic
です。
他のarmの内容を真似ただけなので
内容はよくわかりません。
FSPのコードで通るようになった部分は例えば以下のような箇所です。
uint64_t R_BSP_GICC_GetMaskLevel (void)
{
uint64_t mask_level;
__asm("mrs %0, S3_0_C4_C6_0;"
"isb;"
: "=r" (mask_level)
:);
mask_level &= GIC_REG_MASK_8BIT;
return mask_level;
}
S3_0_C4_C6_0
はqemu上ではICC_PMR_EL1
という名前で登録されています。
ややこしいですが、gicの設定を行うことで、gic固有レジスタへアクセスが可能になりました。