LoginSignup
0
0

More than 1 year has passed since last update.

QEMUにマシンを追加する(FSPのコンソール割り込みまで)

Last updated at Posted at 2023-05-17

前回に引き続きハードウェアを実装していきます。
サンプルプログラムのコンソール割り込みは大分ややこしいので
今回は一旦その手前までで区切ります。

実行コマンド

./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固有レジスタへアクセスが可能になりました。


RZ/A3UL-EBKのコンソールサンプルとQEMUの割り込みに続きます。

0
0
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
0