diff options
Diffstat (limited to 'hw/ppc')
57 files changed, 2520 insertions, 1127 deletions
diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index ced6bbc..347dcce 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -44,6 +44,11 @@ config POWERNV select SSI_M25P80 select PNV_SPI +config PPC405 + bool + default y + depends on PPC + config PPC440 bool default y @@ -87,7 +92,7 @@ config AMIGAONE select VT82C686 select SMBUS_EEPROM -config PEGASOS2 +config PEGASOS bool default y depends on PPC diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index e9407a5..74a1fa3 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -173,7 +173,7 @@ static const Property nvram_properties[] = { DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk), }; -static void nvram_class_init(ObjectClass *oc, void *data) +static void nvram_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -221,7 +221,7 @@ struct bd_info { static void create_bd_info(hwaddr addr, ram_addr_t ram_size) { - struct bd_info *bd = g_new0(struct bd_info, 1); + g_autofree struct bd_info *bd = g_new0(struct bd_info, 1); bd->bi_memsize = cpu_to_be32(ram_size); bd->bi_flashstart = cpu_to_be32(PROM_ADDR); @@ -324,11 +324,7 @@ static void amigaone_init(MachineState *machine) error_report("Could not find firmware '%s'", machine->firmware); exit(1); } - sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE); - if (sz <= 0 || sz > PROM_SIZE) { - error_report("Could not load firmware '%s'", filename); - exit(1); - } + sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE, &error_fatal); } /* Articia S */ @@ -413,12 +409,7 @@ static void amigaone_init(MachineState *machine) loadaddr = ROUND_UP(loadaddr + 4 * MiB, 4 * KiB); loadaddr = MAX(loadaddr, INITRD_MIN_ADDR); sz = load_image_targphys(machine->initrd_filename, loadaddr, - bi->bd_info - loadaddr); - if (sz <= 0) { - error_report("Could not load initrd '%s'", - machine->initrd_filename); - exit(1); - } + bi->bd_info - loadaddr, &error_fatal); bi->initrd_start = loadaddr; bi->initrd_end = loadaddr + sz; } diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 69269aa..8842f7f 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -18,7 +18,9 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/guest-random.h" +#include "exec/target_page.h" #include "qapi/error.h" +#include "cpu-models.h" #include "e500.h" #include "e500-ccsr.h" #include "net/net.h" @@ -78,8 +80,6 @@ #define MPC85XX_ESDHC_IRQ 72 #define RTC_REGS_OFFSET 0x68 -#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) - struct boot_info { uint32_t dt_base; @@ -119,7 +119,7 @@ static uint32_t *pci_map_create(void *fdt, uint32_t mpic, int first_slot, } static void dt_serial_create(void *fdt, unsigned long long offset, - const char *soc, const char *mpic, + const char *soc, uint32_t freq, const char *mpic, const char *alias, int idx, bool defcon) { char *ser; @@ -130,7 +130,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); - qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", PLATFORM_CLK_FREQ_HZ); + qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", freq); qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); @@ -381,8 +381,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, int fdt_size; void *fdt; uint8_t hypercall[16]; - uint32_t clock_freq = PLATFORM_CLK_FREQ_HZ; - uint32_t tb_freq = PLATFORM_CLK_FREQ_HZ; + uint32_t clock_freq, tb_freq; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; char *soc; @@ -410,7 +409,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (dtb_file) { char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, dtb_file); if (!filename) { goto out; } @@ -483,6 +482,9 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (kvmppc_get_hasidle(env)) { qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } + } else { + clock_freq = pmc->clock_freq; + tb_freq = pmc->tb_freq; } /* Create CPU nodes */ @@ -563,12 +565,12 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, */ if (serial_hd(1)) { dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, - soc, mpic, "serial1", 1, false); + soc, pmc->clock_freq, mpic, "serial1", 1, false); } if (serial_hd(0)) { dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, - soc, mpic, "serial0", 0, true); + soc, pmc->clock_freq, mpic, "serial0", 0, true); } /* i2c */ @@ -930,7 +932,6 @@ void ppce500_init(MachineState *machine) CPUPPCState *firstenv = NULL; MemoryRegion *ccsr_addr_space; SysBusDevice *s; - PPCE500CCSRState *ccsr; I2CBus *i2c; irqs = g_new0(IrqLines, smp_cpus); @@ -942,9 +943,8 @@ void ppce500_init(MachineState *machine) env = &cpu->env; cs = CPU(cpu); - if (env->mmu_model != POWERPC_MMU_BOOKE206) { - error_report("MMU model %i not supported by this machine", - env->mmu_model); + if (!(POWERPC_CPU_GET_CLASS(cpu)->svr & POWERPC_SVR_E500)) { + error_report("This machine needs a CPU from the e500 family"); exit(1); } @@ -967,7 +967,7 @@ void ppce500_init(MachineState *machine) env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; - ppc_booke_timers_init(cpu, PLATFORM_CLK_FREQ_HZ, PPC_TIMER_E500); + ppc_booke_timers_init(cpu, pmc->tb_freq, PPC_TIMER_E500); /* Register reset handler */ if (!i) { @@ -992,10 +992,10 @@ void ppce500_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0, machine->ram); dev = qdev_new("e500-ccsr"); + s = SYS_BUS_DEVICE(dev); object_property_add_child(OBJECT(machine), "e500-ccsr", OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - ccsr = CCSR(dev); - ccsr_addr_space = &ccsr->ccsr_space; + sysbus_realize_and_unref(s, &error_fatal); + ccsr_addr_space = sysbus_mmio_get_region(s, 0); memory_region_add_subregion(address_space_mem, pmc->ccsrbar_base, ccsr_addr_space); @@ -1226,14 +1226,8 @@ void ppce500_init(MachineState *machine) if (machine->kernel_filename && !kernel_as_payload) { kernel_base = cur_base; kernel_size = load_image_targphys(machine->kernel_filename, - cur_base, - machine->ram_size - cur_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", - machine->kernel_filename); - exit(1); - } - + cur_base, machine->ram_size - cur_base, + &error_fatal); cur_base += kernel_size; } @@ -1241,14 +1235,8 @@ void ppce500_init(MachineState *machine) if (machine->initrd_filename) { initrd_base = (cur_base + INITRD_LOAD_PAD) & ~INITRD_PAD_MASK; initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - machine->ram_size - initrd_base); - - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } - + machine->ram_size - initrd_base, + &error_fatal); cur_base = initrd_base + initrd_size; } @@ -1283,6 +1271,7 @@ static void e500_ccsr_initfn(Object *obj) PPCE500CCSRState *ccsr = CCSR(obj); memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr", MPC8544_CCSRBAR_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(ccsr), &ccsr->ccsr_space); } static const TypeInfo e500_ccsr_info = { diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 01db102..00f4905 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -5,6 +5,8 @@ #include "hw/platform-bus.h" #include "qom/object.h" +#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) + struct PPCE500MachineState { /*< private >*/ MachineState parent_obj; @@ -37,6 +39,8 @@ struct PPCE500MachineClass { hwaddr pci_mmio_base; hwaddr pci_mmio_bus_base; hwaddr spin_base; + uint32_t clock_freq; + uint32_t tb_freq; }; void ppce500_init(MachineState *machine); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 70a8033..4f1d659 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -68,7 +68,7 @@ HotplugHandler *e500plat_machine_get_hotpug_handler(MachineState *machine, #define TYPE_E500PLAT_MACHINE MACHINE_TYPE_NAME("ppce500") -static void e500plat_machine_class_init(ObjectClass *oc, void *data) +static void e500plat_machine_class_init(ObjectClass *oc, const void *data) { PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); @@ -93,6 +93,8 @@ static void e500plat_machine_class_init(ObjectClass *oc, void *data) pmc->pci_mmio_base = 0xC00000000ULL; pmc->pci_mmio_bus_base = 0xE0000000ULL; pmc->spin_base = 0xFEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "generic paravirt e500 platform"; mc->init = e500plat_init; @@ -107,7 +109,7 @@ static const TypeInfo e500plat_info = { .name = TYPE_E500PLAT_MACHINE, .parent = TYPE_PPCE500_MACHINE, .class_init = e500plat_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2d5309d..951de4b 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -50,6 +50,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "hw/nvram/mac_nvram.h" @@ -187,7 +188,8 @@ static void ppc_core99_init(MachineState *machine) if (bios_size <= 0) { /* or load binary ROM image */ - bios_size = load_image_targphys(filename, PROM_BASE, PROM_SIZE); + bios_size = load_image_targphys(filename, PROM_BASE, PROM_SIZE, + &error_fatal); } g_free(filename); } @@ -209,24 +211,16 @@ static void ppc_core99_init(MachineState *machine) if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, kernel_base, - machine->ram_size - kernel_base); - } - if (kernel_size < 0) { - error_report("could not load kernel '%s'", - machine->kernel_filename); - exit(1); + machine->ram_size - kernel_base, + &error_fatal); } /* load initrd */ if (machine->initrd_filename) { initrd_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } + machine->ram_size - initrd_base, + &error_fatal); cmdline_base = TARGET_PAGE_ALIGN(initrd_base + initrd_size); } else { cmdline_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); @@ -562,7 +556,7 @@ static int core99_kvm_type(MachineState *machine, const char *arg) return 2; } -static void core99_machine_class_init(ObjectClass *oc, void *data) +static void core99_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); @@ -630,8 +624,6 @@ static void core99_instance_init(Object *obj) object_property_set_description(obj, "via", "Set VIA configuration. " "Valid values are cuda, pmu and pmu-adb"); - - return; } static const TypeInfo core99_machine_info = { @@ -640,7 +632,7 @@ static const TypeInfo core99_machine_info = { .class_init = core99_machine_class_init, .instance_init = core99_instance_init, .instance_size = sizeof(Core99MachineState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index b581469..cd2bb46 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -28,6 +28,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "hw/boards.h" @@ -142,7 +143,8 @@ static void ppc_heathrow_init(MachineState *machine) if (bios_size <= 0) { /* or if could not load ELF try loading a binary ROM image */ - bios_size = load_image_targphys(filename, PROM_BASE, PROM_SIZE); + bios_size = load_image_targphys(filename, PROM_BASE, PROM_SIZE, + &error_fatal); bios_addr = PROM_BASE; } g_free(filename); @@ -165,12 +167,8 @@ static void ppc_heathrow_init(MachineState *machine) if (kernel_size < 0) { kernel_size = load_image_targphys(machine->kernel_filename, kernel_base, - machine->ram_size - kernel_base); - } - if (kernel_size < 0) { - error_report("could not load kernel '%s'", - machine->kernel_filename); - exit(1); + machine->ram_size - kernel_base, + &error_fatal); } /* load initrd */ if (machine->initrd_filename) { @@ -178,12 +176,8 @@ static void ppc_heathrow_init(MachineState *machine) KERNEL_GAP); initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } + machine->ram_size - initrd_base, + &error_fatal); cmdline_base = TARGET_PAGE_ALIGN(initrd_base + initrd_size); } else { cmdline_base = TARGET_PAGE_ALIGN(kernel_base + kernel_size + KERNEL_GAP); @@ -401,7 +395,7 @@ static int heathrow_kvm_type(MachineState *machine, const char *arg) return 2; } -static void heathrow_class_init(ObjectClass *oc, void *data) +static void heathrow_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); @@ -429,7 +423,7 @@ static const TypeInfo ppc_heathrow_machine_info = { .name = MACHINE_TYPE_NAME("g3beige"), .parent = TYPE_MACHINE, .class_init = heathrow_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 9893f8a..f7dac87 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -26,6 +26,7 @@ ppc_ss.add(when: 'CONFIG_PSERIES', if_true: files( 'spapr_nvdimm.c', 'spapr_rtas_ddw.c', 'spapr_numa.c', + 'spapr_fadump.c', 'pef.c', )) ppc_ss.add(when: ['CONFIG_PSERIES', 'CONFIG_TCG'], if_true: files( @@ -57,6 +58,8 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv_n1_chiplet.c', )) # PowerPC 4xx boards +ppc_ss.add(when: 'CONFIG_PPC405', if_true: files( + 'ppe42_machine.c')) ppc_ss.add(when: 'CONFIG_PPC440', if_true: files( 'ppc440_bamboo.c', 'ppc440_uc.c')) @@ -84,8 +87,8 @@ ppc_ss.add(when: 'CONFIG_E500', if_true: files( ppc_ss.add(when: 'CONFIG_VIRTEX', if_true: files('virtex_ml507.c')) # AmigaOne ppc_ss.add(when: 'CONFIG_AMIGAONE', if_true: files('amigaone.c')) -# Pegasos2 -ppc_ss.add(when: 'CONFIG_PEGASOS2', if_true: files('pegasos2.c')) +# Pegasos +ppc_ss.add(when: 'CONFIG_PEGASOS', if_true: files('pegasos.c')) ppc_ss.add(when: 'CONFIG_VOF', if_true: files('vof.c')) ppc_ss.add(when: ['CONFIG_VOF', 'CONFIG_PSERIES'], if_true: files('spapr_vof.c')) diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index d74af76..5826985 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -37,7 +37,7 @@ static void mpc8544ds_init(MachineState *machine) ppce500_init(machine); } -static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) +static void mpc8544ds_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); @@ -55,6 +55,8 @@ static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) pmc->pci_mmio_bus_base = 0xC0000000ULL; pmc->pci_pio_base = 0xE1000000ULL; pmc->spin_base = 0xEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "mpc8544ds"; mc->init = mpc8544ds_init; diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index 8b2d726..39b4ce9 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -19,7 +19,6 @@ #define TYPE_PEF_GUEST "pef-guest" OBJECT_DECLARE_SIMPLE_TYPE(PefGuest, PEF_GUEST) -typedef struct PefGuest PefGuest; typedef struct PefGuestClass PefGuestClass; struct PefGuestClass { @@ -128,7 +127,7 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest, { TYPE_USER_CREATABLE }, { NULL }) -static void pef_guest_class_init(ObjectClass *oc, void *data) +static void pef_guest_class_init(ObjectClass *oc, const void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos.c index 246d6d6..8ce185d 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos.c @@ -1,5 +1,5 @@ /* - * QEMU PowerPC CHRP (Genesi/bPlan Pegasos II) hardware System Emulator + * QEMU PowerPC CHRP (Genesi/bPlan Pegasos I/II) hardware System Emulator * * Copyright (c) 2018-2021 BALATON Zoltan * @@ -15,6 +15,7 @@ #include "hw/pci/pci_host.h" #include "hw/irq.h" #include "hw/or-irq.h" +#include "hw/pci-host/articia.h" #include "hw/pci-host/mv64361.h" #include "hw/isa/vt82c686.h" #include "hw/ide/pci.h" @@ -31,7 +32,7 @@ #include "qemu/error-report.h" #include "system/kvm.h" #include "kvm_ppc.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/qom-qobject.h" #include "qobject/qdict.h" #include "trace.h" @@ -55,34 +56,27 @@ #define H_PRIVILEGE -3 /* Caller not privileged */ #define H_PARAMETER -4 /* Parameter invalid, out-of-range or conflicting */ -#define BUS_FREQ_HZ 133333333 +typedef enum { + PEGASOS1 = 1, + PEGASOS2 = 2, +} PegasosMachineType; -#define PCI0_CFG_ADDR 0xcf8 -#define PCI0_MEM_BASE 0xc0000000 -#define PCI0_MEM_SIZE 0x20000000 -#define PCI0_IO_BASE 0xf8000000 -#define PCI0_IO_SIZE 0x10000 - -#define PCI1_CFG_ADDR 0xc78 -#define PCI1_MEM_BASE 0x80000000 -#define PCI1_MEM_SIZE 0x40000000 -#define PCI1_IO_BASE 0xfe000000 -#define PCI1_IO_SIZE 0x10000 - -#define TYPE_PEGASOS2_MACHINE MACHINE_TYPE_NAME("pegasos2") -OBJECT_DECLARE_TYPE(Pegasos2MachineState, MachineClass, PEGASOS2_MACHINE) +#define TYPE_PEGASOS_MACHINE MACHINE_TYPE_NAME("pegasos") +OBJECT_DECLARE_SIMPLE_TYPE(PegasosMachineState, PEGASOS_MACHINE) -struct Pegasos2MachineState { +struct PegasosMachineState { MachineState parent_obj; + PegasosMachineType type; PowerPCCPU *cpu; - DeviceState *mv; + DeviceState *nb; /* north bridge */ + DeviceState *sb; /* south bridge */ + int bus_freq_hz; IRQState pci_irqs[PCI_NUM_PINS]; OrIRQState orirq[PCI_NUM_PINS]; qemu_irq mv_pirq[PCI_NUM_PINS]; qemu_irq via_pirq[PCI_NUM_PINS]; Vof *vof; - void *fdt_blob; uint64_t kernel_addr; uint64_t kernel_entry; uint64_t kernel_size; @@ -90,44 +84,80 @@ struct Pegasos2MachineState { uint64_t initrd_size; }; -static void *build_fdt(MachineState *machine, int *fdt_size); +static void *pegasos1_build_fdt(PegasosMachineState *pm, int *fdt_size); +static void *pegasos2_build_fdt(PegasosMachineState *pm, int *fdt_size); -static void pegasos2_cpu_reset(void *opaque) +static void pegasos_cpu_reset(void *opaque) { PowerPCCPU *cpu = opaque; - Pegasos2MachineState *pm = PEGASOS2_MACHINE(current_machine); + PegasosMachineState *pm = PEGASOS_MACHINE(current_machine); cpu_reset(CPU(cpu)); cpu->env.spr[SPR_HID1] = 7ULL << 28; if (pm->vof) { cpu->env.gpr[1] = 2 * VOF_STACK_SIZE - 0x20; cpu->env.nip = 0x100; + } else if (pm->type == PEGASOS1) { + cpu->env.nip = 0xfffc0100; } cpu_ppc_tb_reset(&cpu->env); } static void pegasos2_pci_irq(void *opaque, int n, int level) { - Pegasos2MachineState *pm = opaque; + PegasosMachineState *pm = opaque; /* PCI interrupt lines are connected to both MV64361 and VT8231 */ qemu_set_irq(pm->mv_pirq[n], level); qemu_set_irq(pm->via_pirq[n], level); } -static void pegasos2_init(MachineState *machine) +/* Set up PCI interrupt routing: lines from pci.0 and pci.1 are ORed */ +static void pegasos2_setup_pci_irq(PegasosMachineState *pm) +{ + for (int h = 0; h < 2; h++) { + DeviceState *pd; + g_autofree const char *pn = g_strdup_printf("pcihost%d", h); + + pd = DEVICE(object_resolve_path_component(OBJECT(pm->nb), pn)); + assert(pd); + for (int i = 0; i < PCI_NUM_PINS; i++) { + OrIRQState *ori = &pm->orirq[i]; + + if (h == 0) { + g_autofree const char *n = g_strdup_printf("pci-orirq[%d]", i); + + object_initialize_child_with_props(OBJECT(pm), n, + ori, sizeof(*ori), + TYPE_OR_IRQ, &error_fatal, + "num-lines", "2", NULL); + qdev_realize(DEVICE(ori), NULL, &error_fatal); + qemu_init_irq(&pm->pci_irqs[i], pegasos2_pci_irq, pm, i); + qdev_connect_gpio_out(DEVICE(ori), 0, &pm->pci_irqs[i]); + pm->mv_pirq[i] = qdev_get_gpio_in_named(pm->nb, "gpp", 12 + i); + pm->via_pirq[i] = qdev_get_gpio_in_named(pm->sb, "pirq", i); + } + qdev_connect_gpio_out(pd, i, qdev_get_gpio_in(DEVICE(ori), h)); + } + } + qdev_connect_gpio_out_named(pm->sb, "intr", 0, + qdev_get_gpio_in_named(pm->nb, "gpp", 31)); +} + +static void pegasos_init(MachineState *machine) { - Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); + PegasosMachineState *pm = PEGASOS_MACHINE(machine); CPUPPCState *env; MemoryRegion *rom = g_new(MemoryRegion, 1); - PCIBus *pci_bus; + PCIBus *pci_bus = NULL; Object *via; PCIDevice *dev; I2CBus *i2c_bus; const char *fwname = machine->firmware ?: PROM_FILENAME; char *filename; - int i; + hwaddr prom_addr; ssize_t sz; + int devfn; uint8_t *spd_data; /* init CPU */ @@ -139,8 +169,8 @@ static void pegasos2_init(MachineState *machine) } /* Set time-base frequency */ - cpu_ppc_tb_init(env, BUS_FREQ_HZ / 4); - qemu_register_reset(pegasos2_cpu_reset, pm->cpu); + cpu_ppc_tb_init(env, pm->bus_freq_hz / 4); + qemu_register_reset(pegasos_cpu_reset, pm->cpu); /* RAM */ if (machine->ram_size > 2 * GiB) { @@ -158,12 +188,17 @@ static void pegasos2_init(MachineState *machine) if (!machine->firmware && !pm->vof) { pm->vof = g_malloc0(sizeof(*pm->vof)); } - memory_region_init_rom(rom, NULL, "pegasos2.rom", PROM_SIZE, &error_fatal); - memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); + prom_addr = PROM_ADDR; + if (pm->type == PEGASOS1) { + prom_addr += PROM_SIZE; + } + memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), prom_addr, rom); sz = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); if (sz <= 0) { - sz = load_image_targphys(filename, pm->vof ? 0 : PROM_ADDR, PROM_SIZE); + sz = load_image_targphys(filename, pm->vof ? 0 : prom_addr, PROM_SIZE, + &error_fatal); } if (sz <= 0 || sz > PROM_SIZE) { error_report("Could not load firmware '%s'", filename); @@ -174,16 +209,38 @@ static void pegasos2_init(MachineState *machine) pm->vof->fw_size = sz; } - /* Marvell Discovery II system controller */ - pm->mv = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, - qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT))); - for (i = 0; i < PCI_NUM_PINS; i++) { - pm->mv_pirq[i] = qdev_get_gpio_in_named(pm->mv, "gpp", 12 + i); + /* north bridge */ + switch (pm->type) { + case PEGASOS1: + { + MemoryRegion *pci_mem, *mr; + + /* Articia S */ + pm->nb = DEVICE(sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL)); + pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->nb), 1); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(pm->nb), "pci-mem-low", pci_mem, + 0, 0x1000000); + memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(pm->nb), "pci-mem-high", pci_mem, + 0x80000000, 0x7d000000); + memory_region_add_subregion(get_system_memory(), 0x80000000, mr); + pci_bus = PCI_BUS(qdev_get_child_bus(pm->nb, "pci.0")); + break; + } + case PEGASOS2: + /* Marvell Discovery II system controller */ + pm->nb = DEVICE(sysbus_create_simple(TYPE_MV64361, -1, + qdev_get_gpio_in(DEVICE(pm->cpu), PPC6xx_INPUT_INT))); + pci_bus = mv64361_get_pci_bus(pm->nb, 1); + break; } - pci_bus = mv64361_get_pci_bus(pm->mv, 1); /* VIA VT8231 South Bridge (multifunction PCI device) */ - via = OBJECT(pci_new_multifunction(PCI_DEVFN(12, 0), TYPE_VT8231_ISA)); + devfn = PCI_DEVFN(pm->type == PEGASOS1 ? 7 : 12, 0); + pm->sb = DEVICE(pci_new_multifunction(devfn, TYPE_VT8231_ISA)); + via = OBJECT(pm->sb); /* Set properties on individual devices before realizing the south bridge */ if (machine->audiodev) { @@ -192,14 +249,9 @@ static void pegasos2_init(MachineState *machine) } pci_realize_and_unref(PCI_DEVICE(via), pci_bus, &error_abort); - for (i = 0; i < PCI_NUM_PINS; i++) { - pm->via_pirq[i] = qdev_get_gpio_in_named(DEVICE(via), "pirq", i); - } object_property_add_alias(OBJECT(machine), "rtc-time", object_resolve_path_component(via, "rtc"), "date"); - qdev_connect_gpio_out_named(DEVICE(via), "intr", 0, - qdev_get_gpio_in_named(pm->mv, "gpp", 31)); dev = PCI_DEVICE(object_resolve_path_component(via, "ide")); pci_ide_create_devs(dev); @@ -212,29 +264,20 @@ static void pegasos2_init(MachineState *machine) /* other PC hardware */ pci_vga_init(pci_bus); - /* PCI interrupt routing: lines from pci.0 and pci.1 are ORed */ - for (int h = 0; h < 2; h++) { - DeviceState *pd; - g_autofree const char *pn = g_strdup_printf("pcihost%d", h); - - pd = DEVICE(object_resolve_path_component(OBJECT(pm->mv), pn)); - assert(pd); - for (i = 0; i < PCI_NUM_PINS; i++) { - OrIRQState *ori = &pm->orirq[i]; - - if (h == 0) { - g_autofree const char *n = g_strdup_printf("pci-orirq[%d]", i); - - object_initialize_child_with_props(OBJECT(pm), n, - ori, sizeof(*ori), - TYPE_OR_IRQ, &error_fatal, - "num-lines", "2", NULL); - qdev_realize(DEVICE(ori), NULL, &error_fatal); - qemu_init_irq(&pm->pci_irqs[i], pegasos2_pci_irq, pm, i); - qdev_connect_gpio_out(DEVICE(ori), 0, &pm->pci_irqs[i]); - } - qdev_connect_gpio_out(pd, i, qdev_get_gpio_in(DEVICE(ori), h)); + /* pci interrupt routing */ + switch (pm->type) { + case PEGASOS1: + qdev_connect_gpio_out_named(pm->sb, "intr", 0, + qdev_get_gpio_in(DEVICE(pm->cpu), + PPC6xx_INPUT_INT)); + for (int i = 0; i < PCI_NUM_PINS; i++) { + qdev_connect_gpio_out(pm->nb, i, + qdev_get_gpio_in_named(pm->sb, "pirq", i)); } + break; + case PEGASOS2: + pegasos2_setup_pci_irq(pm); + break; } if (machine->kernel_filename) { @@ -259,12 +302,7 @@ static void pegasos2_init(MachineState *machine) pm->initrd_addr = ROUND_UP(pm->initrd_addr, 4); pm->initrd_addr = MAX(pm->initrd_addr, INITRD_MIN_ADDR); sz = load_image_targphys(machine->initrd_filename, pm->initrd_addr, - machine->ram_size - pm->initrd_addr); - if (sz <= 0) { - error_report("Could not load initrd '%s'", - machine->initrd_filename); - exit(1); - } + machine->ram_size - pm->initrd_addr, &error_fatal); pm->initrd_size = sz; } @@ -273,25 +311,104 @@ static void pegasos2_init(MachineState *machine) } } -static uint32_t pegasos2_mv_reg_read(Pegasos2MachineState *pm, +static void pegasos_superio_write(uint8_t addr, uint8_t val) +{ + cpu_physical_memory_write(0xfe0003f0, &addr, 1); + cpu_physical_memory_write(0xfe0003f1, &val, 1); +} + +static void pegasos1_pci_config_write(PegasosMachineState *pm, int bus, + uint32_t addr, uint32_t len, uint32_t val) +{ + addr |= BIT(31); + cpu_physical_memory_write(0xfec00cf8, &addr, 4); + cpu_physical_memory_write(0xfee00cfc, &val, len); +} + +static void pegasos1_chipset_reset(PegasosMachineState *pm) +{ + uint8_t elcr = 0x2e; + cpu_physical_memory_write(0xfe0004d1, &elcr, sizeof(elcr)); + + pegasos1_pci_config_write(pm, 0, PCI_COMMAND, 2, PCI_COMMAND_IO | + PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 0) << 8) | + PCI_INTERRUPT_LINE, 2, 0x9); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 0) << 8) | + 0x50, 1, 0x6); + pegasos_superio_write(0xf4, 0xbe); + pegasos_superio_write(0xf6, 0xef); + pegasos_superio_write(0xf7, 0xfc); + pegasos_superio_write(0xf2, 0x14); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 0) << 8) | + 0x51, 1, 0x3d); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 0) << 8) | + 0x55, 1, 0x90); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 0) << 8) | + 0x56, 1, 0x99); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 0) << 8) | + 0x57, 1, 0x90); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 1) << 8) | + PCI_INTERRUPT_LINE, 2, 0x10e); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 1) << 8) | + PCI_CLASS_PROG, 1, 0xf); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 1) << 8) | + 0x40, 1, 0xb); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 1) << 8) | + 0x50, 4, 0x17171717); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 1) << 8) | + PCI_COMMAND, 2, 0x87); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 2) << 8) | + PCI_INTERRUPT_LINE, 2, 0x409); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 2) << 8) | + PCI_COMMAND, 2, 0x7); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 3) << 8) | + PCI_INTERRUPT_LINE, 2, 0x409); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 3) << 8) | + PCI_COMMAND, 2, 0x7); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 4) << 8) | + PCI_INTERRUPT_LINE, 2, 0x9); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 4) << 8) | + 0x48, 4, 0x2001); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 4) << 8) | + 0x41, 1, 0); + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 4) << 8) | + 0x90, 4, 0x1000); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 5) << 8) | + PCI_INTERRUPT_LINE, 2, 0x309); + + pegasos1_pci_config_write(pm, 0, (PCI_DEVFN(7, 6) << 8) | + PCI_INTERRUPT_LINE, 2, 0x309); +} + +static uint32_t pegasos2_mv_reg_read(PegasosMachineState *pm, uint32_t addr, uint32_t len) { - MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->nb), 0); uint64_t val = 0xffffffffULL; memory_region_dispatch_read(r, addr, &val, size_memop(len) | MO_LE, MEMTXATTRS_UNSPECIFIED); return val; } -static void pegasos2_mv_reg_write(Pegasos2MachineState *pm, uint32_t addr, +static void pegasos2_mv_reg_write(PegasosMachineState *pm, uint32_t addr, uint32_t len, uint32_t val) { - MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->mv), 0); + MemoryRegion *r = sysbus_mmio_get_region(SYS_BUS_DEVICE(pm->nb), 0); memory_region_dispatch_write(r, addr, val, size_memop(len) | MO_LE, MEMTXATTRS_UNSPECIFIED); } -static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, +#define PCI0_CFG_ADDR 0xcf8 +#define PCI1_CFG_ADDR 0xc78 + +static uint32_t pegasos2_pci_config_read(PegasosMachineState *pm, int bus, uint32_t addr, uint32_t len) { hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; @@ -304,7 +421,7 @@ static uint32_t pegasos2_pci_config_read(Pegasos2MachineState *pm, int bus, return val; } -static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, +static void pegasos2_pci_config_write(PegasosMachineState *pm, int bus, uint32_t addr, uint32_t len, uint32_t val) { hwaddr pcicfg = bus ? PCI1_CFG_ADDR : PCI0_CFG_ADDR; @@ -313,25 +430,8 @@ static void pegasos2_pci_config_write(Pegasos2MachineState *pm, int bus, pegasos2_mv_reg_write(pm, pcicfg + 4, len, val); } -static void pegasos2_superio_write(uint8_t addr, uint8_t val) +static void pegasos2_chipset_reset(PegasosMachineState *pm) { - cpu_physical_memory_write(PCI1_IO_BASE + 0x3f0, &addr, 1); - cpu_physical_memory_write(PCI1_IO_BASE + 0x3f1, &val, 1); -} - -static void pegasos2_machine_reset(MachineState *machine, ResetType type) -{ - Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); - void *fdt; - uint64_t d[2]; - int sz; - - qemu_devices_reset(type); - if (!pm->vof) { - return; /* Firmware should set up machine so nothing to do */ - } - - /* Otherwise, set up devices that board firmware would normally do */ pegasos2_mv_reg_write(pm, 0, 4, 0x28020ff); pegasos2_mv_reg_write(pm, 0x278, 4, 0xa31fc); pegasos2_mv_reg_write(pm, 0xf300, 4, 0x11ff0400); @@ -346,10 +446,10 @@ static void pegasos2_machine_reset(MachineState *machine, ResetType type) PCI_INTERRUPT_LINE, 2, 0x9); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x6); - pegasos2_superio_write(0xf4, 0xbe); - pegasos2_superio_write(0xf6, 0xef); - pegasos2_superio_write(0xf7, 0xfc); - pegasos2_superio_write(0xf2, 0x14); + pegasos_superio_write(0xf4, 0xbe); + pegasos_superio_write(0xf6, 0xef); + pegasos_superio_write(0xf7, 0xfc); + pegasos_superio_write(0xf2, 0x14); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | 0x50, 1, 0x2); pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 0) << 8) | @@ -394,6 +494,35 @@ static void pegasos2_machine_reset(MachineState *machine, ResetType type) pegasos2_pci_config_write(pm, 1, (PCI_DEVFN(12, 6) << 8) | PCI_INTERRUPT_LINE, 2, 0x309); +} + +static void pegasos_machine_reset(MachineState *machine, ResetType type) +{ + PegasosMachineState *pm = PEGASOS_MACHINE(machine); + void *fdt = NULL; + uint32_t c[2]; + uint64_t d[2]; + int sz; + + qemu_devices_reset(type); + if (!pm->vof) { + return; /* Firmware should set up machine so nothing to do */ + } + + /* Otherwise, set up devices that board firmware would normally do */ + switch (pm->type) { + case PEGASOS1: + pegasos1_chipset_reset(pm); + fdt = pegasos1_build_fdt(pm, &sz); + break; + case PEGASOS2: + pegasos2_chipset_reset(pm); + fdt = pegasos2_build_fdt(pm, &sz); + break; + } + if (!fdt) { + exit(1); + } /* Device tree and VOF set up */ vof_init(pm->vof, machine->ram_size, &error_fatal); @@ -411,19 +540,32 @@ static void pegasos2_machine_reset(MachineState *machine, ResetType type) error_report("Memory for initrd is in use"); exit(1); } - fdt = build_fdt(machine, &sz); + + /* Set memory size */ + c[0] = 0; + c[1] = cpu_to_be32(machine->ram_size); + qemu_fdt_setprop(fdt, "/memory@0", "reg", c, sizeof(c)); + + /* Boot parameters */ + if (pm->initrd_addr && pm->initrd_size) { + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", + pm->initrd_addr + pm->initrd_size); + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", + pm->initrd_addr); + } + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", + machine->kernel_cmdline ?: ""); /* FIXME: VOF assumes entry is same as load address */ d[0] = cpu_to_be64(pm->kernel_entry); d[1] = cpu_to_be64(pm->kernel_size - (pm->kernel_entry - pm->kernel_addr)); qemu_fdt_setprop(fdt, "/chosen", "qemu,boot-kernel", d, sizeof(d)); - g_free(pm->fdt_blob); - pm->fdt_blob = fdt; - vof_build_dt(fdt, pm->vof); + vof_client_open_store(fdt, pm->vof, "/chosen", "stdin", "/failsafe"); vof_client_open_store(fdt, pm->vof, "/chosen", "stdout", "/failsafe"); /* Set machine->fdt for 'dumpdtb' QMP/HMP command */ + g_free(machine->fdt); machine->fdt = fdt; pm->cpu->vhyp = PPC_VIRTUAL_HYPERVISOR(machine); @@ -448,7 +590,7 @@ enum pegasos2_rtas_tokens { RTAS_SYSTEM_REBOOT = 20, }; -static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, +static target_ulong pegasos2_rtas(PowerPCCPU *cpu, PegasosMachineState *pm, target_ulong args_real) { AddressSpace *as = CPU(cpu)->as; @@ -544,14 +686,14 @@ static target_ulong pegasos2_rtas(PowerPCCPU *cpu, Pegasos2MachineState *pm, } } -static bool pegasos2_cpu_in_nested(PowerPCCPU *cpu) +static bool pegasos_cpu_in_nested(PowerPCCPU *cpu) { return false; } -static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) +static void pegasos_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) { - Pegasos2MachineState *pm = PEGASOS2_MACHINE(vhyp); + PegasosMachineState *pm = PEGASOS_MACHINE(vhyp); CPUPPCState *env = &cpu->env; /* The TCG path should also be holding the BQL at this point */ @@ -560,10 +702,10 @@ static void pegasos2_hypercall(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) if (FIELD_EX64(env->msr, MSR, PR)) { qemu_log_mask(LOG_GUEST_ERROR, "Hypercall made with MSR[PR]=1\n"); env->gpr[3] = H_PRIVILEGE; - } else if (env->gpr[3] == KVMPPC_H_RTAS) { + } else if (env->gpr[3] == KVMPPC_H_RTAS && pm->type == PEGASOS2) { env->gpr[3] = pegasos2_rtas(cpu, pm, env->gpr[4]); } else if (env->gpr[3] == KVMPPC_H_VOF_CLIENT) { - int ret = vof_client_call(MACHINE(pm), pm->vof, pm->fdt_blob, + int ret = vof_client_call(MACHINE(pm), pm->vof, MACHINE(pm)->fdt, env->gpr[4]); env->gpr[3] = (ret ? H_PARAMETER : H_SUCCESS); } else { @@ -582,56 +724,88 @@ static target_ulong vhyp_encode_hpt_for_kvm_pr(PPCVirtualHypervisor *vhyp) return POWERPC_CPU(current_cpu)->env.spr[SPR_SDR1]; } -static bool pegasos2_setprop(MachineState *ms, const char *path, - const char *propname, void *val, int vallen) +static bool pegasos_setprop(MachineState *ms, const char *path, + const char *propname, void *val, int vallen) { return true; } -static void pegasos2_machine_class_init(ObjectClass *oc, void *data) +static void pegasos_machine_init(MachineClass *mc) { - MachineClass *mc = MACHINE_CLASS(oc); - PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); - VofMachineIfClass *vmc = VOF_MACHINE_CLASS(oc); + PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(mc); + VofMachineIfClass *vmc = VOF_MACHINE_CLASS(mc); - mc->desc = "Genesi/bPlan Pegasos II"; - mc->init = pegasos2_init; - mc->reset = pegasos2_machine_reset; + mc->init = pegasos_init; + mc->reset = pegasos_machine_reset; mc->block_default_type = IF_IDE; mc->default_boot_order = "cd"; mc->default_display = "std"; - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); - mc->default_ram_id = "pegasos2.ram"; + mc->default_ram_id = "ram"; mc->default_ram_size = 512 * MiB; machine_add_audiodev_property(mc); - vhc->cpu_in_nested = pegasos2_cpu_in_nested; - vhc->hypercall = pegasos2_hypercall; + vhc->cpu_in_nested = pegasos_cpu_in_nested; + vhc->hypercall = pegasos_hypercall; vhc->cpu_exec_enter = vhyp_nop; vhc->cpu_exec_exit = vhyp_nop; vhc->encode_hpt_for_kvm_pr = vhyp_encode_hpt_for_kvm_pr; - vmc->setprop = pegasos2_setprop; + vmc->setprop = pegasos_setprop; } -static const TypeInfo pegasos2_machine_info = { - .name = TYPE_PEGASOS2_MACHINE, - .parent = TYPE_MACHINE, - .class_init = pegasos2_machine_class_init, - .instance_size = sizeof(Pegasos2MachineState), - .interfaces = (InterfaceInfo[]) { - { TYPE_PPC_VIRTUAL_HYPERVISOR }, - { TYPE_VOF_MACHINE_IF }, - { } - }, -}; +static void pegasos1_init(Object *obj) +{ + PegasosMachineState *pm = PEGASOS_MACHINE(obj); + + pm->type = PEGASOS1; + pm->bus_freq_hz = 33000000; +} -static void pegasos2_machine_register_types(void) +static void pegasos1_machine_class_init(ObjectClass *oc, const void *data) { - type_register_static(&pegasos2_machine_info); + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Genesi/bPlan Pegasos I"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750cxe_v3.1b"); } -type_init(pegasos2_machine_register_types) +static void pegasos2_init(Object *obj) +{ + PegasosMachineState *pm = PEGASOS_MACHINE(obj); + + pm->type = PEGASOS2; + pm->bus_freq_hz = 133333333; +} + +static void pegasos2_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Genesi/bPlan Pegasos II"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); +} + +DEFINE_MACHINE_EXTENDED("pegasos", MACHINE, PegasosMachineState, + pegasos_machine_init, true, (const InterfaceInfo[]) { + { TYPE_PPC_VIRTUAL_HYPERVISOR }, + { TYPE_VOF_MACHINE_IF }, { } }) + +static const TypeInfo pegasos_machine_types[] = { + { + .name = MACHINE_TYPE_NAME("pegasos1"), + .parent = TYPE_PEGASOS_MACHINE, + .class_init = pegasos1_machine_class_init, + .instance_init = pegasos1_init, + }, + { + .name = MACHINE_TYPE_NAME("pegasos2"), + .parent = TYPE_PEGASOS_MACHINE, + .class_init = pegasos2_machine_class_init, + .instance_init = pegasos2_init, + }, +}; + +DEFINE_TYPES(pegasos_machine_types) /* FDT creation for passing to firmware */ @@ -654,121 +828,14 @@ static void dt_usb(PCIBus *bus, PCIDevice *d, FDTInfo *fi) qemu_fdt_setprop_string(fi->fdt, fi->path, "device_type", "usb"); } -static void dt_isa(PCIBus *bus, PCIDevice *d, FDTInfo *fi) -{ - GString *name = g_string_sized_new(64); - uint32_t cells[3]; - - qemu_fdt_setprop_cell(fi->fdt, fi->path, "#size-cells", 1); - qemu_fdt_setprop_cell(fi->fdt, fi->path, "#address-cells", 2); - qemu_fdt_setprop_string(fi->fdt, fi->path, "device_type", "isa"); - qemu_fdt_setprop_string(fi->fdt, fi->path, "name", "isa"); - - /* additional devices */ - g_string_printf(name, "%s/lpt@i3bc", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); - cells[0] = cpu_to_be32(7); - cells[1] = 0; - qemu_fdt_setprop(fi->fdt, name->str, "interrupts", - cells, 2 * sizeof(cells[0])); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x3bc); - cells[2] = cpu_to_be32(8); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "lpt"); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "lpt"); - - g_string_printf(name, "%s/fdc@i3f0", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); - cells[0] = cpu_to_be32(6); - cells[1] = 0; - qemu_fdt_setprop(fi->fdt, name->str, "interrupts", - cells, 2 * sizeof(cells[0])); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x3f0); - cells[2] = cpu_to_be32(8); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "fdc"); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "fdc"); - - g_string_printf(name, "%s/timer@i40", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x40); - cells[2] = cpu_to_be32(8); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "timer"); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "timer"); - - g_string_printf(name, "%s/rtc@i70", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - qemu_fdt_setprop_string(fi->fdt, name->str, "compatible", "ds1385-rtc"); - qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); - cells[0] = cpu_to_be32(8); - cells[1] = 0; - qemu_fdt_setprop(fi->fdt, name->str, "interrupts", - cells, 2 * sizeof(cells[0])); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x70); - cells[2] = cpu_to_be32(2); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "rtc"); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "rtc"); - - g_string_printf(name, "%s/keyboard@i60", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - cells[0] = cpu_to_be32(1); - cells[1] = 0; - qemu_fdt_setprop(fi->fdt, name->str, "interrupts", - cells, 2 * sizeof(cells[0])); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x60); - cells[2] = cpu_to_be32(5); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "keyboard"); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "keyboard"); - - g_string_printf(name, "%s/8042@i60", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - qemu_fdt_setprop_cell(fi->fdt, name->str, "#interrupt-cells", 2); - qemu_fdt_setprop_cell(fi->fdt, name->str, "#size-cells", 0); - qemu_fdt_setprop_cell(fi->fdt, name->str, "#address-cells", 1); - qemu_fdt_setprop_string(fi->fdt, name->str, "interrupt-controller", ""); - qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x60); - cells[2] = cpu_to_be32(5); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", ""); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "8042"); - - g_string_printf(name, "%s/serial@i2f8", fi->path); - qemu_fdt_add_subnode(fi->fdt, name->str); - qemu_fdt_setprop_cell(fi->fdt, name->str, "clock-frequency", 0); - cells[0] = cpu_to_be32(3); - cells[1] = 0; - qemu_fdt_setprop(fi->fdt, name->str, "interrupts", - cells, 2 * sizeof(cells[0])); - cells[0] = cpu_to_be32(1); - cells[1] = cpu_to_be32(0x2f8); - cells[2] = cpu_to_be32(8); - qemu_fdt_setprop(fi->fdt, name->str, "reg", cells, 3 * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, name->str, "device_type", "serial"); - qemu_fdt_setprop_string(fi->fdt, name->str, "name", "serial"); - - g_string_free(name, TRUE); -} - static struct { const char *id; const char *name; void (*dtf)(PCIBus *bus, PCIDevice *d, FDTInfo *fi); } device_map[] = { + { "pci10cc,660", "host", NULL }, + { "pci10cc,661", "host", NULL }, { "pci11ab,6460", "host", NULL }, - { "pci1106,8231", "isa", dt_isa }, { "pci1106,571", "ide", dt_ide }, { "pci1106,3044", "firewire", NULL }, { "pci1106,3038", "usb", dt_usb }, @@ -780,7 +847,7 @@ static struct { static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) { FDTInfo *fi = opaque; - GString *node = g_string_new(NULL); + GString *node; uint32_t cells[(PCI_NUM_REGIONS + 1) * 5]; int i, j; const char *name = NULL; @@ -788,7 +855,10 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) pci_get_word(&d->config[PCI_VENDOR_ID]), pci_get_word(&d->config[PCI_DEVICE_ID])); - if (pci_get_word(&d->config[PCI_CLASS_DEVICE]) == + if (!strcmp(pn, "pci1106,8231")) { + return; /* ISA bridge and devices are included in dtb */ + } + if (pci_get_word(&d->config[PCI_CLASS_DEVICE]) == PCI_CLASS_NETWORK_ETHERNET) { name = "ethernet"; } else if (pci_get_word(&d->config[PCI_CLASS_DEVICE]) >> 8 == @@ -801,6 +871,7 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) break; } } + node = g_string_new(NULL); g_string_printf(node, "%s/%s@%x", fi->path, (name ?: pn), PCI_SLOT(d->devfn)); if (PCI_FUNC(d->devfn)) { @@ -846,12 +917,11 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) j += 5; } qemu_fdt_setprop(fi->fdt, node->str, "reg", cells, j * sizeof(cells[0])); - qemu_fdt_setprop_string(fi->fdt, node->str, "name", name ?: pn); if (pci_get_byte(&d->config[PCI_INTERRUPT_PIN])) { qemu_fdt_setprop_cell(fi->fdt, node->str, "interrupts", pci_get_byte(&d->config[PCI_INTERRUPT_PIN])); } - /* Pegasos2 firmware has subsystem-id amd subsystem-vendor-id swapped */ + /* Pegasos firmware has subsystem-id and subsystem-vendor-id swapped */ qemu_fdt_setprop_cell(fi->fdt, node->str, "subsystem-vendor-id", pci_get_word(&d->config[PCI_SUBSYSTEM_ID])); qemu_fdt_setprop_cell(fi->fdt, node->str, "subsystem-id", @@ -867,136 +937,9 @@ static void add_pci_device(PCIBus *bus, PCIDevice *d, void *opaque) g_string_free(node, TRUE); } -static void *build_fdt(MachineState *machine, int *fdt_size) +static void add_cpu_info(void *fdt, PowerPCCPU *cpu, int bus_freq) { - Pegasos2MachineState *pm = PEGASOS2_MACHINE(machine); - PowerPCCPU *cpu = pm->cpu; - PCIBus *pci_bus; - FDTInfo fi; - uint32_t cells[16]; - void *fdt = create_device_tree(fdt_size); - - fi.fdt = fdt; - - /* root node */ - qemu_fdt_setprop_string(fdt, "/", "CODEGEN,description", - "Pegasos CHRP PowerPC System"); - qemu_fdt_setprop_string(fdt, "/", "CODEGEN,board", "Pegasos2"); - qemu_fdt_setprop_string(fdt, "/", "CODEGEN,vendor", "bplan GmbH"); - qemu_fdt_setprop_string(fdt, "/", "revision", "2B"); - qemu_fdt_setprop_string(fdt, "/", "model", "Pegasos2"); - qemu_fdt_setprop_string(fdt, "/", "device_type", "chrp"); - qemu_fdt_setprop_cell(fdt, "/", "#address-cells", 1); - qemu_fdt_setprop_string(fdt, "/", "name", "bplan,Pegasos2"); - - /* pci@c0000000 */ - qemu_fdt_add_subnode(fdt, "/pci@c0000000"); - cells[0] = 0; - cells[1] = 0; - qemu_fdt_setprop(fdt, "/pci@c0000000", "bus-range", - cells, 2 * sizeof(cells[0])); - qemu_fdt_setprop_cell(fdt, "/pci@c0000000", "pci-bridge-number", 1); - cells[0] = cpu_to_be32(PCI0_MEM_BASE); - cells[1] = cpu_to_be32(PCI0_MEM_SIZE); - qemu_fdt_setprop(fdt, "/pci@c0000000", "reg", cells, 2 * sizeof(cells[0])); - cells[0] = cpu_to_be32(0x01000000); - cells[1] = 0; - cells[2] = 0; - cells[3] = cpu_to_be32(PCI0_IO_BASE); - cells[4] = 0; - cells[5] = cpu_to_be32(PCI0_IO_SIZE); - cells[6] = cpu_to_be32(0x02000000); - cells[7] = 0; - cells[8] = cpu_to_be32(PCI0_MEM_BASE); - cells[9] = cpu_to_be32(PCI0_MEM_BASE); - cells[10] = 0; - cells[11] = cpu_to_be32(PCI0_MEM_SIZE); - qemu_fdt_setprop(fdt, "/pci@c0000000", "ranges", - cells, 12 * sizeof(cells[0])); - qemu_fdt_setprop_cell(fdt, "/pci@c0000000", "#size-cells", 2); - qemu_fdt_setprop_cell(fdt, "/pci@c0000000", "#address-cells", 3); - qemu_fdt_setprop_string(fdt, "/pci@c0000000", "device_type", "pci"); - qemu_fdt_setprop_string(fdt, "/pci@c0000000", "name", "pci"); - - fi.path = "/pci@c0000000"; - pci_bus = mv64361_get_pci_bus(pm->mv, 0); - pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); - - /* pci@80000000 */ - qemu_fdt_add_subnode(fdt, "/pci@80000000"); - cells[0] = 0; - cells[1] = 0; - qemu_fdt_setprop(fdt, "/pci@80000000", "bus-range", - cells, 2 * sizeof(cells[0])); - qemu_fdt_setprop_cell(fdt, "/pci@80000000", "pci-bridge-number", 0); - cells[0] = cpu_to_be32(PCI1_MEM_BASE); - cells[1] = cpu_to_be32(PCI1_MEM_SIZE); - qemu_fdt_setprop(fdt, "/pci@80000000", "reg", cells, 2 * sizeof(cells[0])); - qemu_fdt_setprop_cell(fdt, "/pci@80000000", "8259-interrupt-acknowledge", - 0xf1000cb4); - cells[0] = cpu_to_be32(0x01000000); - cells[1] = 0; - cells[2] = 0; - cells[3] = cpu_to_be32(PCI1_IO_BASE); - cells[4] = 0; - cells[5] = cpu_to_be32(PCI1_IO_SIZE); - cells[6] = cpu_to_be32(0x02000000); - cells[7] = 0; - cells[8] = cpu_to_be32(PCI1_MEM_BASE); - cells[9] = cpu_to_be32(PCI1_MEM_BASE); - cells[10] = 0; - cells[11] = cpu_to_be32(PCI1_MEM_SIZE); - qemu_fdt_setprop(fdt, "/pci@80000000", "ranges", - cells, 12 * sizeof(cells[0])); - qemu_fdt_setprop_cell(fdt, "/pci@80000000", "#size-cells", 2); - qemu_fdt_setprop_cell(fdt, "/pci@80000000", "#address-cells", 3); - qemu_fdt_setprop_string(fdt, "/pci@80000000", "device_type", "pci"); - qemu_fdt_setprop_string(fdt, "/pci@80000000", "name", "pci"); - - fi.path = "/pci@80000000"; - pci_bus = mv64361_get_pci_bus(pm->mv, 1); - pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); - - qemu_fdt_add_subnode(fdt, "/failsafe"); - qemu_fdt_setprop_string(fdt, "/failsafe", "device_type", "serial"); - qemu_fdt_setprop_string(fdt, "/failsafe", "name", "failsafe"); - - qemu_fdt_add_subnode(fdt, "/rtas"); - qemu_fdt_setprop_cell(fdt, "/rtas", "system-reboot", RTAS_SYSTEM_REBOOT); - qemu_fdt_setprop_cell(fdt, "/rtas", "hibernate", RTAS_HIBERNATE); - qemu_fdt_setprop_cell(fdt, "/rtas", "suspend", RTAS_SUSPEND); - qemu_fdt_setprop_cell(fdt, "/rtas", "power-off", RTAS_POWER_OFF); - qemu_fdt_setprop_cell(fdt, "/rtas", "set-indicator", RTAS_SET_INDICATOR); - qemu_fdt_setprop_cell(fdt, "/rtas", "display-character", - RTAS_DISPLAY_CHARACTER); - qemu_fdt_setprop_cell(fdt, "/rtas", "write-pci-config", - RTAS_WRITE_PCI_CONFIG); - qemu_fdt_setprop_cell(fdt, "/rtas", "read-pci-config", - RTAS_READ_PCI_CONFIG); - /* Pegasos2 firmware misspells check-exception and guests use that */ - qemu_fdt_setprop_cell(fdt, "/rtas", "check-execption", - RTAS_CHECK_EXCEPTION); - qemu_fdt_setprop_cell(fdt, "/rtas", "event-scan", RTAS_EVENT_SCAN); - qemu_fdt_setprop_cell(fdt, "/rtas", "set-time-of-day", - RTAS_SET_TIME_OF_DAY); - qemu_fdt_setprop_cell(fdt, "/rtas", "get-time-of-day", - RTAS_GET_TIME_OF_DAY); - qemu_fdt_setprop_cell(fdt, "/rtas", "nvram-store", RTAS_NVRAM_STORE); - qemu_fdt_setprop_cell(fdt, "/rtas", "nvram-fetch", RTAS_NVRAM_FETCH); - qemu_fdt_setprop_cell(fdt, "/rtas", "restart-rtas", RTAS_RESTART_RTAS); - qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-error-log-max", 0); - qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-event-scan-rate", 0); - qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-display-device", 0); - qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", 20); - qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-version", 1); - qemu_fdt_setprop_string(fdt, "/rtas", "name", "rtas"); - - /* cpus */ - qemu_fdt_add_subnode(fdt, "/cpus"); - qemu_fdt_setprop_cell(fdt, "/cpus", "#cpus", 1); - qemu_fdt_setprop_cell(fdt, "/cpus", "#address-cells", 1); - qemu_fdt_setprop_cell(fdt, "/cpus", "#size-cells", 0); - qemu_fdt_setprop_string(fdt, "/cpus", "name", "cpus"); + uint32_t cells[2]; /* FIXME Get CPU name from CPU object */ const char *cp = "/cpus/PowerPC,G4"; @@ -1041,36 +984,72 @@ static void *build_fdt(MachineState *machine, int *fdt_size) qemu_fdt_setprop_cell(fdt, cp, "reservation-granule-size", 4); qemu_fdt_setprop_cell(fdt, cp, "timebase-frequency", cpu->env.tb_env->tb_freq); - qemu_fdt_setprop_cell(fdt, cp, "bus-frequency", BUS_FREQ_HZ); - qemu_fdt_setprop_cell(fdt, cp, "clock-frequency", BUS_FREQ_HZ * 7.5); + qemu_fdt_setprop_cell(fdt, cp, "bus-frequency", bus_freq); + qemu_fdt_setprop_cell(fdt, cp, "clock-frequency", bus_freq * 7.5); qemu_fdt_setprop_cell(fdt, cp, "cpu-version", cpu->env.spr[SPR_PVR]); cells[0] = 0; cells[1] = 0; qemu_fdt_setprop(fdt, cp, "reg", cells, 2 * sizeof(cells[0])); qemu_fdt_setprop_string(fdt, cp, "device_type", "cpu"); - qemu_fdt_setprop_string(fdt, cp, "name", strrchr(cp, '/') + 1); +} - /* memory */ - qemu_fdt_add_subnode(fdt, "/memory@0"); - cells[0] = 0; - cells[1] = cpu_to_be32(machine->ram_size); - qemu_fdt_setprop(fdt, "/memory@0", "reg", cells, 2 * sizeof(cells[0])); - qemu_fdt_setprop_string(fdt, "/memory@0", "device_type", "memory"); - qemu_fdt_setprop_string(fdt, "/memory@0", "name", "memory"); +static void *load_dtb(const char *filename, int *fdt_size) +{ + void *fdt; + g_autofree char *name = qemu_find_file(QEMU_FILE_TYPE_DTB, filename); - qemu_fdt_add_subnode(fdt, "/chosen"); - if (pm->initrd_addr && pm->initrd_size) { - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", - pm->initrd_addr + pm->initrd_size); - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", - pm->initrd_addr); + if (!name) { + error_report("Could not find dtb file '%s'", filename); + return NULL; } - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", - machine->kernel_cmdline ?: ""); - qemu_fdt_setprop_string(fdt, "/chosen", "name", "chosen"); + fdt = load_device_tree(name, fdt_size); + if (!fdt) { + error_report("Could not load dtb file '%s'", name); + } + return fdt; +} + +static void *pegasos1_build_fdt(PegasosMachineState *pm, int *fdt_size) +{ + FDTInfo fi; + PCIBus *pci_bus; + void *fdt = load_dtb("pegasos1.dtb", fdt_size); + + if (!fdt) { + return NULL; + } + qemu_fdt_setprop_string(fdt, "/", "name", "bplan,Pegasos"); - qemu_fdt_add_subnode(fdt, "/openprom"); - qemu_fdt_setprop_string(fdt, "/openprom", "model", "Pegasos2,1.1"); + add_cpu_info(fdt, pm->cpu, pm->bus_freq_hz); + + fi.fdt = fdt; + fi.path = "/pci@80000000"; + pci_bus = PCI_BUS(qdev_get_child_bus(pm->nb, "pci.0")); + pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); + + return fdt; +} + +static void *pegasos2_build_fdt(PegasosMachineState *pm, int *fdt_size) +{ + FDTInfo fi; + PCIBus *pci_bus; + void *fdt = load_dtb("pegasos2.dtb", fdt_size); + + if (!fdt) { + return NULL; + } + qemu_fdt_setprop_string(fdt, "/", "name", "bplan,Pegasos2"); + + add_cpu_info(fdt, pm->cpu, pm->bus_freq_hz); + + fi.fdt = fdt; + fi.path = "/pci@c0000000"; + pci_bus = mv64361_get_pci_bus(pm->nb, 0); + pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); + fi.path = "/pci@80000000"; + pci_bus = mv64361_get_pci_bus(pm->nb, 1); + pci_for_each_device_reverse(pci_bus, 0, add_pci_device, &fi); return fdt; } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 63f2232..895132d 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "qemu/log.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" @@ -490,6 +491,37 @@ static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) pnv_dt_lpc(chip, fdt, 0, PNV10_LPCM_BASE(chip), PNV10_LPCM_SIZE); } +static void pnv_chip_power11_dt_populate(PnvChip *chip, void *fdt) +{ + static const char compat[] = "ibm,power11-xscom\0ibm,xscom"; + int i; + + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV11_XSCOM_BASE(chip)), + cpu_to_be64(PNV11_XSCOM_SIZE), + compat, sizeof(compat)); + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = chip->cores[i]; + int offset; + + offset = pnv_dt_core(chip, pnv_core, fdt); + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features_31, sizeof(pa_features_31)))); + + if (pnv_core->big_core) { + i++; /* Big-core groups two QEMU cores */ + } + } + + if (chip->ram_size) { + pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); + } + + pnv_dt_lpc(chip, fdt, 0, PNV11_LPCM_BASE(chip), PNV11_LPCM_SIZE); +} + static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off) { uint32_t io_base = d->ioport_id; @@ -822,6 +854,26 @@ static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) return pnv_lpc_isa_create(&chip10->lpc, false, errp); } +static ISABus *pnv_chip_power11_isa_create(PnvChip *chip, Error **errp) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + qemu_irq irq; + + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPCHC); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "LPCHC", 0, irq); + + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ0); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 0, irq); + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ1); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 1, irq); + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ2); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 2, irq); + irq = qdev_get_gpio_in(DEVICE(&chip11->psi), PSIHB9_IRQ_LPC_SIRQ3); + qdev_connect_gpio_out_named(DEVICE(&chip11->lpc), "SERIRQ", 3, irq); + + return pnv_lpc_isa_create(&chip11->lpc, false, errp); +} + static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) { return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); @@ -885,6 +937,12 @@ static uint64_t pnv_chip_power10_xscom_core_base(PnvChip *chip, return PNV10_XSCOM_EC_BASE(core_id); } +static uint64_t pnv_chip_power11_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV11_XSCOM_EC_BASE(core_id); +} + static bool pnv_match_cpu(const char *default_type, const char *cpu_type) { PowerPCCPUClass *ppc_default = @@ -914,6 +972,16 @@ static void pnv_chip_power10_pic_print_info(PnvChip *chip, GString *buf) pnv_chip_power9_pic_print_info_child, buf); } +static void pnv_chip_power11_pic_print_info(PnvChip *chip, GString *buf) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + + pnv_xive2_pic_print_info(&chip11->xive, buf); + pnv_psi_pic_print_info(&chip11->psi, buf); + object_child_foreach_recursive(OBJECT(chip), + pnv_chip_power9_pic_print_info_child, buf); +} + /* Always give the first 1GB to chip 0 else we won't boot */ static uint64_t pnv_chip_get_ram_size(PnvMachineState *pnv, int chip_id) { @@ -941,7 +1009,6 @@ static void pnv_init(MachineState *machine) PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine); int max_smt_threads = pmc->max_smt_threads; char *fw_filename; - long fw_size; uint64_t chip_ram_start = 0; int i; char *chip_typename; @@ -1000,36 +1067,22 @@ static void pnv_init(MachineState *machine) exit(1); } - fw_size = load_image_targphys(fw_filename, pnv->fw_load_addr, FW_MAX_SIZE); - if (fw_size < 0) { - error_report("Could not load OPAL firmware '%s'", fw_filename); - exit(1); - } + load_image_targphys(fw_filename, pnv->fw_load_addr, FW_MAX_SIZE, + &error_fatal); g_free(fw_filename); /* load kernel */ if (machine->kernel_filename) { - long kernel_size; - - kernel_size = load_image_targphys(machine->kernel_filename, - KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE); - if (kernel_size < 0) { - error_report("Could not load kernel '%s'", - machine->kernel_filename); - exit(1); - } + load_image_targphys(machine->kernel_filename, + KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE, &error_fatal); } /* load initrd */ if (machine->initrd_filename) { pnv->initrd_base = INITRD_LOAD_ADDR; pnv->initrd_size = load_image_targphys(machine->initrd_filename, - pnv->initrd_base, INITRD_MAX_SIZE); - if (pnv->initrd_size < 0) { - error_report("Could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } + pnv->initrd_base, + INITRD_MAX_SIZE, &error_fatal); } /* load dtb if passed */ @@ -1421,6 +1474,60 @@ static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); } +static void *pnv_chip_power10_intc_get(PnvChip *chip) +{ + return &PNV10_CHIP(chip)->xive; +} + +static void pnv_chip_power11_intc_create(PnvChip *chip, PowerPCCPU *cpu, + Error **errp) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + Error *local_err = NULL; + Object *obj; + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + /* + * The core creates its interrupt presenter but the XIVE2 interrupt + * controller object is initialized afterwards. Hopefully, it's + * only used at runtime. + */ + obj = xive_tctx_create(OBJECT(cpu), XIVE_PRESENTER(&chip11->xive), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pnv_cpu->intc = obj; +} + +static void pnv_chip_power11_intc_reset(PnvChip *chip, PowerPCCPU *cpu) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + xive_tctx_reset(XIVE_TCTX(pnv_cpu->intc)); +} + +static void pnv_chip_power11_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + xive_tctx_destroy(XIVE_TCTX(pnv_cpu->intc)); + pnv_cpu->intc = NULL; +} + +static void pnv_chip_power11_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + GString *buf) +{ + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), buf); +} + +static void *pnv_chip_power11_intc_get(PnvChip *chip) +{ + return &PNV11_CHIP(chip)->xive; +} + /* * Allowed core identifiers on a POWER8 Processor Chip : * @@ -1451,6 +1558,8 @@ static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, #define POWER10_CORE_MASK (0xffffffffffffffull) +#define POWER11_CORE_MASK (0xffffffffffffffull) + static void pnv_chip_power8_instance_init(Object *obj) { Pnv8Chip *chip8 = PNV8_CHIP(obj); @@ -1618,7 +1727,7 @@ static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr) return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); } -static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8e_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1642,7 +1751,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1666,7 +1775,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8nvl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1794,12 +1903,83 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) } } +static uint64_t pnv_handle_sprd_load(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + uint64_t sprc = env->spr[SPR_POWER_SPRC]; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + return pc->scratch[(sprc >> 3) & 0x7]; + + case 0x1e0: /* core thread state */ + if (env->excp_model == POWERPC_EXCP_POWER9) { + /* + * Only implement for POWER9 because skiboot uses it to check + * big-core mode. Other bits are unimplemented so we would + * prefer to get unimplemented message on POWER10 if it were + * used anywhere. + */ + if (pc->big_core) { + return PPC_BIT(63); + } else { + return 0; + } + } + /* fallthru */ + + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } + return 0; +} + +static void pnv_handle_sprd_store(CPUPPCState *env, uint64_t val) +{ + PowerPCCPU *cpu = env_archcpu(env); + uint64_t sprc = env->spr[SPR_POWER_SPRC]; + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + int nr; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + /* + * Log stores to SCRATCH, because some firmware uses these for + * debugging and logging, but they would normally be read by the BMC, + * which is not implemented in QEMU yet. This gives a way to get at the + * information. Could also dump these upon checkstop. + */ + nr = (sprc >> 3) & 0x7; + pc->scratch[nr] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } +} + static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); Pnv9Chip *chip9 = PNV9_CHIP(dev); PnvChip *chip = PNV_CHIP(dev); Pnv9Psi *psi9 = &chip9->psi; + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; Error *local_err = NULL; int i; @@ -1827,6 +2007,12 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) return; } + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + /* XIVE interrupt controller (POWER9) */ object_property_set_int(OBJECT(&chip9->xive), "ic-bar", PNV9_XIVE_IC_BASE(chip), &error_fatal); @@ -1954,7 +2140,7 @@ static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) return addr >> 3; } -static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -2078,6 +2264,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); PnvChip *chip = PNV_CHIP(dev); Pnv10Chip *chip10 = PNV10_CHIP(dev); + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; Error *local_err = NULL; int i; @@ -2105,6 +2293,12 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + /* XIVE2 interrupt controller (POWER10) */ object_property_set_int(OBJECT(&chip10->xive), "ic-bar", PNV10_XIVE2_IC_BASE(chip), &error_fatal); @@ -2264,6 +2458,302 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) } } +static void pnv_chip_power11_instance_init(Object *obj) +{ + PnvChip *chip = PNV_CHIP(obj); + Pnv11Chip *chip11 = PNV11_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(obj); + int i; + + object_initialize_child(obj, "adu", &chip11->adu, TYPE_PNV_ADU); + + /* + * Use Power10 device models for PSI/LPC/OCC/SBE/HOMER as corresponding + * device models for Power11 are same + */ + object_initialize_child(obj, "psi", &chip11->psi, TYPE_PNV10_PSI); + object_initialize_child(obj, "lpc", &chip11->lpc, TYPE_PNV10_LPC); + object_initialize_child(obj, "occ", &chip11->occ, TYPE_PNV10_OCC); + object_initialize_child(obj, "sbe", &chip11->sbe, TYPE_PNV10_SBE); + object_initialize_child(obj, "homer", &chip11->homer, TYPE_PNV10_HOMER); + + object_initialize_child(obj, "xive", &chip11->xive, TYPE_PNV_XIVE2); + object_property_add_alias(obj, "xive-fabric", OBJECT(&chip11->xive), + "xive-fabric"); + object_initialize_child(obj, "chiptod", &chip11->chiptod, + TYPE_PNV11_CHIPTOD); + object_initialize_child(obj, "n1-chiplet", &chip11->n1_chiplet, + TYPE_PNV_N1_CHIPLET); + + chip->num_pecs = pcc->num_pecs; + + for (i = 0; i < chip->num_pecs; i++) { + object_initialize_child(obj, "pec[*]", &chip11->pecs[i], + TYPE_PNV_PHB5_PEC); + } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip11->i2c[i], TYPE_PNV_I2C); + } + + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_initialize_child(obj, "pib_spic[*]", &chip11->pib_spic[i], + TYPE_PNV_SPI); + } +} + +static void pnv_chip_power11_quad_realize(Pnv11Chip *chip11, Error **errp) +{ + PnvChip *chip = PNV_CHIP(chip11); + int i; + + chip11->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4); + chip11->quads = g_new0(PnvQuad, chip11->nr_quads); + + for (i = 0; i < chip11->nr_quads; i++) { + PnvQuad *eq = &chip11->quads[i]; + + pnv_chip_quad_realize_one(chip, eq, chip->cores[i * 4], + PNV_QUAD_TYPE_NAME("power11")); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_EQ_BASE(eq->quad_id), + &eq->xscom_regs); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_QME_BASE(eq->quad_id), + &eq->xscom_qme_regs); + } +} + +static void pnv_chip_power11_phb_realize(PnvChip *chip, Error **errp) +{ + Pnv11Chip *chip11 = PNV11_CHIP(chip); + int i; + + for (i = 0; i < chip->num_pecs; i++) { + PnvPhb4PecState *pec = &chip11->pecs[i]; + PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); + uint32_t pec_cplt_base; + uint32_t pec_nest_base; + uint32_t pec_pci_base; + + object_property_set_int(OBJECT(pec), "index", i, &error_fatal); + object_property_set_int(OBJECT(pec), "chip-id", chip->chip_id, + &error_fatal); + object_property_set_link(OBJECT(pec), "chip", OBJECT(chip), + &error_fatal); + if (!qdev_realize(DEVICE(pec), NULL, errp)) { + return; + } + + pec_cplt_base = pecc->xscom_cplt_base(pec); + pec_nest_base = pecc->xscom_nest_base(pec); + pec_pci_base = pecc->xscom_pci_base(pec); + + pnv_xscom_add_subregion(chip, pec_cplt_base, + &pec->nest_pervasive.xscom_ctrl_regs_mr); + pnv_xscom_add_subregion(chip, pec_nest_base, &pec->nest_regs_mr); + pnv_xscom_add_subregion(chip, pec_pci_base, &pec->pci_regs_mr); + } +} + +static void pnv_chip_power11_realize(DeviceState *dev, Error **errp) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + PnvChip *chip = PNV_CHIP(dev); + Pnv11Chip *chip11 = PNV11_CHIP(dev); + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; + Error *local_err = NULL; + int i; + + /* XSCOM bridge is first */ + pnv_xscom_init(chip, PNV11_XSCOM_SIZE, PNV11_XSCOM_BASE(chip)); + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + + /* ADU */ + object_property_set_link(OBJECT(&chip11->adu), "lpc", OBJECT(&chip11->lpc), + &error_abort); + if (!qdev_realize(DEVICE(&chip11->adu), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_ADU_BASE, + &chip11->adu.xscom_regs); + + pnv_chip_power11_quad_realize(chip11, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* XIVE2 interrupt controller */ + object_property_set_int(OBJECT(&chip11->xive), "ic-bar", + PNV11_XIVE2_IC_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "esb-bar", + PNV11_XIVE2_ESB_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "end-bar", + PNV11_XIVE2_END_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "nvpg-bar", + PNV11_XIVE2_NVPG_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "nvc-bar", + PNV11_XIVE2_NVC_BASE(chip), &error_fatal); + object_property_set_int(OBJECT(&chip11->xive), "tm-bar", + PNV11_XIVE2_TM_BASE(chip), &error_fatal); + object_property_set_link(OBJECT(&chip11->xive), "chip", OBJECT(chip), + &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&chip11->xive), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_XIVE2_BASE, + &chip11->xive.xscom_regs); + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip11->psi), "bar", + PNV11_PSIHB_BASE(chip), &error_fatal); + /* PSI can be configured to use 64k ESB pages on Power11 */ + object_property_set_int(OBJECT(&chip11->psi), "shift", XIVE_ESB_64K, + &error_fatal); + if (!qdev_realize(DEVICE(&chip11->psi), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_PSIHB_BASE, + &PNV_PSI(&chip11->psi)->xscom_regs); + + /* LPC */ + if (!qdev_realize(DEVICE(&chip11->lpc), NULL, errp)) { + return; + } + memory_region_add_subregion(get_system_memory(), PNV11_LPCM_BASE(chip), + &chip11->lpc.xscom_regs); + + chip->fw_mr = &chip11->lpc.isa_fw; + chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", + (uint64_t) PNV11_LPCM_BASE(chip)); + + /* ChipTOD */ + object_property_set_bool(OBJECT(&chip11->chiptod), "primary", + chip->chip_id == 0, &error_abort); + object_property_set_bool(OBJECT(&chip11->chiptod), "secondary", + chip->chip_id == 1, &error_abort); + object_property_set_link(OBJECT(&chip11->chiptod), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip11->chiptod), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_CHIPTOD_BASE, + &chip11->chiptod.xscom_regs); + + /* HOMER (must be created before OCC) */ + object_property_set_link(OBJECT(&chip11->homer), "chip", OBJECT(chip), + &error_abort); + if (!qdev_realize(DEVICE(&chip11->homer), NULL, errp)) { + return; + } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV11_XSCOM_PBA_BASE, + &chip11->homer.pba_regs); + /* Homer RAM region */ + memory_region_add_subregion(get_system_memory(), chip11->homer.base, + &chip11->homer.mem); + + /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip11->occ), "homer", + OBJECT(&chip11->homer), &error_abort); + if (!qdev_realize(DEVICE(&chip11->occ), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_OCC_BASE, + &chip11->occ.xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip11->occ), 0, qdev_get_gpio_in( + DEVICE(&chip11->psi), PSIHB9_IRQ_OCC)); + + /* OCC SRAM model */ + memory_region_add_subregion(get_system_memory(), + PNV11_OCC_SENSOR_BASE(chip), + &chip11->occ.sram_regs); + + /* SBE */ + if (!qdev_realize(DEVICE(&chip11->sbe), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_SBE_CTRL_BASE, + &chip11->sbe.xscom_ctrl_regs); + pnv_xscom_add_subregion(chip, PNV11_XSCOM_SBE_MBOX_BASE, + &chip11->sbe.xscom_mbox_regs); + qdev_connect_gpio_out(DEVICE(&chip11->sbe), 0, qdev_get_gpio_in( + DEVICE(&chip11->psi), PSIHB9_IRQ_PSU)); + + /* N1 chiplet */ + if (!qdev_realize(DEVICE(&chip11->n1_chiplet), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_CHIPLET_CTRL_REGS_BASE, + &chip11->n1_chiplet.nest_pervasive.xscom_ctrl_regs_mr); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_PB_SCOM_EQ_BASE, + &chip11->n1_chiplet.xscom_pb_eq_mr); + + pnv_xscom_add_subregion(chip, PNV11_XSCOM_N1_PB_SCOM_ES_BASE, + &chip11->n1_chiplet.xscom_pb_es_mr); + + /* PHBs */ + pnv_chip_power11_phb_realize(chip, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip11->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_I2CM_BASE + + (chip11->i2c[i].engine - 1) * + PNV11_XSCOM_I2CM_SIZE, + &chip11->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip11->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip11->psi), + PSIHB9_IRQ_SBE_I2C)); + } + /* PIB SPI Controller */ + for (i = 0; i < PNV10_CHIP_MAX_PIB_SPIC; i++) { + object_property_set_int(OBJECT(&chip11->pib_spic[i]), "spic_num", + i, &error_fatal); + /* pib_spic[2] connected to 25csm04 which implements 1 byte transfer */ + object_property_set_int(OBJECT(&chip11->pib_spic[i]), "transfer_len", + (i == 2) ? 1 : 4, &error_fatal); + object_property_set_int(OBJECT(&chip11->pib_spic[i]), "chip-id", + chip->chip_id, &error_fatal); + if (!sysbus_realize(SYS_BUS_DEVICE(OBJECT + (&chip11->pib_spic[i])), errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV11_XSCOM_PIB_SPIC_BASE + + i * PNV11_XSCOM_PIB_SPIC_SIZE, + &chip11->pib_spic[i].xscom_spic_regs); + } +} + static void pnv_rainier_i2c_init(PnvMachineState *pnv) { int i; @@ -2302,7 +2792,7 @@ static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) return addr >> 3; } -static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -2315,6 +2805,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) k->intc_reset = pnv_chip_power10_intc_reset; k->intc_destroy = pnv_chip_power10_intc_destroy; k->intc_print_info = pnv_chip_power10_intc_print_info; + k->intc_get = pnv_chip_power10_intc_get; k->isa_create = pnv_chip_power10_isa_create; k->dt_populate = pnv_chip_power10_dt_populate; k->pic_print_info = pnv_chip_power10_pic_print_info; @@ -2329,6 +2820,40 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) &k->parent_realize); } +static uint32_t pnv_chip_power11_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV11_XSCOM_SIZE - 1); + return addr >> 3; +} + +static void pnv_chip_power11_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; + + k->chip_cfam_id = 0x220da04980000000ull; /* P11 DD2.0 (with NX) */ + k->cores_mask = POWER11_CORE_MASK; + k->get_pir_tir = pnv_get_pir_tir_p10; + k->intc_create = pnv_chip_power11_intc_create; + k->intc_reset = pnv_chip_power11_intc_reset; + k->intc_destroy = pnv_chip_power11_intc_destroy; + k->intc_print_info = pnv_chip_power11_intc_print_info; + k->intc_get = pnv_chip_power11_intc_get; + k->isa_create = pnv_chip_power11_isa_create; + k->dt_populate = pnv_chip_power11_dt_populate; + k->pic_print_info = pnv_chip_power11_pic_print_info; + k->xscom_core_base = pnv_chip_power11_xscom_core_base; + k->xscom_pcba = pnv_chip_power11_xscom_pcba; + dc->desc = "PowerNV Chip Power11"; + k->num_pecs = PNV10_CHIP_MAX_PEC; + k->i2c_num_engines = PNV10_CHIP_MAX_I2C; + k->i2c_ports_per_engine = i2c_ports_per_engine; + + device_class_set_parent_realize(dc, pnv_chip_power11_realize, + &k->parent_realize); +} + static void pnv_chip_core_sanitize(PnvMachineState *pnv, PnvChip *chip, Error **errp) { @@ -2461,7 +2986,7 @@ static const Property pnv_chip_properties[] = { DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), }; -static void pnv_chip_class_init(ObjectClass *klass, void *data) +static void pnv_chip_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2608,65 +3133,88 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) } } -static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, - XiveTCTXMatch *match) +static bool pnv_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) { PnvMachineState *pnv = PNV_MACHINE(xfb); - int total_count = 0; int i; for (i = 0; i < pnv->num_chips; i++) { Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); XivePresenter *xptr = XIVE_PRESENTER(&chip9->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, - cam_ignore, priority, logic_serv, match); + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); + } - if (count < 0) { - return count; - } + return !!match->count; +} + +static bool pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - total_count += count; + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); } - return total_count; + return !!match->count; } -static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, +static int pnv10_xive_broadcast(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, - XiveTCTXMatch *match) + bool crowd, bool cam_ignore, + uint8_t priority) { PnvMachineState *pnv = PNV_MACHINE(xfb); - int total_count = 0; int i; for (i = 0; i < pnv->num_chips; i++) { Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, - cam_ignore, priority, logic_serv, match); + xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority); + } + return 0; +} + +static bool pnv11_xive_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) +{ + PnvMachineState *pnv = PNV_MACHINE(xfb); + int i; - if (count < 0) { - return count; - } + for (i = 0; i < pnv->num_chips; i++) { + Pnv11Chip *chip11 = PNV11_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip11->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - total_count += count; + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); } - return total_count; + return !!match->count; } -static int pnv10_xive_broadcast(XiveFabric *xfb, +static int pnv11_xive_broadcast(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, bool crowd, bool cam_ignore, uint8_t priority) @@ -2675,8 +3223,8 @@ static int pnv10_xive_broadcast(XiveFabric *xfb, int i; for (i = 0; i < pnv->num_chips; i++) { - Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); - XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); + Pnv11Chip *chip11 = PNV11_CHIP(pnv->chips[i]); + XivePresenter *xptr = XIVE_PRESENTER(&chip11->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); xpc->broadcast(xptr, nvt_blk, nvt_idx, crowd, cam_ignore, priority); @@ -2724,7 +3272,7 @@ static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) } } -static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power8_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); @@ -2753,7 +3301,7 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } -static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power9_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); @@ -2792,7 +3340,7 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) "Use 1 LPAR per core mode"); } -static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) +static void pnv_machine_p10_common_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); @@ -2822,7 +3370,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } -static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power10_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -2847,7 +3395,8 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) "Use 1 LPAR per core mode"); } -static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) +static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); @@ -2857,6 +3406,46 @@ static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) pmc->i2c_init = pnv_rainier_i2c_init; } +static void pnv_machine_power11_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); + static const char compat[] = "qemu,powernv11\0ibm,powernv"; + + static GlobalProperty phb_compat[] = { + { TYPE_PNV_PHB, "version", "5" }, + { TYPE_PNV_PHB_ROOT_PORT, "version", "5" }, + }; + + compat_props_add(mc->compat_props, phb_compat, G_N_ELEMENTS(phb_compat)); + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); + pmc->max_smt_threads = 4; + pmc->has_lpar_per_thread = true; + pmc->quirk_tb_big_core = true; + pmc->dt_power_mgt = pnv_dt_power_mgt; + + xfc->match_nvt = pnv11_xive_match_nvt; + xfc->broadcast = pnv11_xive_broadcast; + + mc->desc = "IBM PowerNV (Non-Virtualized) Power11"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power11_v2.0"); + + object_class_property_add_bool(oc, "big-core", + pnv_machine_get_big_core, + pnv_machine_set_big_core); + object_class_property_set_description(oc, "big-core", + "Use big-core (aka fused-core) mode"); + + object_class_property_add_bool(oc, "lpar-per-core", + pnv_machine_get_lpar_per_core, + pnv_machine_set_lpar_per_core); + object_class_property_set_description(oc, "lpar-per-core", + "Use 1 LPAR per core mode"); +} + static void pnv_cpu_do_nmi_on_cpu(CPUState *cs, run_on_cpu_data arg) { CPUPPCState *env = cpu_env(cs); @@ -2912,7 +3501,7 @@ static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void pnv_machine_class_init(ObjectClass *oc, void *data) +static void pnv_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); @@ -2962,8 +3551,24 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) .parent = TYPE_PNV10_CHIP, \ } +#define DEFINE_PNV11_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV11_CHIP, \ + } + static const TypeInfo types[] = { { + .name = MACHINE_TYPE_NAME("powernv11"), + .parent = TYPE_PNV_MACHINE, + .class_init = pnv_machine_power11_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_FABRIC }, + { }, + }, + }, + { .name = MACHINE_TYPE_NAME("powernv10-rainier"), .parent = MACHINE_TYPE_NAME("powernv10"), .class_init = pnv_machine_p10_rainier_class_init, @@ -2972,7 +3577,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv10"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power10_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_FABRIC }, { }, }, @@ -2981,7 +3586,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv9"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_FABRIC }, { }, }, @@ -2990,7 +3595,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv8"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power8_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XICS_FABRIC }, { }, }, @@ -3002,7 +3607,7 @@ static const TypeInfo types[] = { .instance_size = sizeof(PnvMachineState), .class_init = pnv_machine_class_init, .class_size = sizeof(PnvMachineClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { TYPE_NMI }, { }, @@ -3018,6 +3623,17 @@ static const TypeInfo types[] = { }, /* + * P11 chip and variants + */ + { + .name = TYPE_PNV11_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power11_instance_init, + .instance_size = sizeof(Pnv11Chip), + }, + DEFINE_PNV11_CHIP_TYPE(TYPE_PNV_CHIP_POWER11, pnv_chip_power11_class_init), + + /* * P10 chip and variants */ { diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c index d09a167..005fbda 100644 --- a/hw/ppc/pnv_adu.c +++ b/hw/ppc/pnv_adu.c @@ -189,7 +189,7 @@ static const Property pnv_adu_properties[] = { DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), }; -static void pnv_adu_class_init(ObjectClass *klass, void *data) +static void pnv_adu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -204,7 +204,7 @@ static const TypeInfo pnv_adu_type_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvADU), .class_init = pnv_adu_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } }, }; diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index c8987ae..f887a18 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -210,6 +210,22 @@ static void chiptod_power10_broadcast_ttype(PnvChipTOD *sender, } } +static void chiptod_power11_broadcast_ttype(PnvChipTOD *sender, + uint32_t trigger) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv11Chip *chip11 = PNV11_CHIP(pnv->chips[i]); + PnvChipTOD *chiptod = &chip11->chiptod; + + if (chiptod != sender) { + chiptod_receive_ttype(chiptod, trigger); + } + } +} + static PnvCore *pnv_chip_get_core_by_xscom_base(PnvChip *chip, uint32_t xscom_base) { @@ -283,6 +299,12 @@ static PnvCore *chiptod_power10_tx_ttype_target(PnvChipTOD *chiptod, } } +static PnvCore *chiptod_power11_tx_ttype_target(PnvChipTOD *chiptod, + uint64_t val) +{ + return chiptod_power10_tx_ttype_target(chiptod, val); +} + static void pnv_chiptod_xscom_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -456,7 +478,7 @@ static const Property pnv_chiptod_properties[] = { DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) +static void pnv_chiptod_power9_class_init(ObjectClass *klass, const void *data) { PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -478,7 +500,7 @@ static const TypeInfo pnv_chiptod_power9_type_info = { .parent = TYPE_PNV_CHIPTOD, .instance_size = sizeof(PnvChipTOD), .class_init = pnv_chiptod_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -492,7 +514,7 @@ static int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt, return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); } -static void pnv_chiptod_power10_class_init(ObjectClass *klass, void *data) +static void pnv_chiptod_power10_class_init(ObjectClass *klass, const void *data) { PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -514,7 +536,43 @@ static const TypeInfo pnv_chiptod_power10_type_info = { .parent = TYPE_PNV_CHIPTOD, .instance_size = sizeof(PnvChipTOD), .class_init = pnv_chiptod_power10_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static int pnv_chiptod_power11_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power-chiptod\0ibm,power11-chiptod"; + + return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); +} + +static void pnv_chiptod_power11_class_init(ObjectClass *klass, const void *data) +{ + PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + dc->desc = "PowerNV ChipTOD Controller (Power11)"; + device_class_set_props(dc, pnv_chiptod_properties); + + xdc->dt_xscom = pnv_chiptod_power11_dt_xscom; + + pctc->broadcast_ttype = chiptod_power11_broadcast_ttype; + pctc->tx_ttype_target = chiptod_power11_tx_ttype_target; + + pctc->xscom_size = PNV_XSCOM_CHIPTOD_SIZE; +} + +static const TypeInfo pnv_chiptod_power11_type_info = { + .name = TYPE_PNV11_CHIPTOD, + .parent = TYPE_PNV_CHIPTOD, + .instance_size = sizeof(PnvChipTOD), + .class_init = pnv_chiptod_power11_class_init, + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -555,7 +613,7 @@ static void pnv_chiptod_unrealize(DeviceState *dev) qemu_unregister_reset(pnv_chiptod_reset, chiptod); } -static void pnv_chiptod_class_init(ObjectClass *klass, void *data) +static void pnv_chiptod_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -579,6 +637,7 @@ static void pnv_chiptod_register_types(void) type_register_static(&pnv_chiptod_type_info); type_register_static(&pnv_chiptod_power9_type_info); type_register_static(&pnv_chiptod_power10_type_info); + type_register_static(&pnv_chiptod_power11_type_info); } type_init(pnv_chiptod_register_types); diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index a33977d..fb2dfc7 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -449,7 +449,7 @@ static const Property pnv_core_properties[] = { DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_core_power8_class_init(ObjectClass *oc, void *data) +static void pnv_core_power8_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -457,7 +457,7 @@ static void pnv_core_power8_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV_XSCOM_EX_SIZE; } -static void pnv_core_power9_class_init(ObjectClass *oc, void *data) +static void pnv_core_power9_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -465,7 +465,7 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV_XSCOM_EX_SIZE; } -static void pnv_core_power10_class_init(ObjectClass *oc, void *data) +static void pnv_core_power10_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -473,7 +473,12 @@ static void pnv_core_power10_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV10_XSCOM_EC_SIZE; } -static void pnv_core_class_init(ObjectClass *oc, void *data) +static void pnv_core_power11_class_init(ObjectClass *oc, const void *data) +{ + pnv_core_power10_class_init(oc, data); +} + +static void pnv_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -504,6 +509,7 @@ static const TypeInfo pnv_core_infos[] = { DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"), DEFINE_PNV_CORE_TYPE(power9, "power9_v2.2"), DEFINE_PNV_CORE_TYPE(power10, "power10_v2.0"), + DEFINE_PNV_CORE_TYPE(power11, "power11_v2.0"), }; DEFINE_TYPES(pnv_core_infos) @@ -700,7 +706,7 @@ static const Property pnv_quad_properties[] = { DEFINE_PROP_UINT32("quad-id", PnvQuad, quad_id, 0), }; -static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power9_class_init(ObjectClass *oc, const void *data) { PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -711,7 +717,7 @@ static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) pqc->xscom_size = PNV9_XSCOM_EQ_SIZE; } -static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power10_class_init(ObjectClass *oc, const void *data) { PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -725,7 +731,13 @@ static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) pqc->xscom_qme_size = PNV10_XSCOM_QME_SIZE; } -static void pnv_quad_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power11_class_init(ObjectClass *oc, const void *data) +{ + /* Power11 quad is similar to Power10 quad */ + pnv_quad_power10_class_init(oc, data); +} + +static void pnv_quad_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -752,6 +764,11 @@ static const TypeInfo pnv_quad_infos[] = { .name = PNV_QUAD_TYPE_NAME("power10"), .class_init = pnv_quad_power10_class_init, }, + { + .parent = TYPE_PNV_QUAD, + .name = PNV_QUAD_TYPE_NAME("power11"), + .class_init = pnv_quad_power11_class_init, + }, }; DEFINE_TYPES(pnv_quad_infos); diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index 18a53a8..2208ffe 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qapi/error.h" #include "exec/hwaddr.h" -#include "exec/memory.h" +#include "system/memory.h" #include "system/cpus.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" @@ -89,7 +89,7 @@ static hwaddr pnv_homer_power8_get_base(PnvChip *chip) return PNV_HOMER_BASE(chip); } -static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) +static void pnv_homer_power8_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); @@ -156,7 +156,7 @@ static hwaddr pnv_homer_power9_get_base(PnvChip *chip) return PNV9_HOMER_BASE(chip); } -static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) +static void pnv_homer_power9_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); @@ -223,7 +223,7 @@ static hwaddr pnv_homer_power10_get_base(PnvChip *chip) return PNV10_HOMER_BASE(chip); } -static void pnv_homer_power10_class_init(ObjectClass *klass, void *data) +static void pnv_homer_power10_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); @@ -266,7 +266,7 @@ static const Property pnv_homer_properties[] = { DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_homer_class_init(ObjectClass *klass, void *data) +static void pnv_homer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index 8d35f45..60de479 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -549,7 +549,7 @@ static const Property pnv_i2c_properties[] = { DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), }; -static void pnv_i2c_class_init(ObjectClass *klass, void *data) +static void pnv_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -569,7 +569,7 @@ static const TypeInfo pnv_i2c_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvI2C), .class_init = pnv_i2c_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d812dc8..f6beba0 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -714,7 +714,7 @@ static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp) PNV_XSCOM_LPC_SIZE); } -static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -732,7 +732,7 @@ static const TypeInfo pnv_lpc_power8_info = { .name = TYPE_PNV8_LPC, .parent = TYPE_PNV_LPC, .class_init = pnv_lpc_power8_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -760,7 +760,7 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4); } -static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvLpcClass *plc = PNV_LPC_CLASS(klass); @@ -777,7 +777,7 @@ static const TypeInfo pnv_lpc_power9_info = { .class_init = pnv_lpc_power9_class_init, }; -static void pnv_lpc_power10_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -843,7 +843,7 @@ static const Property pnv_lpc_properties[] = { DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), }; -static void pnv_lpc_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_n1_chiplet.c b/hw/ppc/pnv_n1_chiplet.c index 03ff9fb..053f647 100644 --- a/hw/ppc/pnv_n1_chiplet.c +++ b/hw/ppc/pnv_n1_chiplet.c @@ -136,7 +136,7 @@ static void pnv_n1_chiplet_realize(DeviceState *dev, Error **errp) PNV10_XSCOM_N1_PB_SCOM_ES_SIZE); } -static void pnv_n1_chiplet_class_init(ObjectClass *klass, void *data) +static void pnv_n1_chiplet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -159,7 +159,7 @@ static const TypeInfo pnv_n1_chiplet_info = { .instance_init = pnv_n1_chiplet_instance_init, .instance_size = sizeof(PnvN1Chiplet), .class_init = pnv_n1_chiplet_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_nest_pervasive.c b/hw/ppc/pnv_nest_pervasive.c index 780fa69..1b1b14f 100644 --- a/hw/ppc/pnv_nest_pervasive.c +++ b/hw/ppc/pnv_nest_pervasive.c @@ -181,7 +181,7 @@ static void pnv_nest_pervasive_realize(DeviceState *dev, Error **errp) PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE); } -static void pnv_nest_pervasive_class_init(ObjectClass *klass, void *data) +static void pnv_nest_pervasive_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -194,7 +194,7 @@ static const TypeInfo pnv_nest_pervasive_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvNestChipletPervasive), .class_init = pnv_nest_pervasive_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 177c5e5..24b789c 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -150,7 +150,6 @@ static void pnv_occ_common_area_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { /* callback function defined to occ common area write */ - return; } static const MemoryRegionOps pnv_occ_power8_xscom_ops = { @@ -173,7 +172,7 @@ const MemoryRegionOps pnv_occ_sram_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power8_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -247,7 +246,7 @@ static const MemoryRegionOps pnv_occ_power9_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power9_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -267,7 +266,7 @@ static const TypeInfo pnv_occ_power9_type_info = { .class_init = pnv_occ_power9_class_init, }; -static void pnv_occ_power10_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power10_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -336,7 +335,7 @@ static const Property pnv_occ_properties[] = { DEFINE_PROP_LINK("homer", PnvOCC, homer, TYPE_PNV_HOMER, PnvHomer *), }; -static void pnv_occ_class_init(ObjectClass *klass, void *data) +static void pnv_occ_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -790,7 +789,7 @@ static bool occ_opal_process_command(PnvOCC *occ, static bool occ_model_tick(PnvOCC *occ) { - struct occ_dynamic_data dynamic_data; + QEMU_UNINITIALIZED struct occ_dynamic_data dynamic_data; if (!occ_read_dynamic_data(occ, &dynamic_data, NULL)) { /* Can't move OCC state field to safe because we can't map it! */ diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 9db44ca..af7cfd0 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -119,7 +119,7 @@ static const Property pnv_pnor_properties[] = { DEFINE_PROP_DRIVE("drive", PnvPnor, blk), }; -static void pnv_pnor_class_init(ObjectClass *klass, void *data) +static void pnv_pnor_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 1fe11dd..5d947d8 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/irq.h" #include "target/ppc/cpu.h" #include "qemu/log.h" @@ -557,7 +557,7 @@ static const Property pnv_psi_properties[] = { DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), }; -static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -887,7 +887,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) pnv_psi_realize(dev, errp); } -static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -913,13 +913,13 @@ static const TypeInfo pnv_psi_power9_info = { .instance_size = sizeof(Pnv9Psi), .instance_init = pnv_psi_power9_instance_init, .class_init = pnv_psi_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { }, }, }; -static void pnv_psi_power10_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -939,7 +939,7 @@ static const TypeInfo pnv_psi_power10_info = { .class_init = pnv_psi_power10_class_init, }; -static void pnv_psi_class_init(ObjectClass *klass, void *data) +static void pnv_psi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -959,7 +959,7 @@ static const TypeInfo pnv_psi_info = { .class_init = pnv_psi_class_init, .class_size = sizeof(PnvPsiClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_sbe.c b/hw/ppc/pnv_sbe.c index 74cee4e..34dc013 100644 --- a/hw/ppc/pnv_sbe.c +++ b/hw/ppc/pnv_sbe.c @@ -331,7 +331,7 @@ static const MemoryRegionOps pnv_sbe_power9_xscom_mbox_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_sbe_power9_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_power9_class_init(ObjectClass *klass, const void *data) { PnvSBEClass *psc = PNV_SBE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -350,7 +350,7 @@ static const TypeInfo pnv_sbe_power9_type_info = { .class_init = pnv_sbe_power9_class_init, }; -static void pnv_sbe_power10_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_power10_class_init(ObjectClass *klass, const void *data) { PnvSBEClass *psc = PNV_SBE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -386,7 +386,7 @@ static void pnv_sbe_realize(DeviceState *dev, Error **errp) sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe); } -static void pnv_sbe_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 3a80931..3e436c7 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -27,6 +27,7 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" +#include "exec/cpu-interrupt.h" #include "system/cpus.h" #include "qemu/log.h" #include "qemu/main-loop.h" @@ -189,6 +190,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) if (level) { trace_ppc_irq_cpu("stop"); cs->halted = 1; + cpu_exit(cs); } else { trace_ppc_irq_cpu("restart"); cs->halted = 0; @@ -385,6 +387,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) if (level) { trace_ppc_irq_cpu("stop"); cs->halted = 1; + cpu_exit(cs); } else { trace_ppc_irq_cpu("restart"); cs->halted = 0; diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 099fda3..7e739a2 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -64,7 +64,7 @@ static int bamboo_load_device_tree(MachineState *machine, uint32_t tb_freq = 400000000; uint32_t clock_freq = 400000000; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (!filename) { return -1; } @@ -242,13 +242,8 @@ static void bamboo_init(MachineState *machine) /* Load initrd. */ if (initrd_filename) { initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR, - machine->ram_size - RAMDISK_ADDR); - - if (initrd_size < 0) { - error_report("could not load ram disk '%s' at %x", - initrd_filename, RAMDISK_ADDR); - exit(1); - } + machine->ram_size - RAMDISK_ADDR, + &error_fatal); } /* If we're loading a kernel directly, we must load the device tree too. */ diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 9da30a1..89e3fae 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1027,7 +1027,7 @@ static const Property ppc460ex_pcie_props[] = { PowerPCCPU *), }; -static void ppc460ex_pcie_class_init(ObjectClass *klass, void *data) +static void ppc460ex_pcie_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 9ce9777..f36c519 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -236,7 +236,7 @@ static const Property ppc4xx_mal_properties[] = { DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), }; -static void ppc4xx_mal_class_init(ObjectClass *oc, void *data) +static void ppc4xx_mal_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -326,7 +326,7 @@ static void ppc405_plb_realize(DeviceState *dev, Error **errp) ppc4xx_dcr_register(dcr, PLB4A1_ACR, plb, &dcr_read_plb, &dcr_write_plb); } -static void ppc405_plb_class_init(ObjectClass *oc, void *data) +static void ppc405_plb_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -512,7 +512,7 @@ static void ppc405_ebc_realize(DeviceState *dev, Error **errp) ppc4xx_dcr_register(dcr, EBC0_CFGDATA, ebc, &dcr_read_ebc, &dcr_write_ebc); } -static void ppc405_ebc_class_init(ObjectClass *oc, void *data) +static void ppc405_ebc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -543,7 +543,7 @@ static const Property ppc4xx_dcr_properties[] = { PowerPCCPU *), }; -static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data) +static void ppc4xx_dcr_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index 562bff8..5927698 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -34,7 +34,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" /* get_system_memory() */ +#include "system/address-spaces.h" /* get_system_memory() */ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/ppc/ppc4xx.h" @@ -431,7 +431,7 @@ static const Property ppc4xx_sdram_ddr_props[] = { DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), }; -static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data) +static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -715,7 +715,7 @@ static const Property ppc4xx_sdram_ddr2_props[] = { DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), }; -static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data) +static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 925e670b..13403a5 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "qemu/timer.h" #include "system/reset.h" @@ -351,7 +352,12 @@ void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags) booke_timer = g_new0(booke_timer_t, 1); cpu->env.tb_env = tb_env; - tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; + if (flags & PPC_TIMER_PPE) { + /* PPE's use a modified version of the booke behavior */ + tb_env->flags = flags | PPC_DECR_UNDERFLOW_TRIGGERED; + } else { + tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED; + } tb_env->tb_freq = freq; tb_env->decr_freq = freq; diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index baab74c..bc70e50 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -99,8 +99,7 @@ static void spin_kick(CPUState *cs, run_on_cpu_data data) cs->halted = 0; cs->exception_index = -1; - cs->stopped = false; - qemu_cpu_kick(cs); + cpu_resume(cs); } static void spin_write(void *opaque, hwaddr addr, uint64_t value, @@ -175,7 +174,7 @@ static void ppce500_spin_initfn(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void ppce500_spin_class_init(ObjectClass *klass, void *data) +static void ppce500_spin_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/ppe42_machine.c b/hw/ppc/ppe42_machine.c new file mode 100644 index 0000000..f14a91b --- /dev/null +++ b/hw/ppc/ppe42_machine.c @@ -0,0 +1,101 @@ +/* + * Test Machine for the IBM PPE42 processor + * + * Copyright (c) 2025, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "system/address-spaces.h" +#include "hw/boards.h" +#include "hw/ppc/ppc.h" +#include "system/system.h" +#include "system/reset.h" +#include "system/kvm.h" +#include "qapi/error.h" + +#define TYPE_PPE42_MACHINE MACHINE_TYPE_NAME("ppe42_machine") +typedef MachineClass Ppe42MachineClass; +typedef struct Ppe42MachineState Ppe42MachineState; +DECLARE_OBJ_CHECKERS(Ppe42MachineState, Ppe42MachineClass, + PPE42_MACHINE, TYPE_PPE42_MACHINE) + +struct Ppe42MachineState { + MachineState parent_obj; + + PowerPCCPU cpu; +}; + +static void main_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); +} + +static void ppe42_machine_init(MachineState *machine) +{ + Ppe42MachineState *pms = PPE42_MACHINE(machine); + PowerPCCPU *cpu = &pms->cpu; + + if (kvm_enabled()) { + error_report("machine %s does not support the KVM accelerator", + MACHINE_GET_CLASS(machine)->name); + exit(EXIT_FAILURE); + } + if (machine->ram_size > 512 * KiB) { + error_report("RAM size more than 512 KiB is not supported"); + exit(1); + } + + /* init CPU */ + object_initialize_child(OBJECT(pms), "cpu", cpu, machine->cpu_type); + if (!qdev_realize(DEVICE(cpu), NULL, &error_fatal)) { + return; + } + + qemu_register_reset(main_cpu_reset, cpu); + + /* This sets the decrementer timebase */ + ppc_booke_timers_init(cpu, 37500000, PPC_TIMER_PPE); + + /* RAM */ + memory_region_add_subregion(get_system_memory(), 0xfff80000, machine->ram); +} + + +static void ppe42_machine_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + static const char * const valid_cpu_types[] = { + POWERPC_CPU_TYPE_NAME("PPE42"), + POWERPC_CPU_TYPE_NAME("PPE42X"), + POWERPC_CPU_TYPE_NAME("PPE42XM"), + NULL, + }; + + mc->desc = "PPE42 Test Machine"; + mc->init = ppe42_machine_init; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("PPE42XM"); + mc->valid_cpu_types = valid_cpu_types; + mc->default_ram_id = "ram"; + mc->default_ram_size = 512 * KiB; +} + +static const TypeInfo ppe42_machine_info = { + .name = TYPE_PPE42_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(Ppe42MachineState), + .class_init = ppe42_machine_class_init, + .class_size = sizeof(Ppe42MachineClass), +}; + +static void ppe42_machine_register_types(void) +{ + type_register_static(&ppe42_machine_info); +} + +type_init(ppe42_machine_register_types); diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 3e68d8e..5654a60 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -35,16 +35,18 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/datadir.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" #include "hw/qdev-properties.h" +#include "exec/target_page.h" #include "system/kvm.h" #include "system/reset.h" #include "trace.h" #include "elf.h" #include "qemu/units.h" -#include "audio/audio.h" +#include "qemu/audio.h" /* SMP is not enabled, for now */ #define MAX_CPUS 1 @@ -54,6 +56,8 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 +#define BIOS_ADDR 0xfff00000 +#define BIOS_SIZE (1 * MiB) #define NVRAM_SIZE 0x2000 static void fw_cfg_boot_set(void *opaque, const char *boot_device, @@ -240,6 +244,9 @@ static void ibm_40p_init(MachineState *machine) ISADevice *isa_dev; ISABus *isa_bus; void *fw_cfg; + MemoryRegion *bios = g_new(MemoryRegion, 1); + char *filename; + ssize_t bios_size = -1; uint32_t kernel_base = 0, initrd_base = 0; long kernel_size = 0, initrd_size = 0; char boot_device; @@ -262,10 +269,28 @@ static void ibm_40p_init(MachineState *machine) cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); qemu_register_reset(ppc_prep_reset, cpu); + /* allocate and load firmware */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_report("Could not find bios image '%s'", bios_name); + exit(1); + } + memory_region_init_rom(bios, NULL, "bios", BIOS_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), BIOS_ADDR, bios); + bios_size = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); + if (bios_size < 0) { + bios_size = load_image_targphys(filename, BIOS_ADDR, BIOS_SIZE, + &error_fatal); + } + if (bios_size < 0 || bios_size > BIOS_SIZE) { + error_report("Could not load bios image '%s'", filename); + return; + } + g_free(filename); + /* PCI host */ dev = qdev_new("raven-pcihost"); - qdev_prop_set_string(dev, "bios-name", bios_name); - qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); pcihost = SYS_BUS_DEVICE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev)); sysbus_realize_and_unref(pcihost, &error_fatal); @@ -284,6 +309,13 @@ static void ibm_40p_init(MachineState *machine) sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(i82378_dev, 15)); isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0")); + /* system control ports */ + isa_dev = isa_new("prep-systemio"); + dev = DEVICE(isa_dev); + qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc); + qdev_prop_set_uint32(dev, "equipment", 0xc0); + isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); + /* Memory controller */ isa_dev = isa_new("rs6000-mc"); dev = DEVICE(isa_dev); @@ -309,7 +341,6 @@ static void ibm_40p_init(MachineState *machine) dev = DEVICE(isa_dev); qdev_prop_set_uint32(dev, "iobase", 0x830); qdev_prop_set_uint32(dev, "irq", 10); - if (machine->audiodev) { qdev_prop_set_string(dev, "audiodev", machine->audiodev); } @@ -320,14 +351,7 @@ static void ibm_40p_init(MachineState *machine) qdev_prop_set_uint32(dev, "config", 12); isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); - isa_dev = isa_new("prep-systemio"); - dev = DEVICE(isa_dev); - qdev_prop_set_uint32(dev, "ibm-planar-id", 0xfc); - qdev_prop_set_uint32(dev, "equipment", 0xc0); - isa_realize_and_unref(isa_dev, isa_bus, &error_fatal); - - dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(1, 0), - "lsi53c810")); + dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(1, 0), "lsi53c810")); lsi53c8xx_handle_legacy_cmdline(dev); qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(i82378_dev, 13)); @@ -356,12 +380,8 @@ static void ibm_40p_init(MachineState *machine) kernel_base = KERNEL_LOAD_ADDR; kernel_size = load_image_targphys(machine->kernel_filename, kernel_base, - machine->ram_size - kernel_base); - if (kernel_size < 0) { - error_report("could not load kernel '%s'", - machine->kernel_filename); - exit(1); - } + machine->ram_size - kernel_base, + &error_fatal); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base); fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size); /* load initrd */ @@ -369,12 +389,8 @@ static void ibm_40p_init(MachineState *machine) initrd_base = INITRD_LOAD_ADDR; initrd_size = load_image_targphys(machine->initrd_filename, initrd_base, - machine->ram_size - initrd_base); - if (initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - machine->initrd_filename); - exit(1); - } + machine->ram_size - initrd_base, + &error_fatal); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); } diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index b1f2e13..41cd923 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -28,7 +28,7 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/error-report.h" /* for error_report() */ #include "qemu/module.h" @@ -290,7 +290,7 @@ static const Property prep_systemio_properties[] = { DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), }; -static void prep_systemio_class_initfn(ObjectClass *klass, void *data) +static void prep_systemio_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index 0e5d53b..a096405 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -24,7 +24,7 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "trace.h" #include "qom/object.h" @@ -212,7 +212,7 @@ static const Property rs6000mc_properties[] = { DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), }; -static void rs6000mc_class_initfn(ObjectClass *klass, void *data) +static void rs6000mc_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 7dc3b30..038b98d 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -24,7 +24,7 @@ #include "exec/page-protection.h" #include "hw/loader.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "ppc440.h" #include "hw/pci-host/ppc4xx.h" #include "hw/block/flash.h" @@ -43,10 +43,7 @@ #include <libfdt.h> #define BINARY_DEVICE_TREE_FILE "canyonlands.dtb" -#define UBOOT_FILENAME "u-boot-sam460-20100605.bin" -/* to extract the official U-Boot bin from the updater: */ -/* dd bs=1 skip=$(($(stat -c '%s' updater/updater-460) - 0x80000)) \ - if=updater/updater-460 of=u-boot-sam460-20100605.bin */ +#define UBOOT_FILENAME "u-boot-sam460.bin" #define PCIE0_DCRN_BASE 0x100 #define PCIE1_DCRN_BASE 0x120 @@ -97,7 +94,7 @@ static int sam460ex_load_uboot(void) * * Else, it's initialized to zero. And then 512KiB of ROM get * mapped on top of its second half (0xFFF80000..0xFFFFFFFF), - * initialized from u-boot-sam460-20100605.bin. + * initialized from UBOOT_FILENAME. * * This doesn't smell right. * @@ -142,7 +139,7 @@ static int sam460ex_load_device_tree(MachineState *machine, uint32_t clock_freq = CPU_FREQ; int offset; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (!filename) { error_report("Couldn't find dtb file `%s'", BINARY_DEVICE_TREE_FILE); exit(1); @@ -494,12 +491,8 @@ static void sam460ex_init(MachineState *machine) if (machine->initrd_filename) { initrd_size = load_image_targphys(machine->initrd_filename, RAMDISK_ADDR, - machine->ram_size - RAMDISK_ADDR); - if (initrd_size < 0) { - error_report("could not load ram disk '%s' at %x", - machine->initrd_filename, RAMDISK_ADDR); - exit(1); - } + machine->ram_size - RAMDISK_ADDR, + &error_fatal); } /* If we're loading a kernel directly, we must load the device tree too. */ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b0a0f8c..99b843b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -77,7 +77,6 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost-scsi-common.h" -#include "exec/ram_addr.h" #include "system/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" @@ -577,7 +576,7 @@ static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt, /* * Adds ibm,dynamic-reconfiguration-memory node. - * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * Refer to docs/specs/ppc-spapr-hotplug.rst for the documentation * of this device tree node. */ static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, @@ -901,12 +900,80 @@ static int spapr_dt_rng(void *fdt) return ret ? -1 : 0; } +static void spapr_dt_rtas_fadump(SpaprMachineState *spapr, void *fdt, int rtas) +{ + MachineState *ms = MACHINE(spapr); + MachineClass *mc = MACHINE_GET_CLASS(ms); + FadumpMemStruct *fdm = &spapr->registered_fdm; + uint16_t dump_status_flag; + + uint32_t max_possible_cpus = mc->possible_cpu_arch_ids(ms)->len; + uint64_t fadump_cpu_state_size = 0; + uint16_t fadump_versions[2] = { + FADUMP_VERSION /* min supported version */, + FADUMP_VERSION /* max supported version */ + }; + uint32_t fadump_rgn_sizes[2][3] = { + { + cpu_to_be32(FADUMP_CPU_STATE_DATA), + 0, 0 /* Calculated later */ + }, + { + cpu_to_be32(FADUMP_HPTE_REGION), + 0, 0 /* HPTE region not implemented */ + } + }; + + /* + * CPU State Data contains multiple fields such as header, num_cpus and + * register entries + * + * Calculate the maximum CPU State Data size, according to maximum + * possible CPUs the QEMU VM can have + * + * This calculation must match the 'cpu_state_len' calculation done in + * 'populate_cpu_state_data' in spapr_fadump.c + */ + fadump_cpu_state_size += sizeof(struct FadumpRegSaveAreaHeader); + fadump_cpu_state_size += 0xc; /* padding as in PAPR */ + fadump_cpu_state_size += sizeof(uint32_t); /* num_cpus */ + fadump_cpu_state_size += max_possible_cpus * /* reg entries */ + FADUMP_PER_CPU_REG_ENTRIES * + sizeof(struct FadumpRegEntry); + + /* Set maximum size for CPU state data region */ + assert(fadump_rgn_sizes[0][0] == cpu_to_be32(FADUMP_CPU_STATE_DATA)); + + /* Upper 32 bits of size, usually 0 */ + fadump_rgn_sizes[0][1] = cpu_to_be32(fadump_cpu_state_size >> 32); + + /* Lower 32 bits of size */ + fadump_rgn_sizes[0][2] = cpu_to_be32(fadump_cpu_state_size & 0xffffffff); + + /* Add device tree properties required from platform for fadump */ + _FDT((fdt_setprop(fdt, rtas, "ibm,configure-kernel-dump-version", + fadump_versions, sizeof(fadump_versions)))); + _FDT((fdt_setprop(fdt, rtas, "ibm,configure-kernel-dump-sizes", + fadump_rgn_sizes, sizeof(fadump_rgn_sizes)))); + + dump_status_flag = be16_to_cpu(fdm->header.dump_status_flag); + if (dump_status_flag & FADUMP_STATUS_DUMP_TRIGGERED) { + uint64_t fdm_size = + sizeof(struct FadumpSectionHeader) + + (be16_to_cpu(fdm->header.dump_num_sections) * + sizeof(struct FadumpSection)); + + _FDT((fdt_setprop(fdt, rtas, "ibm,kernel-dump", fdm, fdm_size))); + } +} + static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) { MachineState *ms = MACHINE(spapr); int rtas; GString *hypertas = g_string_sized_new(256); GString *qemu_hypertas = g_string_sized_new(256); + uint64_t max_device_addr = 0; uint32_t lrdr_capacity[] = { 0, 0, @@ -917,13 +984,15 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) /* Do we have device memory? */ if (MACHINE(spapr)->device_memory) { - uint64_t max_device_addr = MACHINE(spapr)->device_memory->base + + max_device_addr = MACHINE(spapr)->device_memory->base + memory_region_size(&MACHINE(spapr)->device_memory->mr); - - lrdr_capacity[0] = cpu_to_be32(max_device_addr >> 32); - lrdr_capacity[1] = cpu_to_be32(max_device_addr & 0xffffffff); + } else if (ms->ram_size == ms->maxram_size) { + max_device_addr = ms->ram_size; } + lrdr_capacity[0] = cpu_to_be32(max_device_addr >> 32); + lrdr_capacity[1] = cpu_to_be32(max_device_addr & 0xffffffff); + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); /* hypertas */ @@ -1013,6 +1082,8 @@ static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) _FDT(fdt_setprop(fdt, rtas, "ibm,lrdr-capacity", lrdr_capacity, sizeof(lrdr_capacity))); + spapr_dt_rtas_fadump(spapr, fdt, rtas); + spapr_dt_rtas_tokens(fdt, rtas); } @@ -1070,7 +1141,6 @@ static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt, static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) { MachineState *machine = MACHINE(spapr); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); int chosen; _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); @@ -1141,9 +1211,7 @@ static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt, bool reset) * We can deal with BAR reallocation just fine, advertise it * to the guest */ - if (smc->linux_pci_probe) { - _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); - } + _FDT(fdt_setprop_cell(fdt, chosen, "linux,pci-probe-only", 0)); spapr_dt_ov5_platform_support(spapr, fdt, chosen); } @@ -1180,7 +1248,6 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) { MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); uint32_t root_drc_type_mask = 0; int ret; void *fdt; @@ -1211,16 +1278,10 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) /* Host Model & Serial Number */ if (spapr->host_model) { _FDT(fdt_setprop_string(fdt, 0, "host-model", spapr->host_model)); - } else if (smc->broken_host_serial_model && kvmppc_get_host_model(&buf)) { - _FDT(fdt_setprop_string(fdt, 0, "host-model", buf)); - g_free(buf); } if (spapr->host_serial) { _FDT(fdt_setprop_string(fdt, 0, "host-serial", spapr->host_serial)); - } else if (smc->broken_host_serial_model && kvmppc_get_host_serial(&buf)) { - _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); - g_free(buf); } _FDT(fdt_setprop_cell(fdt, 0, "#address-cells", 2)); @@ -1258,9 +1319,8 @@ void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) /* ibm,drc-indexes and friends */ root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_LMB; - if (smc->dr_phb_enabled) { - root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PHB; - } + root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PHB; + if (mc->nvdimm_supported) { root_drc_type_mask |= SPAPR_DR_CONNECTOR_TYPE_PMEM; } @@ -2059,13 +2119,6 @@ static const VMStateDescription vmstate_spapr_irq_map = { }, }; -static bool spapr_dtb_needed(void *opaque) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(opaque); - - return smc->update_dt_enabled; -} - static int spapr_dtb_pre_load(void *opaque) { SpaprMachineState *spapr = (SpaprMachineState *)opaque; @@ -2081,7 +2134,6 @@ static const VMStateDescription vmstate_spapr_dtb = { .name = "spapr_dtb", .version_id = 1, .minimum_version_id = 1, - .needed = spapr_dtb_needed, .pre_load = spapr_dtb_pre_load, .fields = (const VMStateField[]) { VMSTATE_UINT32(fdt_initial_size, SpaprMachineState), @@ -2518,7 +2570,7 @@ static void htab_save_cleanup(void *opaque) static SaveVMHandlers savevm_htab_handlers = { .save_setup = htab_save_setup, .save_live_iterate = htab_save_iterate, - .save_live_complete_precopy = htab_save_complete, + .save_complete = htab_save_complete, .save_cleanup = htab_save_cleanup, .load_state = htab_load, }; @@ -2603,7 +2655,6 @@ static CPUArchId *spapr_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) { MachineState *ms = MACHINE(spapr); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); Error *local_err = NULL; bool vsmt_user = !!spapr->vsmt; int kvm_smt = kvmppc_smt_threads(); @@ -2639,15 +2690,6 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) return; } /* In this case, spapr->vsmt has been set by the command line */ - } else if (!smc->smp_threads_vsmt) { - /* - * Default VSMT value is tricky, because we need it to be as - * consistent as possible (for migration), but this requires - * changing it for at least some existing cases. We pick 8 as - * the value that we'd get with KVM on POWER8, the - * overwhelmingly common case in production systems. - */ - spapr->vsmt = MAX(8, smp_threads); } else { spapr->vsmt = smp_threads; } @@ -2756,7 +2798,6 @@ static PCIHostState *spapr_create_default_phb(void) static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp) { MachineState *machine = MACHINE(spapr); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); hwaddr rma_size = machine->ram_size; hwaddr node0_size = spapr_node0_size(machine); @@ -2769,15 +2810,6 @@ static hwaddr spapr_rma_size(SpaprMachineState *spapr, Error **errp) */ rma_size = MIN(rma_size, 1 * TiB); - /* - * Clamp the RMA size based on machine type. This is for - * migration compatibility with older qemu versions, which limited - * the RMA size for complicated and mostly bad reasons. - */ - if (smc->rma_limit) { - rma_size = MIN(rma_size, smc->rma_limit); - } - if (rma_size < MIN_RMA_SLOF) { error_setg(errp, "pSeries SLOF firmware requires >= %" HWADDR_PRIx @@ -2815,18 +2847,14 @@ static void spapr_machine_init(MachineState *machine) int i; MemoryRegion *sysmem = get_system_memory(); long load_limit, fw_size; - Error *resize_hpt_err = NULL; + Error *errp = NULL; NICInfo *nd; if (!filename) { error_report("Could not find LPAR firmware '%s'", bios_name); exit(1); } - fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE); - if (fw_size <= 0) { - error_report("Could not load LPAR firmware '%s'", filename); - exit(1); - } + fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE, &error_fatal); /* * if Secure VM (PEF) support is configured, then initialize it @@ -2843,7 +2871,7 @@ static void spapr_machine_init(MachineState *machine) /* Determine capabilities to run with */ spapr_caps_init(spapr); - kvmppc_check_papr_resize_hpt(&resize_hpt_err); + kvmppc_check_papr_resize_hpt(&errp); if (spapr->resize_hpt == SPAPR_RESIZE_HPT_DEFAULT) { /* * If the user explicitly requested a mode we should either @@ -2851,10 +2879,10 @@ static void spapr_machine_init(MachineState *machine) * it's not set explicitly, we reset our mode to something * that works */ - if (resize_hpt_err) { + if (errp) { spapr->resize_hpt = SPAPR_RESIZE_HPT_DISABLED; - error_free(resize_hpt_err); - resize_hpt_err = NULL; + error_free(errp); + errp = NULL; } else { spapr->resize_hpt = smc->resize_hpt_default; } @@ -2862,14 +2890,14 @@ static void spapr_machine_init(MachineState *machine) assert(spapr->resize_hpt != SPAPR_RESIZE_HPT_DEFAULT); - if ((spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) && resize_hpt_err) { + if ((spapr->resize_hpt != SPAPR_RESIZE_HPT_DISABLED) && errp) { /* * User requested HPT resize, but this host can't supply it. Bail out */ - error_report_err(resize_hpt_err); + error_report_err(errp); exit(1); } - error_free(resize_hpt_err); + error_free(errp); spapr->rma_size = spapr_rma_size(spapr, &error_fatal); @@ -3007,10 +3035,8 @@ static void spapr_machine_init(MachineState *machine) * connectors for a PHBs PCI slots) are added as needed during their * parent's realization. */ - if (smc->dr_phb_enabled) { - for (i = 0; i < SPAPR_MAX_PHBS; i++) { - spapr_dr_connector_new(OBJECT(machine), TYPE_SPAPR_DRC_PHB, i); - } + for (i = 0; i < SPAPR_MAX_PHBS; i++) { + spapr_dr_connector_new(OBJECT(machine), TYPE_SPAPR_DRC_PHB, i); } /* Set up PCI */ @@ -3085,14 +3111,9 @@ static void spapr_machine_init(MachineState *machine) spapr->initrd_base = (spapr->kernel_addr + spapr->kernel_size + 0x1ffff) & ~0xffff; spapr->initrd_size = load_image_targphys(initrd_filename, - spapr->initrd_base, - load_limit - - spapr->initrd_base); - if (spapr->initrd_size < 0) { - error_report("could not load initial ram disk '%s'", - initrd_filename); - exit(1); - } + spapr->initrd_base, + load_limit - spapr->initrd_base, + &error_fatal); } } @@ -3345,9 +3366,7 @@ static char *spapr_get_ic_mode(Object *obj, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); - if (spapr->irq == &spapr_irq_xics_legacy) { - return g_strdup("legacy"); - } else if (spapr->irq == &spapr_irq_xics) { + if (spapr->irq == &spapr_irq_xics) { return g_strdup("xics"); } else if (spapr->irq == &spapr_irq_xive) { return g_strdup("xive"); @@ -3361,11 +3380,6 @@ static void spapr_set_ic_mode(Object *obj, const char *value, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(obj); - if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { - error_setg(errp, "This machine only uses the legacy XICS backend, don't pass ic-mode"); - return; - } - /* The legacy IRQ backend can not be set */ if (strcmp(value, "xics") == 0) { spapr->irq = &spapr_irq_xics; @@ -4091,20 +4105,65 @@ int spapr_phb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, return 0; } +static bool spapr_phb_placement(SpaprMachineState *spapr, uint32_t index, + uint64_t *buid, hwaddr *pio, + hwaddr *mmio32, hwaddr *mmio64, + unsigned n_dma, uint32_t *liobns, Error **errp) +{ + /* + * New-style PHB window placement. + * + * Goals: Gives large (1TiB), naturally aligned 64-bit MMIO window + * for each PHB, in addition to 2GiB 32-bit MMIO and 64kiB PIO + * windows. + * + * Some guest kernels can't work with MMIO windows above 1<<46 + * (64TiB), so we place up to 31 PHBs in the area 32TiB..64TiB + * + * 32TiB..(33TiB+1984kiB) contains the 64kiB PIO windows for each + * PHB stacked together. (32TiB+2GiB)..(32TiB+64GiB) contains the + * 2GiB 32-bit MMIO windows for each PHB. Then 33..64TiB has the + * 1TiB 64-bit MMIO windows for each PHB. + */ + const uint64_t base_buid = 0x800000020000000ULL; + int i; + + /* Sanity check natural alignments */ + QEMU_BUILD_BUG_ON((SPAPR_PCI_BASE % SPAPR_PCI_MEM64_WIN_SIZE) != 0); + QEMU_BUILD_BUG_ON((SPAPR_PCI_LIMIT % SPAPR_PCI_MEM64_WIN_SIZE) != 0); + QEMU_BUILD_BUG_ON((SPAPR_PCI_MEM64_WIN_SIZE % SPAPR_PCI_MEM32_WIN_SIZE) != 0); + QEMU_BUILD_BUG_ON((SPAPR_PCI_MEM32_WIN_SIZE % SPAPR_PCI_IO_WIN_SIZE) != 0); + /* Sanity check bounds */ + QEMU_BUILD_BUG_ON((SPAPR_MAX_PHBS * SPAPR_PCI_IO_WIN_SIZE) > + SPAPR_PCI_MEM32_WIN_SIZE); + QEMU_BUILD_BUG_ON((SPAPR_MAX_PHBS * SPAPR_PCI_MEM32_WIN_SIZE) > + SPAPR_PCI_MEM64_WIN_SIZE); + + if (index >= SPAPR_MAX_PHBS) { + error_setg(errp, "\"index\" for PAPR PHB is too large (max %llu)", + SPAPR_MAX_PHBS - 1); + return false; + } + + *buid = base_buid + index; + for (i = 0; i < n_dma; ++i) { + liobns[i] = SPAPR_PCI_LIOBN(index, i); + } + + *pio = SPAPR_PCI_BASE + index * SPAPR_PCI_IO_WIN_SIZE; + *mmio32 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM32_WIN_SIZE; + *mmio64 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM64_WIN_SIZE; + return true; +} + static bool spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); const unsigned windows_supported = spapr_phb_windows_supported(sphb); SpaprDrc *drc; - if (dev->hotplugged && !smc->dr_phb_enabled) { - error_setg(errp, "PHB hotplug not supported for this machine"); - return false; - } - if (sphb->index == (uint32_t)-1) { error_setg(errp, "\"index\" for PAPR PHB is mandatory"); return false; @@ -4120,26 +4179,18 @@ static bool spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, * This will check that sphb->index doesn't exceed the maximum number of * PHBs for the current machine type. */ - return - smc->phb_placement(spapr, sphb->index, - &sphb->buid, &sphb->io_win_addr, - &sphb->mem_win_addr, &sphb->mem64_win_addr, - windows_supported, sphb->dma_liobn, - errp); + return spapr_phb_placement(spapr, sphb->index, + &sphb->buid, &sphb->io_win_addr, + &sphb->mem_win_addr, &sphb->mem64_win_addr, + windows_supported, sphb->dma_liobn, errp); } static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev) { - SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); SpaprDrc *drc; bool hotplugged = spapr_drc_hotplugged(dev); - if (!smc->dr_phb_enabled) { - return; - } - drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index); /* hotplug hooks should check it's enabled before getting this far */ assert(drc); @@ -4265,7 +4316,6 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, { SpaprMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(sms); - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { if (spapr_memory_hot_unplug_supported(sms)) { @@ -4280,10 +4330,6 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, } spapr_core_unplug_request(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { - if (!smc->dr_phb_enabled) { - error_setg(errp, "PHB hot unplug not supported on this machine"); - return; - } spapr_phb_unplug_request(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_TPM_PROXY)) { spapr_tpm_proxy_unplug(hotplug_dev, dev); @@ -4384,57 +4430,6 @@ static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine) return machine->possible_cpus; } -static bool spapr_phb_placement(SpaprMachineState *spapr, uint32_t index, - uint64_t *buid, hwaddr *pio, - hwaddr *mmio32, hwaddr *mmio64, - unsigned n_dma, uint32_t *liobns, Error **errp) -{ - /* - * New-style PHB window placement. - * - * Goals: Gives large (1TiB), naturally aligned 64-bit MMIO window - * for each PHB, in addition to 2GiB 32-bit MMIO and 64kiB PIO - * windows. - * - * Some guest kernels can't work with MMIO windows above 1<<46 - * (64TiB), so we place up to 31 PHBs in the area 32TiB..64TiB - * - * 32TiB..(33TiB+1984kiB) contains the 64kiB PIO windows for each - * PHB stacked together. (32TiB+2GiB)..(32TiB+64GiB) contains the - * 2GiB 32-bit MMIO windows for each PHB. Then 33..64TiB has the - * 1TiB 64-bit MMIO windows for each PHB. - */ - const uint64_t base_buid = 0x800000020000000ULL; - int i; - - /* Sanity check natural alignments */ - QEMU_BUILD_BUG_ON((SPAPR_PCI_BASE % SPAPR_PCI_MEM64_WIN_SIZE) != 0); - QEMU_BUILD_BUG_ON((SPAPR_PCI_LIMIT % SPAPR_PCI_MEM64_WIN_SIZE) != 0); - QEMU_BUILD_BUG_ON((SPAPR_PCI_MEM64_WIN_SIZE % SPAPR_PCI_MEM32_WIN_SIZE) != 0); - QEMU_BUILD_BUG_ON((SPAPR_PCI_MEM32_WIN_SIZE % SPAPR_PCI_IO_WIN_SIZE) != 0); - /* Sanity check bounds */ - QEMU_BUILD_BUG_ON((SPAPR_MAX_PHBS * SPAPR_PCI_IO_WIN_SIZE) > - SPAPR_PCI_MEM32_WIN_SIZE); - QEMU_BUILD_BUG_ON((SPAPR_MAX_PHBS * SPAPR_PCI_MEM32_WIN_SIZE) > - SPAPR_PCI_MEM64_WIN_SIZE); - - if (index >= SPAPR_MAX_PHBS) { - error_setg(errp, "\"index\" for PAPR PHB is too large (max %llu)", - SPAPR_MAX_PHBS - 1); - return false; - } - - *buid = base_buid + index; - for (i = 0; i < n_dma; ++i) { - liobns[i] = SPAPR_PCI_LIOBN(index, i); - } - - *pio = SPAPR_PCI_BASE + index * SPAPR_PCI_IO_WIN_SIZE; - *mmio32 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM32_WIN_SIZE; - *mmio64 = SPAPR_PCI_BASE + (index + 1) * SPAPR_PCI_MEM64_WIN_SIZE; - return true; -} - static ICSState *spapr_ics_get(XICSFabric *dev, int irq) { SpaprMachineState *spapr = SPAPR_MACHINE(dev); @@ -4468,21 +4463,14 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf) /* * This is a XIVE only operation */ -static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool spapr_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { SpaprMachineState *spapr = SPAPR_MACHINE(xfb); XivePresenter *xptr = XIVE_PRESENTER(spapr->active_intc); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, - priority, logic_serv, match); - if (count < 0) { - return count; - } /* * When we implement the save and restore of the thread interrupt @@ -4493,12 +4481,14 @@ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, * Until this is done, the sPAPR machine should find at least one * matching context always. */ - if (count == 0) { + if (!xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, + priority, logic_serv, match)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n", nvt_blk, nvt_idx); + return false; } - return count; + return true; } int spapr_get_vcpu_id(PowerPCCPU *cpu) @@ -4595,7 +4585,7 @@ static void spapr_cpu_exec_exit(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) } } -static void spapr_machine_class_init(ObjectClass *oc, void *data) +static void spapr_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(oc); @@ -4644,14 +4634,12 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) hc->unplug_request = spapr_machine_device_unplug_request; hc->unplug = spapr_machine_device_unplug; - smc->update_dt_enabled = true; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v2.0"); mc->has_hotpluggable_cpus = true; mc->nvdimm_supported = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; fwc->get_dev_path = spapr_get_fw_dev_path; nc->nmi_monitor_handler = spapr_nmi; - smc->phb_placement = spapr_phb_placement; vhc->cpu_in_nested = spapr_cpu_in_nested; vhc->deliver_hv_excp = spapr_exit_nested; vhc->hypercall = emulate_spapr_hypercall; @@ -4698,10 +4686,6 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_AIL_MODE_3] = SPAPR_CAP_ON; spapr_caps_add_properties(smc); smc->irq = &spapr_irq_dual; - smc->dr_phb_enabled = true; - smc->linux_pci_probe = true; - smc->smp_threads_vsmt = true; - smc->nr_xirqs = SPAPR_NR_XIRQS; xfc->match_nvt = spapr_match_nvt; vmc->client_architecture_support = spapr_vof_client_architecture_support; vmc->quiesce = spapr_vof_quiesce; @@ -4717,7 +4701,7 @@ static const TypeInfo spapr_machine_info = { .instance_finalize = spapr_machine_finalizefn, .class_size = sizeof(SpaprMachineClass), .class_init = spapr_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER }, @@ -4739,7 +4723,7 @@ static void spapr_machine_latest_class_options(MachineClass *mc) #define DEFINE_SPAPR_MACHINE_IMPL(latest, ...) \ static void MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__)( \ ObjectClass *oc, \ - void *data) \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(class_options, spapr, __VA_ARGS__)(mc); \ @@ -4767,14 +4751,36 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* + * pseries-10.2 + */ +static void spapr_machine_10_2_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 2); + +/* + * pseries-10.1 + */ +static void spapr_machine_10_1_class_options(MachineClass *mc) +{ + spapr_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); +} + +DEFINE_SPAPR_MACHINE(10, 1); + +/* * pseries-10.0 */ static void spapr_machine_10_0_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_10_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); } -DEFINE_SPAPR_MACHINE_AS_LATEST(10, 0); +DEFINE_SPAPR_MACHINE(10, 0); /* * pseries-9.2 @@ -4957,110 +4963,6 @@ static void spapr_machine_5_0_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE(5, 0); -/* - * pseries-4.2 - */ -static void spapr_machine_4_2_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_5_0_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len); - smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; - smc->default_caps.caps[SPAPR_CAP_FWNMI] = SPAPR_CAP_OFF; - smc->rma_limit = 16 * GiB; - mc->nvdimm_supported = false; -} - -DEFINE_SPAPR_MACHINE(4, 2); - -/* - * pseries-4.1 - */ -static void spapr_machine_4_1_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - static GlobalProperty compat[] = { - /* Only allow 4kiB and 64kiB IOMMU pagesizes */ - { TYPE_SPAPR_PCI_HOST_BRIDGE, "pgsz", "0x11000" }, - }; - - spapr_machine_4_2_class_options(mc); - smc->linux_pci_probe = false; - smc->smp_threads_vsmt = false; - compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} - -DEFINE_SPAPR_MACHINE(4, 1); - -/* - * pseries-4.0 - */ -static bool phb_placement_4_0(SpaprMachineState *spapr, uint32_t index, - uint64_t *buid, hwaddr *pio, - hwaddr *mmio32, hwaddr *mmio64, - unsigned n_dma, uint32_t *liobns, Error **errp) -{ - if (!spapr_phb_placement(spapr, index, buid, pio, mmio32, mmio64, n_dma, - liobns, errp)) { - return false; - } - return true; -} -static void spapr_machine_4_0_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_4_1_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_4_0, hw_compat_4_0_len); - smc->phb_placement = phb_placement_4_0; - smc->irq = &spapr_irq_xics; - smc->pre_4_1_migration = true; -} - -DEFINE_SPAPR_MACHINE(4, 0); - -/* - * pseries-3.1 - */ -static void spapr_machine_3_1_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_4_0_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); - - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); - smc->update_dt_enabled = false; - smc->dr_phb_enabled = false; - smc->broken_host_serial_model = true; - smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; - smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; - smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; - smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; -} - -DEFINE_SPAPR_MACHINE(3, 1); - -/* - * pseries-3.0 - */ - -static void spapr_machine_3_0_class_options(MachineClass *mc) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - - spapr_machine_3_1_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); - - smc->legacy_irq_allocation = true; - smc->nr_xirqs = 0x400; - smc->irq = &spapr_irq_xics_legacy; -} - -DEFINE_SPAPR_MACHINE(3, 0); - static void spapr_machine_register_types(void) { type_register_static(&spapr_machine_info); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 815c94e..170795a 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -27,7 +27,6 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "system/hw_accel.h" -#include "exec/ram_addr.h" #include "target/ppc/cpu.h" #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" @@ -67,7 +66,6 @@ typedef struct SpaprCapabilityInfo { void (*apply)(SpaprMachineState *spapr, uint8_t val, Error **errp); void (*cpu_apply)(SpaprMachineState *spapr, PowerPCCPU *cpu, uint8_t val, Error **errp); - bool (*migrate_needed)(void *opaque); } SpaprCapabilityInfo; static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name, @@ -337,11 +335,6 @@ static void cap_hpt_maxpagesize_apply(SpaprMachineState *spapr, spapr_check_pagesize(spapr, qemu_minrampagesize(), errp); } -static bool cap_hpt_maxpagesize_migrate_needed(void *opaque) -{ - return !SPAPR_MACHINE_GET_CLASS(opaque)->pre_4_1_migration; -} - static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift, uint32_t pshift) { @@ -794,7 +787,6 @@ SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .type = "int", .apply = cap_hpt_maxpagesize_apply, .cpu_apply = cap_hpt_maxpagesize_cpu_apply, - .migrate_needed = cap_hpt_maxpagesize_migrate_needed, }, [SPAPR_CAP_NESTED_KVM_HV] = { .name = "nested-hv", @@ -983,11 +975,8 @@ int spapr_caps_post_migration(SpaprMachineState *spapr) static bool spapr_cap_##sname##_needed(void *opaque) \ { \ SpaprMachineState *spapr = opaque; \ - bool (*needed)(void *opaque) = \ - capability_table[cap].migrate_needed; \ \ - return needed ? needed(opaque) : true && \ - spapr->cmd_line_caps[cap] && \ + return spapr->cmd_line_caps[cap] && \ (spapr->eff.caps[cap] != \ spapr->def.caps[cap]); \ } \ diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index faf9170..4952f9b 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -373,7 +373,7 @@ static const Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID), }; -static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) +static void spapr_cpu_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); @@ -388,7 +388,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) #define DEFINE_SPAPR_CPU_CORE_TYPE(cpu_model) \ { \ .parent = TYPE_SPAPR_CPU_CORE, \ - .class_data = (void *) POWERPC_CPU_TYPE_NAME(cpu_model), \ + .class_data = POWERPC_CPU_TYPE_NAME(cpu_model), \ .class_init = spapr_cpu_core_class_init, \ .name = SPAPR_CPU_CORE_TYPE_NAME(cpu_model), \ } diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 549b652..d2044b4 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -589,7 +589,7 @@ static void spapr_dr_connector_instance_init(Object *obj) drc->state = drck->empty_state; } -static void spapr_dr_connector_class_init(ObjectClass *k, void *data) +static void spapr_dr_connector_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); @@ -665,7 +665,7 @@ static void unrealize_physical(DeviceState *d) qemu_unregister_reset(drc_physical_reset, drcp); } -static void spapr_drc_physical_class_init(ObjectClass *k, void *data) +static void spapr_drc_physical_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -679,7 +679,7 @@ static void spapr_drc_physical_class_init(ObjectClass *k, void *data) drck->empty_state = SPAPR_DRC_STATE_PHYSICAL_POWERON; } -static void spapr_drc_logical_class_init(ObjectClass *k, void *data) +static void spapr_drc_logical_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -690,7 +690,7 @@ static void spapr_drc_logical_class_init(ObjectClass *k, void *data) drck->empty_state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE; } -static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) +static void spapr_drc_cpu_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -701,7 +701,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_core_dt_populate; } -static void spapr_drc_pci_class_init(ObjectClass *k, void *data) +static void spapr_drc_pci_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -712,7 +712,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_pci_dt_populate; } -static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) +static void spapr_drc_lmb_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -723,7 +723,7 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_lmb_dt_populate; } -static void spapr_drc_phb_class_init(ObjectClass *k, void *data) +static void spapr_drc_phb_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -734,7 +734,7 @@ static void spapr_drc_phb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_phb_dt_populate; } -static void spapr_drc_pmem_class_init(ObjectClass *k, void *data) +static void spapr_drc_pmem_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 832b021..892ddc7 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -1041,20 +1041,14 @@ void spapr_clear_pending_hotplug_events(SpaprMachineState *spapr) void spapr_events_init(SpaprMachineState *spapr) { - int epow_irq = SPAPR_IRQ_EPOW; - - if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { - epow_irq = spapr_irq_findone(spapr, &error_fatal); - } - - spapr_irq_claim(spapr, epow_irq, false, &error_fatal); + spapr_irq_claim(spapr, SPAPR_IRQ_EPOW, false, &error_fatal); QTAILQ_INIT(&spapr->pending_events); spapr->event_sources = spapr_event_sources_new(); spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW, - epow_irq); + SPAPR_IRQ_EPOW); /* NOTE: if machine supports modern/dedicated hotplug event source, * we add it to the device-tree unconditionally. This means we may @@ -1065,16 +1059,10 @@ void spapr_events_init(SpaprMachineState *spapr) * checking that it's enabled. */ if (spapr->use_hotplug_event_source) { - int hp_irq = SPAPR_IRQ_HOTPLUG; - - if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { - hp_irq = spapr_irq_findone(spapr, &error_fatal); - } - - spapr_irq_claim(spapr, hp_irq, false, &error_fatal); + spapr_irq_claim(spapr, SPAPR_IRQ_HOTPLUG, false, &error_fatal); spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG, - hp_irq); + SPAPR_IRQ_HOTPLUG); } spapr->epow_notifier.notify = spapr_powerdown_req; diff --git a/hw/ppc/spapr_fadump.c b/hw/ppc/spapr_fadump.c new file mode 100644 index 0000000..13cab0c --- /dev/null +++ b/hw/ppc/spapr_fadump.c @@ -0,0 +1,731 @@ +/* + * Firmware Assisted Dump in PSeries + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/ppc/spapr.h" +#include "qemu/units.h" +#include "system/cpus.h" +#include "system/hw_accel.h" +#include <math.h> + +/* + * Copy the ascii values for first 8 characters from a string into u64 + * variable at their respective indexes. + * e.g. + * The string "FADMPINF" will be converted into 0x4641444d50494e46 + */ +static uint64_t fadump_str_to_u64(const char *str) +{ + uint64_t val = 0; + int i; + + for (i = 0; i < sizeof(val); i++) { + val = (*str) ? (val << 8) | *str++ : val << 8; + } + return val; +} + +/** + * Get the identifier id for register entries of GPRs + * + * It gives the same id as 'fadump_str_to_u64' when the complete string id + * of the GPR is given, ie. + * + * fadump_str_to_u64("GPR05") == fadump_gpr_id_to_u64(5); + * fadump_str_to_u64("GPR12") == fadump_gpr_id_to_u64(12); + * + * And so on. Hence this can be implemented by creating a dynamic + * string for each GPR, such as "GPR00", "GPR01", ... "GPR31" + * Instead of allocating a string, an observation from the math of + * 'fadump_str_to_u64' or from PAPR tells us that there's a pattern + * in the identifier IDs, such that the first 4 bytes are affected only by + * whether it is GPR0*, GPR1*, GPR2*, GPR3*. + * Upper half of 5th byte is always 0x3. Lower half (nibble) of 5th byte + * is the tens digit of the GPR id, ie. GPR ID / 10. + * Upper half of 6th byte is always 0x3. Lower half (nibble) of 5th byte + * is the ones digit of the GPR id, ie. GPR ID % 10 + * + * For example, for GPR 29, the 5th and 6th byte will be 0x32 and 0x39 + */ +static uint64_t fadump_gpr_id_to_u64(uint32_t gpr_id) +{ + uint64_t val = 0; + + /* Valid range of GPR id is only GPR0 to GPR31 */ + assert(gpr_id < 32); + + /* Below calculations set the 0th to 5th byte */ + if (gpr_id <= 9) { + val = fadump_str_to_u64("GPR0"); + } else if (gpr_id <= 19) { + val = fadump_str_to_u64("GPR1"); + } else if (gpr_id <= 29) { + val = fadump_str_to_u64("GPR2"); + } else { + val = fadump_str_to_u64("GPR3"); + } + + /* Set the 6th byte */ + val |= 0x30000000; + val |= ((gpr_id % 10) << 24); + + return val; +} + +/* + * Handle the "FADUMP_CMD_REGISTER" command in 'ibm,configure-kernel-dump' + * + * Note: Any changes made by the kernel to the fadump memory struct won't + * reflect in QEMU after the 'ibm,configure-kernel-dump' RTAS call has returned, + * as we store the passed fadump memory structure passed during fadump + * registration. + * Kernel has to invalidate & re-register fadump, if it intends to make any + * changes to the fadump memory structure + * + * Returns: + * * RTAS_OUT_SUCCESS: On successful registration + * * RTAS_OUT_PARAM_ERROR: If parameters are not correct, eg. too many + * sections, invalid memory addresses that we are + * unable to read, etc + * * RTAS_OUT_DUMP_ALREADY_REGISTERED: Dump already registered + * * RTAS_OUT_HW_ERROR: Misc issue such as memory access failures + */ +uint32_t do_fadump_register(SpaprMachineState *spapr, target_ulong args) +{ + FadumpSectionHeader header; + FadumpSection regions[FADUMP_MAX_SECTIONS] = {0}; + target_ulong fdm_addr = rtas_ld(args, 1); + target_ulong fdm_size = rtas_ld(args, 2); + AddressSpace *default_as = &address_space_memory; + MemTxResult io_result; + MemTxAttrs attrs; + uint64_t next_section_addr; + uint16_t dump_num_sections; + + /* Mark the memory transaction as privileged memory access */ + attrs.user = 0; + attrs.memory = 1; + + if (spapr->fadump_registered) { + /* FADump already registered */ + return RTAS_OUT_DUMP_ALREADY_REGISTERED; + } + + if (spapr->fadump_dump_active) { + return RTAS_OUT_DUMP_ACTIVE; + } + + if (fdm_size < sizeof(FadumpSectionHeader)) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Header size is invalid: " TARGET_FMT_lu "\n", fdm_size); + return RTAS_OUT_PARAM_ERROR; + } + + /* Ensure fdm_addr points to a valid RMR-memory/RMA-memory buffer */ + if ((fdm_addr <= 0) || ((fdm_addr + fdm_size) > spapr->rma_size)) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Invalid fdm address: " TARGET_FMT_lu "\n", fdm_addr); + return RTAS_OUT_PARAM_ERROR; + } + + /* Try to read the passed fadump header */ + io_result = address_space_read(default_as, fdm_addr, attrs, + &header, sizeof(header)); + if (io_result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Unable to read fdm: " TARGET_FMT_lu "\n", fdm_addr); + + return RTAS_OUT_HW_ERROR; + } + + /* Verify that we understand the fadump header version */ + if (header.dump_format_version != cpu_to_be32(FADUMP_VERSION)) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Unknown fadump header version: 0x%x\n", + header.dump_format_version); + return RTAS_OUT_PARAM_ERROR; + } + + /* Reset dump status flags */ + header.dump_status_flag = 0; + + dump_num_sections = be16_to_cpu(header.dump_num_sections); + + if (dump_num_sections > FADUMP_MAX_SECTIONS) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Too many sections: %d sections\n", dump_num_sections); + return RTAS_OUT_PARAM_ERROR; + } + + next_section_addr = + fdm_addr + + be32_to_cpu(header.offset_first_dump_section); + + for (int i = 0; i < dump_num_sections; ++i) { + /* Read the fadump section from memory */ + io_result = address_space_read(default_as, next_section_addr, attrs, + ®ions[i], sizeof(regions[i])); + if (io_result != MEMTX_OK) { + qemu_log_mask(LOG_UNIMP, + "FADump: Unable to read fadump %dth section\n", i); + return RTAS_OUT_PARAM_ERROR; + } + + next_section_addr += sizeof(regions[i]); + } + + spapr->fadump_registered = true; + spapr->fadump_dump_active = false; + + /* Store the registered fadump memory struct */ + spapr->registered_fdm.header = header; + for (int i = 0; i < dump_num_sections; ++i) { + spapr->registered_fdm.rgn[i] = regions[i]; + } + + return RTAS_OUT_SUCCESS; +} + +/* + * Copy the source region of given fadump section, to the destination + * address mentioned in the region + * + * Also set the region's error flag, if the copy fails due to non-existent + * address (MEMTX_DECODE_ERROR) or permission issues (MEMTX_ACCESS_ERROR) + * + * Returns true if successful copy + * + * Returns false in case of any other error, being treated as hardware + * error for fadump purposes + */ +static bool do_preserve_region(FadumpSection *region) +{ + AddressSpace *default_as = &address_space_memory; + MemTxResult io_result; + MemTxAttrs attrs; + uint64_t src_addr, src_len, dest_addr; + uint64_t num_chunks; + g_autofree void *copy_buffer = NULL; + + src_addr = be64_to_cpu(region->source_address); + src_len = be64_to_cpu(region->source_len); + dest_addr = be64_to_cpu(region->destination_address); + + /* Mark the memory transaction as privileged memory access */ + attrs.user = 0; + attrs.memory = 1; + + /* + * Optimisation: Skip copy if source and destination are same + * (eg. param area) + */ + if (src_addr == dest_addr) { + region->bytes_dumped = cpu_to_be64(src_len); + return true; + } + +#define FADUMP_CHUNK_SIZE ((size_t)(32 * MiB)) + copy_buffer = g_try_malloc(FADUMP_CHUNK_SIZE); + if (copy_buffer == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed allocating memory (size: %zu) for copying" + " reserved memory regions\n", FADUMP_CHUNK_SIZE); + return false; + } + + num_chunks = ceil((src_len * 1.0f) / FADUMP_CHUNK_SIZE); + for (uint64_t chunk_id = 0; chunk_id < num_chunks; ++chunk_id) { + /* Take minimum of bytes left to copy, and chunk size */ + uint64_t copy_len = MIN( + src_len - (chunk_id * FADUMP_CHUNK_SIZE), + FADUMP_CHUNK_SIZE + ); + + /* Copy the source region to destination */ + io_result = address_space_read(default_as, src_addr, attrs, + copy_buffer, copy_len); + if ((io_result & MEMTX_DECODE_ERROR) || + (io_result & MEMTX_ACCESS_ERROR)) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to decode/access address in section: %d\n", + region->source_data_type); + + /* + * Invalid source address is not an hardware error, instead + * wrong parameter from the kernel. + * Return true to let caller know to continue reading other + * sections + */ + region->error_flags = FADUMP_ERROR_INVALID_SOURCE_ADDR; + region->bytes_dumped = 0; + return true; + } else if (io_result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to read source region in section: %d\n", + region->source_data_type); + + return false; + } + + io_result = address_space_write(default_as, dest_addr, attrs, + copy_buffer, copy_len); + if ((io_result & MEMTX_DECODE_ERROR) || + (io_result & MEMTX_ACCESS_ERROR)) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to decode/access address in section: %d\n", + region->source_data_type); + + /* + * Invalid destination address is not an hardware error, + * instead wrong parameter from the kernel. + * Return true to let caller know to continue reading other + * sections + */ + region->error_flags = FADUMP_ERROR_INVALID_DEST_ADDR; + region->bytes_dumped = 0; + return true; + } else if (io_result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to write destination in section: %d\n", + region->source_data_type); + + return false; + } + + src_addr += FADUMP_CHUNK_SIZE; + dest_addr += FADUMP_CHUNK_SIZE; + } +#undef FADUMP_CHUNK_SIZE + + /* + * Considering address_space_write would have copied the + * complete region + */ + region->bytes_dumped = cpu_to_be64(src_len); + return true; +} + +/* + * Populate the passed CPUs register entries, in the buffer starting at + * the argument 'curr_reg_entry' + * + * The register entries is an array of pair of register id and register + * value, as described in Table 591/592 in section "H.1 Register Save Area" + * in PAPR v2.13 + * + * Returns pointer just past this CPU's register entries, which can be used + * as the start address for next CPU's register entries + */ +static FadumpRegEntry *populate_cpu_reg_entries(CPUState *cpu, + FadumpRegEntry *curr_reg_entry) +{ + CPUPPCState *env; + PowerPCCPU *ppc_cpu; + uint32_t num_regs_per_cpu = 0; + + ppc_cpu = POWERPC_CPU(cpu); + env = cpu_env(cpu); + num_regs_per_cpu = 0; + + /* + * CPUSTRT and CPUEND register entries follow this format: + * + * 8 Bytes Reg ID (BE) | 4 Bytes (0x0) | 4 Bytes Logical CPU ID (BE) + */ + curr_reg_entry->reg_id = + cpu_to_be64(fadump_str_to_u64("CPUSTRT")); + curr_reg_entry->reg_value = cpu_to_be64( + ppc_cpu->vcpu_id & FADUMP_CPU_ID_MASK); + ++curr_reg_entry; + +#define REG_ENTRY(id, val) \ + do { \ + curr_reg_entry->reg_id = \ + cpu_to_be64(fadump_str_to_u64(#id)); \ + curr_reg_entry->reg_value = cpu_to_be64(val); \ + ++curr_reg_entry; \ + ++num_regs_per_cpu; \ + } while (0) + + REG_ENTRY(ACOP, env->spr[SPR_ACOP]); + REG_ENTRY(AMR, env->spr[SPR_AMR]); + REG_ENTRY(BESCR, env->spr[SPR_BESCR]); + REG_ENTRY(CFAR, env->spr[SPR_CFAR]); + REG_ENTRY(CIABR, env->spr[SPR_CIABR]); + + /* Save the condition register */ + REG_ENTRY(CR, ppc_get_cr(env)); + + REG_ENTRY(CTR, env->spr[SPR_CTR]); + REG_ENTRY(CTRL, env->spr[SPR_CTRL]); + REG_ENTRY(DABR, env->spr[SPR_DABR]); + REG_ENTRY(DABRX, env->spr[SPR_DABRX]); + REG_ENTRY(DAR, env->spr[SPR_DAR]); + REG_ENTRY(DAWR0, env->spr[SPR_DAWR0]); + REG_ENTRY(DAWR1, env->spr[SPR_DAWR1]); + REG_ENTRY(DAWRX0, env->spr[SPR_DAWRX0]); + REG_ENTRY(DAWRX1, env->spr[SPR_DAWRX1]); + REG_ENTRY(DPDES, env->spr[SPR_DPDES]); + REG_ENTRY(DSCR, env->spr[SPR_DSCR]); + REG_ENTRY(DSISR, env->spr[SPR_DSISR]); + REG_ENTRY(EBBHR, env->spr[SPR_EBBHR]); + REG_ENTRY(EBBRR, env->spr[SPR_EBBRR]); + + REG_ENTRY(FPSCR, env->fpscr); + REG_ENTRY(FSCR, env->spr[SPR_FSCR]); + + /* Save the GPRs */ + for (int gpr_id = 0; gpr_id < 32; ++gpr_id) { + curr_reg_entry->reg_id = + cpu_to_be64(fadump_gpr_id_to_u64(gpr_id)); + curr_reg_entry->reg_value = + cpu_to_be64(env->gpr[gpr_id]); + ++curr_reg_entry; + ++num_regs_per_cpu; + } + + REG_ENTRY(IAMR, env->spr[SPR_IAMR]); + REG_ENTRY(IC, env->spr[SPR_IC]); + REG_ENTRY(LR, env->spr[SPR_LR]); + + REG_ENTRY(MSR, env->msr); + REG_ENTRY(NIA, env->nip); /* NIA */ + REG_ENTRY(PIR, env->spr[SPR_PIR]); + REG_ENTRY(PSPB, env->spr[SPR_PSPB]); + REG_ENTRY(PVR, env->spr[SPR_PVR]); + REG_ENTRY(RPR, env->spr[SPR_RPR]); + REG_ENTRY(SPURR, env->spr[SPR_SPURR]); + REG_ENTRY(SRR0, env->spr[SPR_SRR0]); + REG_ENTRY(SRR1, env->spr[SPR_SRR1]); + REG_ENTRY(TAR, env->spr[SPR_TAR]); + REG_ENTRY(TEXASR, env->spr[SPR_TEXASR]); + REG_ENTRY(TFHAR, env->spr[SPR_TFHAR]); + REG_ENTRY(TFIAR, env->spr[SPR_TFIAR]); + REG_ENTRY(TIR, env->spr[SPR_TIR]); + REG_ENTRY(UAMOR, env->spr[SPR_UAMOR]); + REG_ENTRY(VRSAVE, env->spr[SPR_VRSAVE]); + REG_ENTRY(VSCR, env->vscr); + REG_ENTRY(VTB, env->spr[SPR_VTB]); + REG_ENTRY(WORT, env->spr[SPR_WORT]); + REG_ENTRY(XER, env->spr[SPR_XER]); + + /* + * Ignoring transaction checkpoint and few other registers + * mentioned in PAPR as not supported in QEMU + */ +#undef REG_ENTRY + + /* End the registers for this CPU with "CPUEND" reg entry */ + curr_reg_entry->reg_id = + cpu_to_be64(fadump_str_to_u64("CPUEND")); + curr_reg_entry->reg_value = cpu_to_be64( + ppc_cpu->vcpu_id & FADUMP_CPU_ID_MASK); + + /* + * Ensure number of register entries saved matches the expected + * 'FADUMP_PER_CPU_REG_ENTRIES' count + * + * This will help catch an error if in future a new register entry + * is added/removed while not modifying FADUMP_PER_CPU_REG_ENTRIES + */ + assert(FADUMP_PER_CPU_REG_ENTRIES == num_regs_per_cpu + 2 /*CPUSTRT+CPUEND*/); + + ++curr_reg_entry; + + return curr_reg_entry; +} + +/* + * Populate the "Register Save Area"/CPU State as mentioned in section "H.1 + * Register Save Area" in PAPR v2.13 + * + * It allocates the buffer for this region, then populates the register + * entries + * + * Returns the pointer to the buffer (which should be deallocated by the + * callers), and sets the size of this buffer in the argument + * 'cpu_state_len' + */ +static void *get_cpu_state_data(uint64_t *cpu_state_len) +{ + FadumpRegSaveAreaHeader reg_save_hdr; + g_autofree FadumpRegEntry *reg_entries = NULL; + FadumpRegEntry *curr_reg_entry; + CPUState *cpu; + + uint32_t num_reg_entries; + uint32_t reg_entries_size; + uint32_t num_cpus = 0; + + void *cpu_state_buffer = NULL; + uint64_t offset = 0; + + CPU_FOREACH(cpu) { + ++num_cpus; + } + + reg_save_hdr.version = cpu_to_be32(0); + reg_save_hdr.magic_number = + cpu_to_be64(fadump_str_to_u64("REGSAVE")); + + /* Reg save area header is immediately followed by num cpus */ + reg_save_hdr.num_cpu_offset = + cpu_to_be32(sizeof(FadumpRegSaveAreaHeader)); + + num_reg_entries = num_cpus * FADUMP_PER_CPU_REG_ENTRIES; + reg_entries_size = num_reg_entries * sizeof(FadumpRegEntry); + + reg_entries = g_new(FadumpRegEntry, num_reg_entries); + + /* Pointer to current CPU's registers */ + curr_reg_entry = reg_entries; + + /* Populate register entries for all CPUs */ + CPU_FOREACH(cpu) { + cpu_synchronize_state(cpu); + curr_reg_entry = populate_cpu_reg_entries(cpu, curr_reg_entry); + } + + *cpu_state_len = 0; + *cpu_state_len += sizeof(reg_save_hdr); /* reg save header */ + *cpu_state_len += 0xc; /* padding as in PAPR */ + *cpu_state_len += sizeof(num_cpus); /* num_cpus */ + *cpu_state_len += reg_entries_size; /* reg entries */ + + cpu_state_buffer = g_malloc(*cpu_state_len); + + memcpy(cpu_state_buffer + offset, + ®_save_hdr, sizeof(reg_save_hdr)); + offset += sizeof(reg_save_hdr); + + /* Write num_cpus */ + num_cpus = cpu_to_be32(num_cpus); + memcpy(cpu_state_buffer + offset, &num_cpus, sizeof(num_cpus)); + offset += sizeof(num_cpus); + + /* Write the register entries */ + memcpy(cpu_state_buffer + offset, reg_entries, reg_entries_size); + offset += reg_entries_size; + + return cpu_state_buffer; +} + +/* + * Save the CPU State Data (aka "Register Save Area") in given region + * + * Region argument is expected to be of CPU_STATE_DATA type + * + * Returns false only in case of Hardware Error, such as failure to + * read/write a valid address. + * + * Otherwise, even in case of unsuccessful copy of CPU state data for reasons + * such as invalid destination address or non-fatal error errors likely + * caused due to invalid parameters, return true and set region->error_flags + */ +static bool do_populate_cpu_state(FadumpSection *region) +{ + uint64_t dest_addr = be64_to_cpu(region->destination_address); + uint64_t cpu_state_len = 0; + g_autofree void *cpu_state_buffer = NULL; + AddressSpace *default_as = &address_space_memory; + MemTxResult io_result; + MemTxAttrs attrs; + + assert(region->source_data_type == cpu_to_be16(FADUMP_CPU_STATE_DATA)); + + /* Mark the memory transaction as privileged memory access */ + attrs.user = 0; + attrs.memory = 1; + + cpu_state_buffer = get_cpu_state_data(&cpu_state_len); + + io_result = address_space_write(default_as, dest_addr, attrs, + cpu_state_buffer, cpu_state_len); + if ((io_result & MEMTX_DECODE_ERROR) || + (io_result & MEMTX_ACCESS_ERROR)) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to decode/access address in CPU State Region's" + " destination address: 0x%016" PRIx64 "\n", dest_addr); + + /* + * Invalid source address is not an hardware error, instead + * wrong parameter from the kernel. + * Return true to let caller know to continue reading other + * sections + */ + region->error_flags = FADUMP_ERROR_INVALID_SOURCE_ADDR; + region->bytes_dumped = 0; + return true; + } else if (io_result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to write CPU state region.\n"); + + return false; + } + + /* + * Set bytes_dumped in cpu state region, so kernel knows platform have + * exported it + */ + region->bytes_dumped = cpu_to_be64(cpu_state_len); + + if (region->source_len != region->bytes_dumped) { + /* + * Log the error, but don't fail the dump collection here, let + * kernel handle the mismatch + */ + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Mismatch in CPU State region's length exported:" + " Kernel expected: 0x%" PRIx64 " bytes," + " QEMU exported: 0x%" PRIx64 " bytes\n", + be64_to_cpu(region->source_len), + be64_to_cpu(region->bytes_dumped)); + } + + return true; +} + +/* + * Preserve the memory locations registered for fadump + * + * Returns false only in case of RTAS_OUT_HW_ERROR, otherwise true + */ +static bool fadump_preserve_mem(SpaprMachineState *spapr) +{ + FadumpMemStruct *fdm = &spapr->registered_fdm; + uint16_t dump_num_sections, data_type; + + assert(spapr->fadump_registered); + + /* + * Handle all sections + * + * CPU State Data and HPTE regions are handled in their own cases + * + * RMR regions and any custom OS reserved regions such as parameter + * save area, are handled by simply copying the source region to + * destination address + */ + dump_num_sections = be16_to_cpu(fdm->header.dump_num_sections); + for (int i = 0; i < dump_num_sections; ++i) { + data_type = be16_to_cpu(fdm->rgn[i].source_data_type); + + /* Reset error_flags & bytes_dumped for now */ + fdm->rgn[i].error_flags = 0; + fdm->rgn[i].bytes_dumped = 0; + + /* If kernel did not request for the memory region, then skip it */ + if (be32_to_cpu(fdm->rgn[i].request_flag) != FADUMP_REQUEST_FLAG) { + qemu_log_mask(LOG_UNIMP, + "FADump: Skipping copying region as not requested\n"); + continue; + } + + switch (data_type) { + case FADUMP_CPU_STATE_DATA: + if (!do_populate_cpu_state(&fdm->rgn[i])) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to store CPU State Data"); + fdm->header.dump_status_flag |= + cpu_to_be16(FADUMP_STATUS_DUMP_ERROR); + + return false; + } + + break; + case FADUMP_HPTE_REGION: + /* TODO: Add hpte state data */ + break; + case FADUMP_REAL_MODE_REGION: + case FADUMP_PARAM_AREA: + /* Copy the memory region from region's source to its destination */ + if (!do_preserve_region(&fdm->rgn[i])) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to preserve dump section: %d\n", + be16_to_cpu(fdm->rgn[i].source_data_type)); + fdm->header.dump_status_flag |= + cpu_to_be16(FADUMP_STATUS_DUMP_ERROR); + } + + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Skipping unknown source data type: %d\n", data_type); + + fdm->rgn[i].error_flags = + cpu_to_be16(FADUMP_ERROR_INVALID_DATA_TYPE); + } + } + + return true; +} + +/* + * Trigger a fadump boot, ie. next boot will be a crashkernel/fadump boot + * with fadump dump active. + * + * This is triggered by ibm,os-term RTAS call, if fadump was registered. + * + * It preserves the memory and sets 'FADUMP_STATUS_DUMP_TRIGGERED' as + * fadump status, which can be used later to add the "ibm,kernel-dump" + * device tree node as presence of 'FADUMP_STATUS_DUMP_TRIGGERED' signifies + * next boot as fadump boot in our case + */ +void trigger_fadump_boot(SpaprMachineState *spapr, target_ulong spapr_retcode) +{ + FadumpSectionHeader *header = &spapr->registered_fdm.header; + + pause_all_vcpus(); + + /* Preserve the memory locations registered for fadump */ + if (!fadump_preserve_mem(spapr)) { + /* Failed to preserve the registered memory regions */ + rtas_st(spapr_retcode, 0, RTAS_OUT_HW_ERROR); + + /* Cause a reboot */ + qemu_system_guest_panicked(NULL); + return; + } + + /* + * Mark next boot as fadump boot + * + * Note: These is some bit of assumption involved here, as PAPR doesn't + * specify any use of the dump status flags, nor does the kernel use it + * + * But from description in Table 136 in PAPR v2.13, it looks like: + * FADUMP_STATUS_DUMP_TRIGGERED + * = Dump was triggered by the previous system boot (PAPR says) + * = Next boot will be a fadump boot (Assumed) + * + * FADUMP_STATUS_DUMP_PERFORMED + * = Dump performed (Set to 0 by caller of the + * ibm,configure-kernel-dump call) (PAPR says) + * = Firmware has performed the copying/dump of requested regions + * (Assumed) + * = Dump is active for the next boot (Assumed) + */ + header->dump_status_flag = cpu_to_be16( + FADUMP_STATUS_DUMP_TRIGGERED | /* Next boot will be fadump boot */ + FADUMP_STATUS_DUMP_PERFORMED /* Dump is active */ + ); + + /* Reset fadump_registered for next boot */ + spapr->fadump_registered = false; + spapr->fadump_dump_active = true; + + /* + * Then do a guest reset + * + * Requirement: + * GUEST_RESET is expected to NOT clear the memory, as is the case when + * this is merged + */ + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + + rtas_st(spapr_retcode, 0, RTAS_OUT_SUCCESS); +} diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 406aea4..8f03b3e 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -8,7 +8,8 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/error-report.h" -#include "exec/tb-flush.h" +#include "exec/translation-block.h" +#include "exec/target_page.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" @@ -300,7 +301,7 @@ static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, if (kvm_enabled()) { kvmppc_icbi_range(cpu, pdst, len); } else if (tcg_enabled()) { - tb_flush(CPU(cpu)); + tb_invalidate_phys_range(CPU(cpu), dst, dst + len - 1); } else { g_assert_not_reached(); } @@ -508,8 +509,8 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, if (!cpu_has_work(cs)) { cs->halted = 1; cs->exception_index = EXCP_HLT; - cs->exit_request = 1; ppc_maybe_interrupt(env); + cpu_exit(cs); } return H_SUCCESS; @@ -530,8 +531,8 @@ static target_ulong h_confer_self(PowerPCCPU *cpu) } cs->halted = 1; cs->exception_index = EXCP_HALTED; - cs->exit_request = 1; ppc_maybe_interrupt(&cpu->env); + cpu_exit(cs); return H_SUCCESS; } @@ -623,8 +624,7 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, } cs->exception_index = EXCP_YIELD; - cs->exit_request = 1; - cpu_loop_exit(cs); + cpu_exit(cs); return H_SUCCESS; } @@ -981,7 +981,6 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ spapr_setup_hpt(spapr); } - return; } #define FLAGS_MASK 0x01FULL @@ -1476,16 +1475,11 @@ static target_ulong h_update_dt(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong dt = ppc64_phys_to_real(args[0]); struct fdt_header hdr = { 0 }; unsigned cb; - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); void *fdt; cpu_physical_memory_read(dt, &hdr, sizeof(hdr)); cb = fdt32_to_cpu(hdr.totalsize); - if (!smc->update_dt_enabled) { - return H_SUCCESS; - } - /* Check that the fdt did not grow out of proportion */ if (cb > spapr->fdt_initial_size * 2) { trace_spapr_update_dt_failed_size(spapr->fdt_initial_size, cb, diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index db3a14c..c2432a0 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -668,7 +668,7 @@ int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, tcet->liobn, 0, tcet->nb_table << tcet->page_shift); } -static void spapr_tce_table_class_init(ObjectClass *klass, void *data) +static void spapr_tce_table_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = spapr_tce_table_realize; @@ -693,7 +693,8 @@ static const TypeInfo spapr_tce_table_info = { .class_init = spapr_tce_table_class_init, }; -static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void spapr_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index d6d368d..fc45a5d 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -33,12 +33,7 @@ static const TypeInfo spapr_intc_info = { static void spapr_irq_msi_init(SpaprMachineState *spapr) { - if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { - /* Legacy mode doesn't use this allocator */ - return; - } - - spapr->irq_map_nr = spapr_irq_nr_msis(spapr); + spapr->irq_map_nr = SPAPR_IRQ_NR_MSIS; spapr->irq_map = bitmap_new(spapr->irq_map_nr); } @@ -282,21 +277,8 @@ void spapr_irq_dt(SpaprMachineState *spapr, uint32_t nr_servers, sicc->dt(spapr->active_intc, nr_servers, fdt, phandle); } -uint32_t spapr_irq_nr_msis(SpaprMachineState *spapr) -{ - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - - if (smc->legacy_irq_allocation) { - return smc->nr_xirqs; - } else { - return SPAPR_XIRQ_BASE + smc->nr_xirqs - SPAPR_IRQ_MSI; - } -} - void spapr_irq_init(SpaprMachineState *spapr, Error **errp) { - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - if (kvm_enabled() && kvm_kernel_irqchip_split()) { error_setg(errp, "kernel_irqchip split mode not supported on pseries"); return; @@ -317,7 +299,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) object_property_add_child(OBJECT(spapr), "ics", obj); object_property_set_link(obj, ICS_PROP_XICS, OBJECT(spapr), &error_abort); - object_property_set_int(obj, "nr-irqs", smc->nr_xirqs, &error_abort); + object_property_set_int(obj, "nr-irqs", SPAPR_NR_XIRQS, &error_abort); if (!qdev_realize(DEVICE(obj), NULL, errp)) { return; } @@ -331,7 +313,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) int i; dev = qdev_new(TYPE_SPAPR_XIVE); - qdev_prop_set_uint32(dev, "nr-irqs", smc->nr_xirqs + SPAPR_IRQ_NR_IPIS); + qdev_prop_set_uint32(dev, "nr-irqs", SPAPR_NR_XIRQS + SPAPR_IRQ_NR_IPIS); /* * 8 XIVE END structures per CPU. One for each available * priority @@ -358,7 +340,7 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) } spapr->qirqs = qemu_allocate_irqs(spapr_set_irq, spapr, - smc->nr_xirqs + SPAPR_IRQ_NR_IPIS); + SPAPR_NR_XIRQS + SPAPR_IRQ_NR_IPIS); /* * Mostly we don't actually need this until reset, except that not @@ -373,11 +355,10 @@ int spapr_irq_claim(SpaprMachineState *spapr, int irq, bool lsi, Error **errp) { SpaprInterruptController *intcs[] = ALL_INTCS(spapr); int i; - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); int rc; assert(irq >= SPAPR_XIRQ_BASE); - assert(irq < (smc->nr_xirqs + SPAPR_XIRQ_BASE)); + assert(irq < (SPAPR_NR_XIRQS + SPAPR_XIRQ_BASE)); for (i = 0; i < ARRAY_SIZE(intcs); i++) { SpaprInterruptController *intc = intcs[i]; @@ -397,10 +378,9 @@ void spapr_irq_free(SpaprMachineState *spapr, int irq, int num) { SpaprInterruptController *intcs[] = ALL_INTCS(spapr); int i, j; - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); assert(irq >= SPAPR_XIRQ_BASE); - assert((irq + num) <= (smc->nr_xirqs + SPAPR_XIRQ_BASE)); + assert((irq + num) <= (SPAPR_NR_XIRQS + SPAPR_XIRQ_BASE)); for (i = irq; i < (irq + num); i++) { for (j = 0; j < ARRAY_SIZE(intcs); j++) { @@ -417,8 +397,6 @@ void spapr_irq_free(SpaprMachineState *spapr, int irq, int num) qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq) { - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - /* * This interface is basically for VIO and PHB devices to find the * right qemu_irq to manipulate, so we only allow access to the @@ -427,7 +405,7 @@ qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq) * interfaces, we can change this if we need to in future. */ assert(irq >= SPAPR_XIRQ_BASE); - assert(irq < (smc->nr_xirqs + SPAPR_XIRQ_BASE)); + assert(irq < (SPAPR_NR_XIRQS + SPAPR_XIRQ_BASE)); if (spapr->ics) { assert(ics_valid_irq(spapr->ics, irq)); @@ -588,11 +566,6 @@ int spapr_irq_find(SpaprMachineState *spapr, int num, bool align, Error **errp) return first + ics->offset; } -SpaprIrq spapr_irq_xics_legacy = { - .xics = true, - .xive = false, -}; - static void spapr_irq_register_types(void) { type_register_static(&spapr_intc_info); diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c index 201f629..10cf634 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" +#include "exec/target_long.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" @@ -1735,7 +1735,6 @@ static void exit_process_output_buffer(SpaprMachineState *spapr, getset_state(spapr, guest, vcpuid, &gsr); address_space_unmap(CPU(cpu)->as, gsb, len, true, len); - return; } static diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 6f875d7..72b4a63 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -235,8 +235,6 @@ void spapr_dt_persistent_memory(SpaprMachineState *spapr, void *fdt) spapr_dt_nvdimm(spapr, fdt, offset, nvdimm); } g_slist_free(nvdimms); - - return; } static target_ulong h_scm_read_metadata(PowerPCCPU *cpu, @@ -890,7 +888,7 @@ static const Property spapr_nvdimm_properties[] = { }; #endif -static void spapr_nvdimm_class_init(ObjectClass *oc, void *data) +static void spapr_nvdimm_class_init(ObjectClass *oc, const void *data) { NVDIMMClass *nvc = NVDIMM_CLASS(oc); diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 88e2953..75ab4fe 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -15,7 +15,8 @@ #include "hw/ppc/spapr_ovec.h" #include "migration/vmstate.h" #include "qemu/bitmap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "trace.h" #include <libfdt.h> diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index e0a9d50..d596a9e 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -34,7 +34,6 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" -#include "exec/ram_addr.h" #include <libfdt.h> #include "trace.h" #include "qemu/error-report.h" @@ -269,7 +268,6 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong args, uint32_t nret, target_ulong rets) { - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); uint32_t config_addr = rtas_ld(args, 0); uint64_t buid = rtas_ldq(args, 1); unsigned int func = rtas_ld(args, 3); @@ -374,13 +372,8 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, SpaprMachineState *spapr, } /* Allocate MSIs */ - if (smc->legacy_irq_allocation) { - irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, - &err); - } else { - irq = spapr_irq_msi_alloc(spapr, req_num, - ret_intr_type == RTAS_TYPE_MSI, &err); - } + irq = spapr_irq_msi_alloc(spapr, req_num, + ret_intr_type == RTAS_TYPE_MSI, &err); if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", config_addr); @@ -394,9 +387,7 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, SpaprMachineState *spapr, if (i) { spapr_irq_free(spapr, irq, i); } - if (!smc->legacy_irq_allocation) { - spapr_irq_msi_free(spapr, irq, req_num); - } + spapr_irq_msi_free(spapr, irq, req_num); error_reportf_err(err, "Can't allocate MSIs for device %x: ", config_addr); rtas_st(rets, 0, RTAS_OUT_HW_ERROR); @@ -1790,12 +1781,9 @@ static void spapr_phb_unrealize(DeviceState *dev) static void spapr_phb_destroy_msi(gpointer opaque) { SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); SpaprPciMsi *msi = opaque; - if (!smc->legacy_irq_allocation) { - spapr_irq_msi_free(spapr, msi->first_irq, msi->num); - } + spapr_irq_msi_free(spapr, msi->first_irq, msi->num); spapr_irq_free(spapr, msi->first_irq, msi->num); g_free(msi); } @@ -1809,7 +1797,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) SpaprMachineState *spapr = (SpaprMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); - SpaprMachineClass *smc = spapr ? SPAPR_MACHINE_GET_CLASS(spapr) : NULL; SysBusDevice *sbd = SYS_BUS_DEVICE(dev); SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(sbd); PCIHostState *phb = PCI_HOST_BRIDGE(sbd); @@ -1957,18 +1944,6 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) for (i = 0; i < PCI_NUM_PINS; i++) { int irq = SPAPR_IRQ_PCI_LSI + sphb->index * PCI_NUM_PINS + i; - if (smc->legacy_irq_allocation) { - irq = spapr_irq_findone(spapr, errp); - if (irq < 0) { - error_prepend(errp, "can't allocate LSIs: "); - /* - * Older machines will never support PHB hotplug, ie, this is an - * init only path and QEMU will terminate. No need to rollback. - */ - return; - } - } - if (spapr_irq_claim(spapr, irq, true, errp) < 0) { error_prepend(errp, "can't allocate LSIs: "); goto unrealize; @@ -2173,7 +2148,7 @@ static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge, return sphb->dtbusname; } -static void spapr_phb_class_init(ObjectClass *klass, void *data) +static void spapr_phb_class_init(ObjectClass *klass, const void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -2200,7 +2175,7 @@ static const TypeInfo spapr_phb_info = { .instance_size = sizeof(SpaprPhbState), .instance_finalize = spapr_phb_finalizefn, .class_init = spapr_phb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } @@ -2304,7 +2279,7 @@ int spapr_dt_phb(SpaprMachineState *spapr, SpaprPhbState *phb, _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", - spapr_irq_nr_msis(spapr))); + SPAPR_IRQ_NR_MSIS)); /* Dynamic DMA window */ if (phb->ddw_enabled) { diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 76b2a34..a748a0b 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -24,7 +24,7 @@ #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" #include "hw/pci/pci_device.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-container-legacy.h" #include "qemu/error-report.h" #include CONFIG_DEVICES /* CONFIG_VFIO_PCI */ @@ -32,7 +32,7 @@ * Interfaces for IBM EEH (Enhanced Error Handling) */ #ifdef CONFIG_VFIO_PCI -static bool vfio_eeh_container_ok(VFIOContainer *container) +static bool vfio_eeh_container_ok(VFIOLegacyContainer *container) { /* * As of 2016-03-04 (linux-4.5) the host kernel EEH/VFIO @@ -60,7 +60,7 @@ static bool vfio_eeh_container_ok(VFIOContainer *container) return true; } -static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) +static int vfio_eeh_container_op(VFIOLegacyContainer *container, uint32_t op) { struct vfio_eeh_pe_op pe_op = { .argsz = sizeof(pe_op), @@ -83,10 +83,10 @@ static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) return ret; } -static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) +static VFIOLegacyContainer *vfio_eeh_as_container(AddressSpace *as) { - VFIOAddressSpace *space = vfio_get_address_space(as); - VFIOContainerBase *bcontainer = NULL; + VFIOAddressSpace *space = vfio_address_space_get(as); + VFIOContainer *bcontainer = NULL; if (QLIST_EMPTY(&space->containers)) { /* No containers to act on */ @@ -105,20 +105,20 @@ static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) } out: - vfio_put_address_space(space); - return container_of(bcontainer, VFIOContainer, bcontainer); + vfio_address_space_put(space); + return VFIO_IOMMU_LEGACY(bcontainer); } static bool vfio_eeh_as_ok(AddressSpace *as) { - VFIOContainer *container = vfio_eeh_as_container(as); + VFIOLegacyContainer *container = vfio_eeh_as_container(as); return (container != NULL) && vfio_eeh_container_ok(container); } static int vfio_eeh_as_op(AddressSpace *as, uint32_t op) { - VFIOContainer *container = vfio_eeh_as_container(as); + VFIOLegacyContainer *container = vfio_eeh_as_container(as); if (!container) { return -ENODEV; diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 95def5b..6fec607 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -136,7 +136,7 @@ static const Property spapr_rng_properties[] = { RngBackend *), }; -static void spapr_rng_class_init(ObjectClass *oc, void *data) +static void spapr_rng_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 78309db..a6715b4 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -221,7 +221,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, cs->halted = 1; ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); - qemu_cpu_kick(cs); + cpu_exit(cs); } static void rtas_ibm_suspend_me(PowerPCCPU *cpu, SpaprMachineState *spapr, @@ -344,6 +344,73 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, rtas_st(rets, 0, ret); } +/* Papr Section 7.4.9 ibm,configure-kernel-dump RTAS call */ +static void rtas_configure_kernel_dump(PowerPCCPU *cpu, + SpaprMachineState *spapr, + uint32_t token, uint32_t nargs, + target_ulong args, + uint32_t nret, target_ulong rets) +{ + target_ulong cmd = rtas_ld(args, 0); + uint32_t ret_val; + + /* Number of outputs has to be 1 */ + if (nret != 1) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: ibm,configure-kernel-dump called with nret != 1.\n"); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + /* Number of inputs has to be 3 */ + if (nargs != 3) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: ibm,configure-kernel-dump called with nargs != 3.\n"); + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + switch (cmd) { + case FADUMP_CMD_REGISTER: + ret_val = do_fadump_register(spapr, args); + if (ret_val != RTAS_OUT_SUCCESS) { + rtas_st(rets, 0, ret_val); + return; + } + break; + case FADUMP_CMD_UNREGISTER: + if (spapr->fadump_dump_active) { + rtas_st(rets, 0, RTAS_OUT_DUMP_ACTIVE); + return; + } + + spapr->fadump_registered = false; + spapr->fadump_dump_active = false; + memset(&spapr->registered_fdm, 0, sizeof(spapr->registered_fdm)); + break; + case FADUMP_CMD_INVALIDATE: + if (!spapr->fadump_dump_active) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Nothing to invalidate, no dump active\n"); + + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + } + + spapr->fadump_registered = false; + spapr->fadump_dump_active = false; + memset(&spapr->registered_fdm, 0, sizeof(spapr->registered_fdm)); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Unknown command: " TARGET_FMT_lu "\n", cmd); + + rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); + return; + } + + rtas_st(rets, 0, RTAS_OUT_SUCCESS); +} + static void rtas_ibm_os_term(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, @@ -353,6 +420,11 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu, target_ulong msgaddr = rtas_ld(args, 0); char msg[512]; + if (spapr->fadump_registered) { + /* If fadump boot works, control won't come back here */ + return trigger_fadump_boot(spapr, rets); + } + cpu_physical_memory_read(msgaddr, msg, sizeof(msg) - 1); msg[sizeof(msg) - 1] = 0; @@ -551,7 +623,7 @@ static uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, return H_PARAMETER; } -static bool spapr_qtest_callback(CharBackend *chr, gchar **words) +static bool spapr_qtest_callback(CharFrontend *chr, gchar **words) { if (strcmp(words[0], "rtas") == 0) { uint64_t res, args, ret; @@ -659,6 +731,10 @@ static void core_rtas_register_types(void) spapr_rtas_register(RTAS_IBM_NMI_INTERLOCK, "ibm,nmi-interlock", rtas_ibm_nmi_interlock); + /* Register fadump rtas call */ + spapr_rtas_register(RTAS_CONFIGURE_KERNEL_DUMP, "ibm,configure-kernel-dump", + rtas_configure_kernel_dump); + qtest_set_command_cb(spapr_qtest_callback); } diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index 46fbc78..1f7d2d8 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -163,7 +163,7 @@ static const VMStateDescription vmstate_spapr_rtc = { }, }; -static void spapr_rtc_class_init(ObjectClass *oc, void *data) +static void spapr_rtc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index ceaa0ac..1297b3a 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -41,8 +41,8 @@ static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args) target_ulong data_in_size = args[2]; uint64_t data_out = ppc64_phys_to_real(args[3]); target_ulong data_out_size = args[4]; - uint8_t buf_in[TPM_SPAPR_BUFSIZE]; - uint8_t buf_out[TPM_SPAPR_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf_in[TPM_SPAPR_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf_out[TPM_SPAPR_BUFSIZE]; ssize_t ret; trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size); @@ -149,7 +149,7 @@ static const Property spapr_tpm_proxy_properties[] = { DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), }; -static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) +static void spapr_tpm_proxy_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 09243c1..c21a2a3 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -50,7 +50,7 @@ static char *spapr_vio_get_dev_name(DeviceState *qdev) return g_strdup_printf("%s@%x", pc->dt_name, dev->reg); } -static void spapr_vio_bus_class_init(ObjectClass *klass, void *data) +static void spapr_vio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -507,15 +507,6 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) dev->irq = spapr_vio_reg_to_irq(dev->reg); - if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { - int irq = spapr_irq_findone(spapr, errp); - - if (irq < 0) { - return; - } - dev->irq = irq; - } - if (spapr_irq_claim(spapr, dev->irq, false, errp) < 0) { return; } @@ -599,7 +590,7 @@ SpaprVioBus *spapr_vio_bus_init(void) return bus; } -static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) +static void spapr_vio_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -631,7 +622,7 @@ const VMStateDescription vmstate_spapr_vio = { }, }; -static void vio_spapr_device_class_init(ObjectClass *klass, void *data) +static void vio_spapr_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = spapr_vio_busdev_realize; diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 17115be..43a6d50 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -146,7 +146,7 @@ static int xilinx_load_device_tree(MachineState *machine, /* Try the local "ppc.dtb" override. */ fdt = load_device_tree("ppc.dtb", &fdt_size); if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + path = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (path) { fdt = load_device_tree(path, &fdt_size); g_free(path); @@ -253,7 +253,7 @@ static void virtex_init(MachineState *machine) /* If we failed loading ELF's try a raw image. */ kernel_size = load_image_targphys(kernel_filename, boot_offset, - machine->ram_size); + machine->ram_size, &error_fatal); boot_info.bootstrap_pc = boot_offset; high = boot_info.bootstrap_pc + kernel_size + 8192; } @@ -264,13 +264,8 @@ static void virtex_init(MachineState *machine) if (machine->initrd_filename) { initrd_base = high = ROUND_UP(high, 4); initrd_size = load_image_targphys(machine->initrd_filename, - high, machine->ram_size - high); - - if (initrd_size < 0) { - error_report("couldn't load ram disk '%s'", - machine->initrd_filename); - exit(1); - } + high, machine->ram_size - high, + &error_fatal); high = ROUND_UP(high + initrd_size, 4); } diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 09cb77d..5ecfc68 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -15,7 +15,7 @@ #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/ppc/vof.h" #include "hw/ppc/fdt.h" #include "system/runstate.h" @@ -353,34 +353,50 @@ static uint32_t vof_nextprop(const void *fdt, uint32_t phandle, { int offset, nodeoff = fdt_node_offset_by_phandle(fdt, phandle); char prev[OF_PROPNAME_LEN_MAX + 1]; - const char *tmp; + const char *tmp = NULL; + bool match = false; if (readstr(prevaddr, prev, sizeof(prev))) { return PROM_ERROR; } - - fdt_for_each_property_offset(offset, fdt, nodeoff) { - if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) { - return 0; + /* + * "name" may or may not be present in fdt but we should still return it. + * Do that first and then skip it if seen later. + */ + if (prev[0] == '\0') { + tmp = "name"; + } else { + if (strcmp(prev, "name") == 0) { + prev[0] = '\0'; } - if (prev[0] == '\0' || strcmp(prev, tmp) == 0) { - if (prev[0] != '\0') { - offset = fdt_next_property_offset(fdt, offset); - if (offset < 0) { - return 0; - } - } + fdt_for_each_property_offset(offset, fdt, nodeoff) { if (!fdt_getprop_by_offset(fdt, offset, &tmp, NULL)) { return 0; } - - if (VOF_MEM_WRITE(nameaddr, tmp, strlen(tmp) + 1) != MEMTX_OK) { - return PROM_ERROR; + if (strcmp(tmp, "name") == 0) { + continue; + } + if (match) { + break; } - return 1; + if (strcmp(prev, tmp) == 0) { + match = true; + continue; + } + if (prev[0] == '\0') { + break; + } + } + if (offset < 0) { + return 0; } } - + if (tmp) { + if (VOF_MEM_WRITE(nameaddr, tmp, strlen(tmp) + 1) != MEMTX_OK) { + return PROM_ERROR; + } + return 1; + } return 0; } |
