LoginSignup
0
0

QEMUにマシンを追加する(FSP移行まで)

Last updated at Posted at 2023-05-15

前回に引き続き
ハード部分を作成していきます。

今回もbl2を動作させます。

FSPのロード

flash writerでの書き込みを実装するのはとても手間と思われるため
セミホスティング機能を用いてSPI領域にFSP(アプリケーションプログラム)を展開します。

セミホスティング機能を用いたFSPロード時のコマンド

kernelオプションを追加します。

./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 

動いたコード

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)
#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)

#define BOOT_MODE_SPI_1_8       (3)
#define CNTFID0         (0x020)

#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)

#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

#define SPIM_CMNSR      0x0048

#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 FSR_TDFE        0x20

static uint64_t a3ul_mmio_read(void *opaque, hwaddr, 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[0x10000] = {};
static uint32_t tzcs[0x10000] = {};
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);

    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", 0x100000, &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);
    }
    uint8_t *ptr = memory_region_get_ram_ptr(sram) + 0x10000;
    *ptr = BOOT_MODE_SPI_1_8;

}

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 *tzc = tzcs;
    uint32_t bef;
    uint32_t *p;
    uint32_t *ddr = ddrm;

    switch (paddr & 0xFFFF0000) {
        case CPG_BASE:
            cpg[offset] = (uint32_t)value;
            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

ビルドと実行

qemuのbuildディレクトリに移動して以下コマンドを実行してください。

./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

以下の様に表示されて止まると思います。

Jump to Application

IPLのコードを見る限り
一応FSP移行ルートに乗っているようですが、
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

以下の文字列が表示されることが見て取れると思います。

Exception return from AArch64 EL3 to AArch64 EL3 PC 0x20020800

0x20020800はSPI上にロードされたFSPのエントリポイントになります。
従って、FSPへの移行が正常に出来ていることが確認出来ました。
その後、R_SCIF_UART_Openあたりで例外が発生して落ちていると思いますが
FSPの中の話なので今回の確認はここまでです。

デバッグ

BL2の時からkernelのオプションを追加しただけです。

./qemu-system-aarch64 -M rza3ul -cpu cortex-a57 -semihosting -L ../qemu/pc-bios/ -bios ../../ipl/build/qemu/bl1.bin -kernel ../../scif/Debug/scif_uart_rza3ul_evk_ep.elf -nographic -S -gdb tcp::10000 2>&1 | tee qemu.log

BL2と同様、別ターミナルからgdbで開いてください。

$ cd /path/to/ipl
$ ./aarch64-none-lf-gdb build/a3ul/debug/bl2/bl2.elf

(gdb) target remote localhost:10000

変更点の説明

実は今回の変更は2カ所のみです。

一つはqemuのセミホスティング機能を利用して、
FSP(アプリケーションプログラム)をSPI上にロードしたこと。
もう一つはコンソールの無限ループ抑止です。

FSPのSPI ROMへのロード

追加したのは以下の部分です。
なぜか自分の環境ではmachine->kernel_filename
途中で書き換わる不具合が発生することがあったため
やむを得ず、コピーをしています。
不要であればそのままで構いません。

    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);
    }

コンソールの無限ループ抑止

この部分です。
当然、ハードウェアがこんな動きをするはずが無く、
時間があるときに作り直したいと思います。

        } else if (offset == FSR) {
            ret = fsr | FSR_TEND;

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

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