Instantiate PBR in the Phytium Pi machine, map its status and boot memories, provide the heterogeneous MPIDR topology, and connect all CPU objects for reset-time handoff.
Use SD0 as the board firmware backend. Explicitly reject the unsupported -bios and pflash interfaces. Signed-off-by: Bin Meng <[email protected]> --- hw/arm/phytium_e2000.c | 97 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 5 deletions(-) diff --git a/hw/arm/phytium_e2000.c b/hw/arm/phytium_e2000.c index c6d789bb04..e74a0328fe 100644 --- a/hw/arm/phytium_e2000.c +++ b/hw/arm/phytium_e2000.c @@ -13,6 +13,7 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/cpu-common.h" #include "system/address-spaces.h" #include "system/kvm.h" #include "system/system.h" @@ -20,6 +21,7 @@ #include "hw/arm/boot.h" #include "hw/arm/bsa.h" #include "hw/arm/machines-qom.h" +#include "hw/block/flash.h" #include "hw/char/pl011.h" #include "hw/core/boards.h" #include "hw/core/qdev-properties.h" @@ -27,6 +29,7 @@ #include "hw/intc/arm_gicv3_its_common.h" #include "hw/misc/phytium_e2000_ddr.h" #include "hw/misc/phytium_e2000_mhu.h" +#include "hw/misc/phytium_e2000_pbr.h" #include "hw/misc/unimp.h" #include "hw/net/cadence_gem.h" #include "hw/pci/pci.h" @@ -96,8 +99,10 @@ struct PhytiumE2000State { struct arm_boot_info bootinfo; DeviceState *gic; DeviceState *qspi; + PhytiumE2000PBRState *pbr; PhytiumE2000MciState *mci[PHYTIUM_E2000_NUM_MCIS]; CadenceGEMState *gem[PHYTIUM_E2000_NUM_GEMS]; + CPUState *cpu[PHYTIUM_E2000_NUM_CPUS]; MemoryRegion scp_sram; MemoryRegion ram_low; MemoryRegion ram_high; @@ -208,6 +213,17 @@ phytium_e2000_cpu_config[PHYTIUM_E2000_NUM_CPUS] = { }, }; +static uint64_t phytium_e2000_cpu_mp_affinity(unsigned int cpu) +{ + /* + * E2000Q exposes one core in each of the first two clusters and two cores + * in the third cluster. Firmware stores these MPIDRs in its parameter + * tables, so a linear CPU index is not a valid affinity value. + */ + g_assert(cpu < ARRAY_SIZE(phytium_e2000_cpu_config)); + return phytium_e2000_cpu_config[cpu].mp_affinity; +} + static void phytium_e2000_create_its(PhytiumE2000State *s) { DeviceState *dev = qdev_new(its_class_name()); @@ -410,6 +426,15 @@ static void phytium_e2000_create_pcie(PhytiumE2000State *s) } } +static void phytium_e2000_reject_legacy_firmware(MachineState *ms) +{ + if (ms->firmware || drive_get(IF_PFLASH, 0, 0)) { + error_report("phytium-pi: -bios and pflash firmware are not " + "supported; use an if=sd,index=0 image"); + exit(1); + } +} + static BlockBackend *phytium_e2000_sd_blk(int index) { DriveInfo *dinfo = drive_get(IF_SD, 0, index); @@ -471,6 +496,47 @@ static void phytium_e2000_create_qspi(PhytiumE2000State *s) phytium_e2000_memmap[PHYTIUM_E2000_QSPI_DIRECT].base, 2); } +static bool phytium_e2000_create_pbr(PhytiumE2000State *s) +{ + MachineState *ms = MACHINE(s); + DeviceState *dev = qdev_new(TYPE_PHYTIUM_E2000_PBR); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + BlockBackend *boot_blk = NULL; + uint64_t cpu_mpidrs[PHYTIUM_E2000_NUM_CPUS]; + int i; + + if (!ms->kernel_filename) { + boot_blk = phytium_e2000_sd_blk(0); + } + + for (i = 0; i < ms->smp.cpus; i++) { + cpu_mpidrs[i] = phytium_e2000_cpu_mp_affinity(i); + } + + qdev_prop_set_string(dev, "boot-mode", + PHYTIUM_E2000_PBR_BOOT_MODE_SD0); + phytium_e2000_pbr_configure(PHYTIUM_E2000_PBR(dev), boot_blk, + phytium_e2000_memmap[ + PHYTIUM_E2000_RAM].base, + ms->ram_size, cpu_mpidrs, ms->smp.cpus); + + /* + * PBR owns the status snapshot and both boot memories. The status block + * overlaps the broad board-control placeholder and therefore needs the + * higher mapping priority used by the previous status-only device. + */ + object_property_add_child(OBJECT(s), "pbr", OBJECT(dev)); + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map_overlap(sbd, 0, PHYTIUM_E2000_PBR_STATUS_BASE, 2); + sysbus_mmio_map(sbd, 1, + phytium_e2000_memmap[PHYTIUM_E2000_BOOT_SRAM].base); + sysbus_mmio_map(sbd, 2, + phytium_e2000_memmap[PHYTIUM_E2000_BOOT_IACC].base); + s->pbr = PHYTIUM_E2000_PBR(dev); + + return phytium_e2000_pbr_firmware_loaded(s->pbr); +} + static void phytium_e2000_create_ddr_status(PhytiumE2000State *s) { DeviceState *dev = qdev_new(TYPE_PHYTIUM_E2000_DDR); @@ -570,7 +636,8 @@ static void phytium_e2000_create_ram(PhytiumE2000State *s) phytium_e2000_memmap[PHYTIUM_E2000_RAM_HIGH].base, &s->ram_high); } -static void phytium_e2000_create_cpus(PhytiumE2000State *s) +static void phytium_e2000_create_cpus(PhytiumE2000State *s, + bool firmware_loaded) { MachineState *ms = MACHINE(s); const CPUArchIdList *possible_cpus; @@ -587,18 +654,30 @@ static void phytium_e2000_create_cpus(PhytiumE2000State *s) &error_abort); object_property_set_int(cpuobj, "cntfrq", PHYTIUM_E2000_GTIMER_HZ, &error_abort); - if (object_property_find(cpuobj, "has_el3")) { + if (!firmware_loaded && object_property_find(cpuobj, "has_el3")) { /* * The generic-loader U-Boot path starts after the EL3 firmware * stages that normally provide the Phytium SMC services. */ object_property_set_bool(cpuobj, "has_el3", false, &error_abort); } + /* + * PBR releases only the primary MPIDR named in the firmware parameter + * header. Secondary CPUs remain powered off for later firmware or + * PSCI bring-up. + */ + if (firmware_loaded && + i != phytium_e2000_pbr_primary_cpu(s->pbr)) { + object_property_set_bool(cpuobj, "start-powered-off", true, + &error_abort); + } object_property_set_link(cpuobj, "memory", OBJECT(get_system_memory()), &error_abort); cs = CPU(cpuobj); cs->cpu_index = i; qdev_realize(DEVICE(cpuobj), NULL, &error_fatal); + s->cpu[i] = cs; + phytium_e2000_pbr_connect_cpu(s->pbr, i, cs); object_unref(cpuobj); } } @@ -606,6 +685,7 @@ static void phytium_e2000_create_cpus(PhytiumE2000State *s) static void phytium_pi_init(MachineState *ms) { PhytiumE2000State *s = PHYTIUM_PI(ms); + bool firmware_loaded; int i; if (kvm_enabled()) { @@ -626,12 +706,15 @@ static void phytium_pi_init(MachineState *ms) exit(1); } + phytium_e2000_reject_legacy_firmware(ms); + phytium_e2000_create_ram(s); phytium_e2000_create_unimplemented(); phytium_e2000_create_qspi(s); + firmware_loaded = phytium_e2000_create_pbr(s); - phytium_e2000_create_cpus(s); + phytium_e2000_create_cpus(s, firmware_loaded); phytium_e2000_create_gic(s); phytium_e2000_create_scp_sram(s); @@ -654,7 +737,7 @@ static void phytium_pi_init(MachineState *ms) s->bootinfo.board_id = -1; s->bootinfo.loader_start = phytium_e2000_memmap[PHYTIUM_E2000_RAM].base; s->bootinfo.psci_conduit = QEMU_PSCI_CONDUIT_SMC; - s->bootinfo.firmware_loaded = false; + s->bootinfo.firmware_loaded = firmware_loaded; arm_load_kernel(ARM_CPU(first_cpu), ms, &s->bootinfo); } @@ -701,7 +784,11 @@ static void phytium_pi_class_init(ObjectClass *oc, const void *data) mc->valid_cpu_types = valid_cpu_types; mc->max_cpus = PHYTIUM_E2000_NUM_CPUS; mc->default_cpus = PHYTIUM_E2000_NUM_CPUS; - mc->default_ram_size = 1 * GiB; + /* + * PBF/BL1 relocates to 0xf8c40000, which is outside a 1 GiB RAM window + * starting at 0x80000000. Two GiB is the minimum useful firmware default. + */ + mc->default_ram_size = 2 * GiB; mc->default_ram_id = "phytium-e2000.ram"; mc->minimum_page_bits = 12; mc->block_default_type = IF_SD; -- 2.53.0
