Add the base topology for the Phytium Pi board built around the E2000Q SoC, with two FTC310 and two FTC664 CPUs, RAM, GICv3/ITS, and PL011 UARTs.
Describe the CPUs as three non-uniform clusters matching the vendor Linux device tree. Preserve the board CPU slot order so firmware MPIDR 0x200 maps to QEMU CPU index 2. Signed-off-by: Bin Meng <[email protected]> --- hw/arm/Kconfig | 8 + hw/arm/meson.build | 1 + hw/arm/phytium_e2000.c | 406 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 415 insertions(+) create mode 100644 hw/arm/phytium_e2000.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 5db7ce057f..20863b5847 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -126,6 +126,14 @@ config OMAP select SD select SERIAL_MM +config PHYTIUM_E2000 + bool + default y + depends on TCG && AARCH64 + select ARM_GIC + select PL011 + select UNIMP + config REALVIEW bool default y diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 78acb04c8b..6f685b414d 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -10,6 +10,7 @@ arm_common_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c' arm_common_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) arm_common_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) arm_common_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c')) +arm_common_ss.add(when: 'CONFIG_PHYTIUM_E2000', if_true: files('phytium_e2000.c')) arm_common_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) arm_common_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c')) arm_common_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) diff --git a/hw/arm/phytium_e2000.c b/hw/arm/phytium_e2000.c new file mode 100644 index 0000000000..defc265a5d --- /dev/null +++ b/hw/arm/phytium_e2000.c @@ -0,0 +1,406 @@ +/* + * Phytium E2000 board models + * + * Copyright (c) 2026 Process Mission + * + * Author: + * Bin Meng <[email protected]> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "system/address-spaces.h" +#include "system/kvm.h" +#include "system/system.h" +#include "exec/hwaddr.h" +#include "hw/arm/boot.h" +#include "hw/arm/bsa.h" +#include "hw/arm/machines-qom.h" +#include "hw/char/pl011.h" +#include "hw/core/boards.h" +#include "hw/core/qdev-properties.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/intc/arm_gicv3_its_common.h" +#include "hw/misc/unimp.h" +#include "qobject/qlist.h" +#include "qom/object.h" +#include "target/arm/cpu.h" +#include "target/arm/cpu-qom.h" +#include "target/arm/gtimer.h" + +#define TYPE_PHYTIUM_PI MACHINE_TYPE_NAME("phytium-pi") +OBJECT_DECLARE_SIMPLE_TYPE(PhytiumE2000State, PHYTIUM_PI) + +#define PHYTIUM_E2000_NUM_CPUS 4 +#define PHYTIUM_E2000_NUM_IRQS 256 + +#define PHYTIUM_E2000_NUM_UARTS 7 + +#define PHYTIUM_E2000_GTIMER_HZ 50000000 + +enum { + PHYTIUM_E2000_LOW_PERIPH, + PHYTIUM_E2000_UART0, + PHYTIUM_E2000_UART1, + PHYTIUM_E2000_UART2, + PHYTIUM_E2000_UART3, + PHYTIUM_E2000_UART4, + PHYTIUM_E2000_UART5, + PHYTIUM_E2000_UART6, + PHYTIUM_E2000_GIC_DIST, + PHYTIUM_E2000_GIC_ITS, + PHYTIUM_E2000_GIC_REDIST, + PHYTIUM_E2000_BOOT_SRAM, + PHYTIUM_E2000_BOARD_CTRL, + PHYTIUM_E2000_BOOT_IACC, + PHYTIUM_E2000_RAM, + PHYTIUM_E2000_RAM_HIGH, +}; + +struct PhytiumE2000State { + MachineState parent; + struct arm_boot_info bootinfo; + DeviceState *gic; + MemoryRegion ram_low; + MemoryRegion ram_high; +}; + +/* + * Keep the physical addresses used by the vendor firmware even before every + * device behind them is modeled. In particular, boot SRAM carries PBR/PBF + * handoff data and IACC is the fixed execution window for system firmware. + */ +static const MemMapEntry phytium_e2000_memmap[] = { + [PHYTIUM_E2000_LOW_PERIPH] = { 0x28000000, 0x00100000 }, + [PHYTIUM_E2000_UART0] = { 0x2800c000, 0x00001000 }, + [PHYTIUM_E2000_UART1] = { 0x2800d000, 0x00001000 }, + [PHYTIUM_E2000_UART2] = { 0x2800e000, 0x00001000 }, + [PHYTIUM_E2000_UART3] = { 0x2800f000, 0x00001000 }, + [PHYTIUM_E2000_UART4] = { 0x28014000, 0x00001000 }, + [PHYTIUM_E2000_UART5] = { 0x2802a000, 0x00001000 }, + [PHYTIUM_E2000_UART6] = { 0x28032000, 0x00001000 }, + [PHYTIUM_E2000_GIC_DIST] = { 0x30800000, 0x00020000 }, + [PHYTIUM_E2000_GIC_ITS] = { 0x30820000, 0x00020000 }, + [PHYTIUM_E2000_GIC_REDIST] = { 0x30880000, 0x00080000 }, + [PHYTIUM_E2000_BOOT_SRAM] = { 0x30c00000, 0x00100000 }, + [PHYTIUM_E2000_BOARD_CTRL] = { 0x31800000, 0x01400000 }, + [PHYTIUM_E2000_BOOT_IACC] = { 0x38000000, 0x08000000 }, + [PHYTIUM_E2000_RAM] = { 0x80000000, 0x80000000 }, + [PHYTIUM_E2000_RAM_HIGH] = { 0x2000000000ULL, 0x180000000ULL }, +}; + +static const int phytium_e2000_uart_irqmap[] = { + [0] = 83, + [1] = 84, + [2] = 85, + [3] = 86, + [4] = 92, + [5] = 103, + [6] = 107, +}; + +typedef struct PhytiumE2000CPUConfig { + const char *type; + uint64_t mp_affinity; + int64_t cluster_id; + int64_t core_id; +} PhytiumE2000CPUConfig; + +static const PhytiumE2000CPUConfig +phytium_e2000_cpu_config[PHYTIUM_E2000_NUM_CPUS] = { + { + .type = ARM_CPU_TYPE_NAME("phytium-ftc664"), + .mp_affinity = 0x000, + .cluster_id = 0, + .core_id = 0, + }, + { + .type = ARM_CPU_TYPE_NAME("phytium-ftc664"), + .mp_affinity = 0x100, + .cluster_id = 1, + .core_id = 0, + }, + { + .type = ARM_CPU_TYPE_NAME("phytium-ftc310"), + .mp_affinity = 0x200, + .cluster_id = 2, + .core_id = 0, + }, + { + .type = ARM_CPU_TYPE_NAME("phytium-ftc310"), + .mp_affinity = 0x201, + .cluster_id = 2, + .core_id = 1, + }, +}; + +static void phytium_e2000_create_its(PhytiumE2000State *s) +{ + DeviceState *dev = qdev_new(its_class_name()); + + object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(s->gic), + &error_abort); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, + phytium_e2000_memmap[PHYTIUM_E2000_GIC_ITS].base); +} + +static void phytium_e2000_create_gic(PhytiumE2000State *s) +{ + MachineState *ms = MACHINE(s); + SysBusDevice *gicbusdev; + QList *redist_region_count; + int i; + + s->gic = qdev_new(gicv3_class_name()); + qdev_prop_set_uint32(s->gic, "revision", 3); + qdev_prop_set_uint32(s->gic, "num-cpu", ms->smp.cpus); + qdev_prop_set_uint32(s->gic, "num-irq", PHYTIUM_E2000_NUM_IRQS + 32); + qdev_prop_set_bit(s->gic, "has-security-extensions", true); + qdev_prop_set_bit(s->gic, "has-lpi", true); + + redist_region_count = qlist_new(); + qlist_append_int(redist_region_count, ms->smp.cpus); + qdev_prop_set_array(s->gic, "redist-region-count", redist_region_count); + + object_property_set_link(OBJECT(s->gic), "sysmem", + OBJECT(get_system_memory()), &error_fatal); + + gicbusdev = SYS_BUS_DEVICE(s->gic); + sysbus_realize_and_unref(gicbusdev, &error_fatal); + sysbus_mmio_map(gicbusdev, 0, + phytium_e2000_memmap[PHYTIUM_E2000_GIC_DIST].base); + sysbus_mmio_map(gicbusdev, 1, + phytium_e2000_memmap[PHYTIUM_E2000_GIC_REDIST].base); + + for (i = 0; i < ms->smp.cpus; i++) { + DeviceState *cpudev = DEVICE(qemu_get_cpu(i)); + int intidbase = PHYTIUM_E2000_NUM_IRQS + i * GIC_INTERNAL; + static const int timer_irq[] = { + [GTIMER_PHYS] = ARCH_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = ARCH_TIMER_VIRT_IRQ, + [GTIMER_HYP] = ARCH_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = ARCH_TIMER_S_EL1_IRQ, + }; + + for (int irq = 0; irq < ARRAY_SIZE(timer_irq); irq++) { + qdev_connect_gpio_out(cpudev, irq, + qdev_get_gpio_in(s->gic, intidbase + timer_irq[irq])); + } + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, + qdev_get_gpio_in(s->gic, intidbase + ARCH_GIC_MAINT_IRQ)); + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, + qdev_get_gpio_in(s->gic, intidbase + VIRTUAL_PMU_IRQ)); + + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + } + + phytium_e2000_create_its(s); +} + +static void phytium_e2000_create_uart(PhytiumE2000State *s, int index) +{ + int map_idx = PHYTIUM_E2000_UART0 + index; + DeviceState *dev = qdev_new(TYPE_PL011); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + + qdev_prop_set_chr(dev, "chardev", serial_hd(index)); + sysbus_realize_and_unref(sbd, &error_fatal); + sysbus_mmio_map(sbd, 0, phytium_e2000_memmap[map_idx].base); + sysbus_connect_irq(sbd, 0, + qdev_get_gpio_in(s->gic, phytium_e2000_uart_irqmap[index])); +} + +static void phytium_e2000_create_unimplemented(void) +{ + /* + * Preserve the SoC address map while individual boot-critical devices are + * introduced. More specific devices may overlap these low-priority + * catch-all regions without silently accepting accesses elsewhere. + */ + create_unimplemented_device( + "phytium-e2000.low-peripheral", + phytium_e2000_memmap[PHYTIUM_E2000_LOW_PERIPH].base, + phytium_e2000_memmap[PHYTIUM_E2000_LOW_PERIPH].size); + create_unimplemented_device( + "phytium-e2000.board-control", + phytium_e2000_memmap[PHYTIUM_E2000_BOARD_CTRL].base, + phytium_e2000_memmap[PHYTIUM_E2000_BOARD_CTRL].size); +} + +static void phytium_e2000_create_ram(PhytiumE2000State *s) +{ + MachineState *ms = MACHINE(s); + uint64_t low_size = + MIN(ms->ram_size, phytium_e2000_memmap[PHYTIUM_E2000_RAM].size); + uint64_t high_size = ms->ram_size - low_size; + + /* + * Keep the machine RAMBlock contiguous for migration, but expose it in + * the two physical windows implemented by E2000. The high alias resumes + * at low_size, so the intervening PCIe hole consumes no guest RAM. + */ + memory_region_init_alias(&s->ram_low, OBJECT(s), + "phytium-e2000.ram-low", ms->ram, 0, low_size); + memory_region_add_subregion(get_system_memory(), + phytium_e2000_memmap[PHYTIUM_E2000_RAM].base, &s->ram_low); + + if (!high_size) { + return; + } + + memory_region_init_alias(&s->ram_high, OBJECT(s), + "phytium-e2000.ram-high", ms->ram, low_size, high_size); + memory_region_add_subregion(get_system_memory(), + phytium_e2000_memmap[PHYTIUM_E2000_RAM_HIGH].base, &s->ram_high); +} + +static void phytium_e2000_create_cpus(PhytiumE2000State *s) +{ + MachineState *ms = MACHINE(s); + const CPUArchIdList *possible_cpus; + int i; + + possible_cpus = MACHINE_GET_CLASS(ms)->possible_cpu_arch_ids(ms); + + for (i = 0; i < ms->smp.cpus; i++) { + Object *cpuobj = object_new(possible_cpus->cpus[i].type); + CPUState *cs; + + object_property_set_int(cpuobj, "mp-affinity", + possible_cpus->cpus[i].arch_id, + &error_abort); + object_property_set_int(cpuobj, "cntfrq", PHYTIUM_E2000_GTIMER_HZ, + &error_abort); + if (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); + } + 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); + object_unref(cpuobj); + } +} + +static void phytium_pi_init(MachineState *ms) +{ + PhytiumE2000State *s = PHYTIUM_PI(ms); + int i; + + if (kvm_enabled()) { + error_report("phytium-pi: KVM is not supported"); + exit(1); + } + + if (ms->smp.cpus > PHYTIUM_E2000_NUM_CPUS) { + error_report("phytium-pi supports at most %d CPUs", + PHYTIUM_E2000_NUM_CPUS); + exit(1); + } + + if (ms->ram_size > + phytium_e2000_memmap[PHYTIUM_E2000_RAM].size + + phytium_e2000_memmap[PHYTIUM_E2000_RAM_HIGH].size) { + error_report("phytium-pi supports at most 8 GiB RAM"); + exit(1); + } + + phytium_e2000_create_ram(s); + phytium_e2000_create_unimplemented(); + + phytium_e2000_create_cpus(s); + phytium_e2000_create_gic(s); + + for (i = 0; i < PHYTIUM_E2000_NUM_UARTS; i++) { + phytium_e2000_create_uart(s, i); + } + + s->bootinfo.ram_size = ms->ram_size; + 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; + arm_load_kernel(ARM_CPU(first_cpu), ms, &s->bootinfo); +} + +static const CPUArchIdList *phytium_e2000_possible_cpu_arch_ids(MachineState *ms) +{ + int i; + + if (ms->possible_cpus) { + return ms->possible_cpus; + } + + ms->possible_cpus = g_malloc0(sizeof(CPUArchIdList) + + sizeof(CPUArchId) * ms->smp.max_cpus); + ms->possible_cpus->len = ms->smp.max_cpus; + + for (i = 0; i < ms->possible_cpus->len; i++) { + const PhytiumE2000CPUConfig *config = &phytium_e2000_cpu_config[i]; + + ms->possible_cpus->cpus[i].type = config->type; + ms->possible_cpus->cpus[i].arch_id = config->mp_affinity; + ms->possible_cpus->cpus[i].props.has_cluster_id = true; + ms->possible_cpus->cpus[i].props.cluster_id = config->cluster_id; + ms->possible_cpus->cpus[i].props.has_core_id = true; + ms->possible_cpus->cpus[i].props.core_id = config->core_id; + ms->possible_cpus->cpus[i].props.has_thread_id = true; + ms->possible_cpus->cpus[i].props.thread_id = 0; + } + + return ms->possible_cpus; +} + +static void phytium_pi_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + /* The machine assigns the FTC310 slots independently */ + ARM_CPU_TYPE_NAME("phytium-ftc664"), + NULL, + }; + + mc->init = phytium_pi_init; + mc->desc = "Phytium Pi board (Phytium E2000Q)"; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("phytium-ftc664"); + 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; + mc->default_ram_id = "phytium-e2000.ram"; + mc->minimum_page_bits = 12; + mc->block_default_type = IF_SD; + mc->no_cdrom = 1; + mc->possible_cpu_arch_ids = phytium_e2000_possible_cpu_arch_ids; +} + +static const TypeInfo phytium_pi_info = { + .name = TYPE_PHYTIUM_PI, + .parent = TYPE_MACHINE, + .class_init = phytium_pi_class_init, + .instance_size = sizeof(PhytiumE2000State), + .interfaces = aarch64_machine_interfaces, +}; + +static void phytium_pi_machine_init(void) +{ + type_register_static(&phytium_pi_info); +} + +type_init(phytium_pi_machine_init); -- 2.53.0
