diff options
Diffstat (limited to 'hw/ppc')
-rw-r--r-- | hw/ppc/Makefile.objs | 4 | ||||
-rw-r--r-- | hw/ppc/pnv.c | 504 | ||||
-rw-r--r-- | hw/ppc/pnv_bmc.c | 116 | ||||
-rw-r--r-- | hw/ppc/pnv_core.c | 20 | ||||
-rw-r--r-- | hw/ppc/pnv_homer.c | 129 | ||||
-rw-r--r-- | hw/ppc/pnv_lpc.c | 76 | ||||
-rw-r--r-- | hw/ppc/pnv_occ.c | 31 | ||||
-rw-r--r-- | hw/ppc/pnv_pnor.c | 135 | ||||
-rw-r--r-- | hw/ppc/pnv_psi.c | 54 | ||||
-rw-r--r-- | hw/ppc/pnv_xscom.c | 79 | ||||
-rw-r--r-- | hw/ppc/ppc.c | 79 | ||||
-rw-r--r-- | hw/ppc/spapr.c | 133 | ||||
-rw-r--r-- | hw/ppc/spapr_hcall.c | 90 | ||||
-rw-r--r-- | hw/ppc/spapr_irq.c | 30 | ||||
-rw-r--r-- | hw/ppc/spapr_ovec.c | 30 |
15 files changed, 1108 insertions, 402 deletions
diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 580bb4f0dd..101e9fc591 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -9,7 +9,9 @@ obj-$(CONFIG_PSERIES) += spapr_tpm_proxy.o obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o # IBM PowerNV obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o -obj-$(CONFIG_POWERNV) += pnv_homer.o +obj-$(CONFIG_POWERNV) += pnv_homer.o pnv_pnor.o + + ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o spapr_pci_nvlink2.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 627c08e5b9..f77e7ca84e 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -44,6 +44,7 @@ #include "hw/ppc/xics.h" #include "hw/qdev-properties.h" #include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_pnor.h" #include "hw/isa/isa.h" #include "hw/boards.h" @@ -279,14 +280,16 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) { - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); + static const char compat[] = "ibm,power8-xscom\0ibm,xscom"; int i; - pnv_dt_xscom(chip, fdt, 0); + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV_XSCOM_BASE(chip)), + cpu_to_be64(PNV_XSCOM_SIZE), + compat, sizeof(compat)); for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + PnvCore *pnv_core = chip->cores[i]; pnv_dt_core(chip, pnv_core, fdt); @@ -301,14 +304,39 @@ static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) { - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); + static const char compat[] = "ibm,power9-xscom\0ibm,xscom"; + int i; + + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV9_XSCOM_BASE(chip)), + cpu_to_be64(PNV9_XSCOM_SIZE), + compat, sizeof(compat)); + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = chip->cores[i]; + + pnv_dt_core(chip, pnv_core, fdt); + } + + if (chip->ram_size) { + pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); + } + + pnv_dt_lpc(chip, fdt, 0, PNV9_LPCM_BASE(chip), PNV9_LPCM_SIZE); +} + +static void pnv_chip_power10_dt_populate(PnvChip *chip, void *fdt) +{ + static const char compat[] = "ibm,power10-xscom\0ibm,xscom"; int i; - pnv_dt_xscom(chip, fdt, 0); + pnv_dt_xscom(chip, fdt, 0, + cpu_to_be64(PNV10_XSCOM_BASE(chip)), + cpu_to_be64(PNV10_XSCOM_SIZE), + compat, sizeof(compat)); for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + PnvCore *pnv_core = chip->cores[i]; pnv_dt_core(chip, pnv_core, fdt); } @@ -317,7 +345,7 @@ static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); } - pnv_dt_lpc(chip, fdt, 0); + pnv_dt_lpc(chip, fdt, 0, PNV10_LPCM_BASE(chip), PNV10_LPCM_SIZE); } static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off) @@ -456,7 +484,7 @@ static void pnv_dt_isa(PnvMachineState *pnv, void *fdt) &args); } -static void pnv_dt_power_mgt(void *fdt) +static void pnv_dt_power_mgt(PnvMachineState *pnv, void *fdt) { int off; @@ -468,8 +496,7 @@ static void pnv_dt_power_mgt(void *fdt) static void *pnv_dt_create(MachineState *machine) { - const char plat_compat8[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv"; - const char plat_compat9[] = "qemu,powernv9\0ibm,powernv"; + PnvMachineClass *pmc = PNV_MACHINE_GET_CLASS(machine); PnvMachineState *pnv = PNV_MACHINE(machine); void *fdt; char *buf; @@ -479,19 +506,15 @@ static void *pnv_dt_create(MachineState *machine) fdt = g_malloc0(FDT_MAX_SIZE); _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + /* /qemu node */ + _FDT((fdt_add_subnode(fdt, 0, "qemu"))); + /* Root node */ _FDT((fdt_setprop_cell(fdt, 0, "#address-cells", 0x2))); _FDT((fdt_setprop_cell(fdt, 0, "#size-cells", 0x2))); _FDT((fdt_setprop_string(fdt, 0, "model", "IBM PowerNV (emulated by qemu)"))); - if (pnv_is_power9(pnv)) { - _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat9, - sizeof(plat_compat9)))); - } else { - _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat8, - sizeof(plat_compat8)))); - } - + _FDT((fdt_setprop(fdt, 0, "compatible", pmc->compat, pmc->compat_size))); buf = qemu_uuid_unparse_strdup(&qemu_uuid); _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf))); @@ -528,9 +551,9 @@ static void *pnv_dt_create(MachineState *machine) pnv_dt_bmc_sensors(pnv->bmc, fdt); } - /* Create an extra node for power management on Power9 */ - if (pnv_is_power9(pnv)) { - pnv_dt_power_mgt(fdt); + /* Create an extra node for power management on machines that support it */ + if (pmc->dt_power_mgt) { + pmc->dt_power_mgt(pnv, fdt); } return fdt; @@ -547,26 +570,10 @@ static void pnv_powerdown_notify(Notifier *n, void *opaque) static void pnv_reset(MachineState *machine) { - PnvMachineState *pnv = PNV_MACHINE(machine); void *fdt; - Object *obj; qemu_devices_reset(); - /* - * OpenPOWER systems have a BMC, which can be defined on the - * command line with: - * - * -device ipmi-bmc-sim,id=bmc0 - * - * This is the internal simulator but it could also be an external - * BMC. - */ - obj = object_resolve_path_type("", "ipmi-bmc-sim", NULL); - if (obj) { - pnv->bmc = IPMI_BMC(obj); - } - fdt = pnv_dt_create(machine); /* Pack resulting tree */ @@ -594,6 +601,12 @@ static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) return pnv_lpc_isa_create(&chip9->lpc, false, errp); } +static ISABus *pnv_chip_power10_isa_create(PnvChip *chip, Error **errp) +{ + Pnv10Chip *chip10 = PNV10_CHIP(chip); + return pnv_lpc_isa_create(&chip10->lpc, false, errp); +} + static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) { return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); @@ -614,6 +627,24 @@ static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) pnv_psi_pic_print_info(&chip9->psi, mon); } +static uint64_t pnv_chip_power8_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV_XSCOM_EX_BASE(core_id); +} + +static uint64_t pnv_chip_power9_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV9_XSCOM_EC_BASE(core_id); +} + +static uint64_t pnv_chip_power10_xscom_core_base(PnvChip *chip, + uint32_t core_id) +{ + return PNV10_XSCOM_EC_BASE(core_id); +} + static bool pnv_match_cpu(const char *default_type, const char *cpu_type) { PowerPCCPUClass *ppc_default = @@ -624,6 +655,23 @@ static bool pnv_match_cpu(const char *default_type, const char *cpu_type) return ppc_default->pvr_match(ppc_default, ppc->pvr); } +static void pnv_ipmi_bt_init(ISABus *bus, IPMIBmc *bmc, uint32_t irq) +{ + Object *obj; + + obj = OBJECT(isa_create(bus, "isa-ipmi-bt")); + object_property_set_link(obj, OBJECT(bmc), "bmc", &error_fatal); + object_property_set_int(obj, irq, "irq", &error_fatal); + object_property_set_bool(obj, true, "realized", &error_fatal); +} + +static void pnv_chip_power10_pic_print_info(PnvChip *chip, Monitor *mon) +{ + Pnv10Chip *chip10 = PNV10_CHIP(chip); + + pnv_psi_pic_print_info(&chip10->psi, mon); +} + static void pnv_init(MachineState *machine) { PnvMachineState *pnv = PNV_MACHINE(machine); @@ -633,6 +681,8 @@ static void pnv_init(MachineState *machine) long fw_size; int i; char *chip_typename; + DriveInfo *pnor = drive_get(IF_MTD, 0, 0); + DeviceState *dev; /* allocate RAM */ if (machine->ram_size < (1 * GiB)) { @@ -644,6 +694,17 @@ static void pnv_init(MachineState *machine) machine->ram_size); memory_region_add_subregion(get_system_memory(), 0, ram); + /* + * Create our simple PNOR device + */ + dev = qdev_create(NULL, TYPE_PNV_PNOR); + if (pnor) { + qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(pnor), + &error_abort); + } + qdev_init_nofail(dev); + pnv->pnor = PNV_PNOR(dev); + /* load skiboot firmware */ if (bios_name == NULL) { bios_name = FW_FILE_NAME; @@ -733,6 +794,9 @@ static void pnv_init(MachineState *machine) } g_free(chip_typename); + /* Create the machine BMC simulator */ + pnv->bmc = pnv_bmc_create(); + /* Instantiate ISA bus on chip 0 */ pnv->isa_bus = pnv_isa_create(pnv->chips[0], &error_fatal); @@ -742,6 +806,9 @@ static void pnv_init(MachineState *machine) /* Create an RTC ISA device too */ mc146818_rtc_init(pnv->isa_bus, 2000, NULL); + /* Create the IPMI BT device for communication with the BMC */ + pnv_ipmi_bt_init(pnv->isa_bus, pnv->bmc, 10); + /* * OpenPOWER systems use a IPMI SEL Event message to notify the * host to powerdown @@ -794,6 +861,12 @@ static void pnv_chip_power8_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) pnv_cpu->intc = NULL; } +static void pnv_chip_power8_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + Monitor *mon) +{ + icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); +} + /* * 0:48 Reserved - Read as zeroes * 49:52 Node ID @@ -809,6 +882,11 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 8) | (core_id << 2); } +static uint32_t pnv_chip_core_pir_p10(PnvChip *chip, uint32_t core_id) +{ + return (chip->chip_id << 8) | (core_id << 2); +} + static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu, Error **errp) { @@ -846,6 +924,38 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) pnv_cpu->intc = NULL; } +static void pnv_chip_power9_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + Monitor *mon) +{ + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); +} + +static void pnv_chip_power10_intc_create(PnvChip *chip, PowerPCCPU *cpu, + Error **errp) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + /* Will be defined when the interrupt controller is */ + pnv_cpu->intc = NULL; +} + +static void pnv_chip_power10_intc_reset(PnvChip *chip, PowerPCCPU *cpu) +{ + ; +} + +static void pnv_chip_power10_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) +{ + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + pnv_cpu->intc = NULL; +} + +static void pnv_chip_power10_intc_print_info(PnvChip *chip, PowerPCCPU *cpu, + Monitor *mon) +{ +} + /* * Allowed core identifiers on a POWER8 Processor Chip : * @@ -873,6 +983,9 @@ static void pnv_chip_power9_intc_destroy(PnvChip *chip, PowerPCCPU *cpu) */ #define POWER9_CORE_MASK (0xffffffffffffffull) + +#define POWER10_CORE_MASK (0xffffffffffffffull) + static void pnv_chip_power8_instance_init(Object *obj) { Pnv8Chip *chip8 = PNV8_CHIP(obj); @@ -884,26 +997,18 @@ static void pnv_chip_power8_instance_init(Object *obj) object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc), TYPE_PNV8_LPC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->lpc), "psi", - OBJECT(&chip8->psi), &error_abort); object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ), TYPE_PNV8_OCC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->occ), "psi", - OBJECT(&chip8->psi), &error_abort); object_initialize_child(obj, "homer", &chip8->homer, sizeof(chip8->homer), TYPE_PNV8_HOMER, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip8->homer), "chip", obj, - &error_abort); } static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) { PnvChip *chip = PNV_CHIP(chip8); PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); int i, j; char *name; XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); @@ -917,7 +1022,7 @@ static void pnv_chip_icp_realize(Pnv8Chip *chip8, Error **errp) /* Map the ICP registers for each thread */ for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + PnvCore *pnv_core = chip->cores[i]; int core_hwid = CPU_CORE(pnv_core)->core_id; for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { @@ -964,6 +1069,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) &PNV_PSI(psi8)->xscom_regs); /* Create LPC controller */ + object_property_set_link(OBJECT(&chip8->lpc), OBJECT(&chip8->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip8->lpc), true, "realized", &error_fatal); pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs); @@ -983,6 +1090,8 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) } /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip8->occ), OBJECT(&chip8->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip8->occ), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -991,35 +1100,49 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip8->occ.xscom_regs); /* OCC SRAM model */ - memory_region_add_subregion(get_system_memory(), PNV_OCC_COMMON_AREA(chip), + memory_region_add_subregion(get_system_memory(), PNV_OCC_SENSOR_BASE(chip), &chip8->occ.sram_regs); /* HOMER */ + object_property_set_link(OBJECT(&chip8->homer), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(&chip8->homer), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV_XSCOM_PBA_BASE, &chip8->homer.pba_regs); + + /* Homer mmio region */ memory_region_add_subregion(get_system_memory(), PNV_HOMER_BASE(chip), &chip8->homer.regs); } +static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV_XSCOM_SIZE - 1); + return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); +} + static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER8E; k->chip_cfam_id = 0x221ef04980000000ull; /* P8 Murano DD2.1 */ k->cores_mask = POWER8E_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; + k->intc_print_info = pnv_chip_power8_intc_print_info; k->isa_create = pnv_chip_power8_isa_create; k->dt_populate = pnv_chip_power8_dt_populate; k->pic_print_info = pnv_chip_power8_pic_print_info; + k->xscom_core_base = pnv_chip_power8_xscom_core_base; + k->xscom_pcba = pnv_chip_power8_xscom_pcba; dc->desc = "PowerNV Chip POWER8E"; device_class_set_parent_realize(dc, pnv_chip_power8_realize, @@ -1031,16 +1154,18 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER8; k->chip_cfam_id = 0x220ea04980000000ull; /* P8 Venice DD2.0 */ k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; + k->intc_print_info = pnv_chip_power8_intc_print_info; k->isa_create = pnv_chip_power8_isa_create; k->dt_populate = pnv_chip_power8_dt_populate; k->pic_print_info = pnv_chip_power8_pic_print_info; + k->xscom_core_base = pnv_chip_power8_xscom_core_base; + k->xscom_pcba = pnv_chip_power8_xscom_pcba; dc->desc = "PowerNV Chip POWER8"; device_class_set_parent_realize(dc, pnv_chip_power8_realize, @@ -1052,16 +1177,18 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER8NVL; k->chip_cfam_id = 0x120d304980000000ull; /* P8 Naples DD1.0 */ k->cores_mask = POWER8_CORE_MASK; k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->intc_reset = pnv_chip_power8_intc_reset; k->intc_destroy = pnv_chip_power8_intc_destroy; + k->intc_print_info = pnv_chip_power8_intc_print_info; k->isa_create = pnv_chip_power8nvl_isa_create; k->dt_populate = pnv_chip_power8_dt_populate; k->pic_print_info = pnv_chip_power8_pic_print_info; + k->xscom_core_base = pnv_chip_power8_xscom_core_base; + k->xscom_pcba = pnv_chip_power8_xscom_pcba; dc->desc = "PowerNV Chip POWER8NVL"; device_class_set_parent_realize(dc, pnv_chip_power8_realize, @@ -1074,35 +1201,23 @@ static void pnv_chip_power9_instance_init(Object *obj) object_initialize_child(obj, "xive", &chip9->xive, sizeof(chip9->xive), TYPE_PNV_XIVE, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->xive), "chip", obj, - &error_abort); object_initialize_child(obj, "psi", &chip9->psi, sizeof(chip9->psi), TYPE_PNV9_PSI, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->psi), "chip", obj, - &error_abort); object_initialize_child(obj, "lpc", &chip9->lpc, sizeof(chip9->lpc), TYPE_PNV9_LPC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->lpc), "psi", - OBJECT(&chip9->psi), &error_abort); object_initialize_child(obj, "occ", &chip9->occ, sizeof(chip9->occ), TYPE_PNV9_OCC, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->occ), "psi", - OBJECT(&chip9->psi), &error_abort); object_initialize_child(obj, "homer", &chip9->homer, sizeof(chip9->homer), TYPE_PNV9_HOMER, &error_abort, NULL); - object_property_add_const_link(OBJECT(&chip9->homer), "chip", obj, - &error_abort); } static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) { PnvChip *chip = PNV_CHIP(chip9); - const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); int i; chip9->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4); @@ -1111,7 +1226,7 @@ static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) for (i = 0; i < chip9->nr_quads; i++) { char eq_name[32]; PnvQuad *eq = &chip9->quads[i]; - PnvCore *pnv_core = PNV_CORE(chip->cores + (i * 4) * typesize); + PnvCore *pnv_core = chip->cores[i * 4]; int core_id = CPU_CORE(pnv_core)->core_id; snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id); @@ -1163,6 +1278,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) "pc-bar", &error_fatal); object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_TM_BASE(chip), "tm-bar", &error_fatal); + object_property_set_link(OBJECT(&chip9->xive), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(&chip9->xive), true, "realized", &local_err); if (local_err) { @@ -1184,6 +1301,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) &PNV_PSI(psi9)->xscom_regs); /* LPC */ + object_property_set_link(OBJECT(&chip9->lpc), OBJECT(&chip9->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip9->lpc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1196,6 +1315,8 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) (uint64_t) PNV9_LPCM_BASE(chip)); /* Create the simplified OCC model */ + object_property_set_link(OBJECT(&chip9->occ), OBJECT(&chip9->psi), "psi", + &error_abort); object_property_set_bool(OBJECT(&chip9->occ), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -1204,41 +1325,143 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); /* OCC SRAM model */ - memory_region_add_subregion(get_system_memory(), PNV9_OCC_COMMON_AREA(chip), + memory_region_add_subregion(get_system_memory(), PNV9_OCC_SENSOR_BASE(chip), &chip9->occ.sram_regs); /* HOMER */ + object_property_set_link(OBJECT(&chip9->homer), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(&chip9->homer), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); return; } + /* Homer Xscom region */ + pnv_xscom_add_subregion(chip, PNV9_XSCOM_PBA_BASE, &chip9->homer.pba_regs); + + /* Homer mmio region */ memory_region_add_subregion(get_system_memory(), PNV9_HOMER_BASE(chip), &chip9->homer.regs); } +static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV9_XSCOM_SIZE - 1); + return addr >> 3; +} + static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); - k->chip_type = PNV_CHIP_POWER9; k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; k->core_pir = pnv_chip_core_pir_p9; k->intc_create = pnv_chip_power9_intc_create; k->intc_reset = pnv_chip_power9_intc_reset; k->intc_destroy = pnv_chip_power9_intc_destroy; + k->intc_print_info = pnv_chip_power9_intc_print_info; k->isa_create = pnv_chip_power9_isa_create; k->dt_populate = pnv_chip_power9_dt_populate; k->pic_print_info = pnv_chip_power9_pic_print_info; + k->xscom_core_base = pnv_chip_power9_xscom_core_base; + k->xscom_pcba = pnv_chip_power9_xscom_pcba; dc->desc = "PowerNV Chip POWER9"; device_class_set_parent_realize(dc, pnv_chip_power9_realize, &k->parent_realize); } +static void pnv_chip_power10_instance_init(Object *obj) +{ + Pnv10Chip *chip10 = PNV10_CHIP(obj); + + object_initialize_child(obj, "psi", &chip10->psi, sizeof(chip10->psi), + TYPE_PNV10_PSI, &error_abort, NULL); + object_initialize_child(obj, "lpc", &chip10->lpc, sizeof(chip10->lpc), + TYPE_PNV10_LPC, &error_abort, NULL); +} + +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); + Error *local_err = NULL; + + /* XSCOM bridge is first */ + pnv_xscom_realize(chip, PNV10_XSCOM_SIZE, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV10_XSCOM_BASE(chip)); + + pcc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip10->psi), PNV10_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip10->psi), true, "realized", + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_PSIHB_BASE, + &PNV_PSI(&chip10->psi)->xscom_regs); + + /* LPC */ + object_property_set_link(OBJECT(&chip10->lpc), OBJECT(&chip10->psi), "psi", + &error_abort); + object_property_set_bool(OBJECT(&chip10->lpc), true, "realized", + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + memory_region_add_subregion(get_system_memory(), PNV10_LPCM_BASE(chip), + &chip10->lpc.xscom_regs); + + chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", + (uint64_t) PNV10_LPCM_BASE(chip)); +} + +static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + addr &= (PNV10_XSCOM_SIZE - 1); + return addr >> 3; +} + +static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvChipClass *k = PNV_CHIP_CLASS(klass); + + k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */ + k->cores_mask = POWER10_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p10; + k->intc_create = pnv_chip_power10_intc_create; + 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->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; + k->xscom_core_base = pnv_chip_power10_xscom_core_base; + k->xscom_pcba = pnv_chip_power10_xscom_pcba; + dc->desc = "PowerNV Chip POWER10"; + + device_class_set_parent_realize(dc, pnv_chip_power10_realize, + &k->parent_realize); +} + static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); @@ -1275,7 +1498,6 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) Error *error = NULL; PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); const char *typename = pnv_chip_core_typename(chip); - size_t typesize = object_type_get_instance_size(typename); int i, core_hwid; if (!object_class_by_name(typename)) { @@ -1290,21 +1512,24 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) return; } - chip->cores = g_malloc0(typesize * chip->nr_cores); + chip->cores = g_new0(PnvCore *, chip->nr_cores); for (i = 0, core_hwid = 0; (core_hwid < sizeof(chip->cores_mask) * 8) && (i < chip->nr_cores); core_hwid++) { char core_name[32]; - void *pnv_core = chip->cores + i * typesize; + PnvCore *pnv_core; uint64_t xscom_core_base; if (!(chip->cores_mask & (1ull << core_hwid))) { continue; } + pnv_core = PNV_CORE(object_new(typename)); + snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid); - object_initialize_child(OBJECT(chip), core_name, pnv_core, typesize, - typename, &error_fatal, NULL); + object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core), + &error_abort); + chip->cores[i] = pnv_core; object_property_set_int(OBJECT(pnv_core), ms->smp.threads, "nr-threads", &error_fatal); object_property_set_int(OBJECT(pnv_core), core_hwid, @@ -1312,20 +1537,16 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) object_property_set_int(OBJECT(pnv_core), pcc->core_pir(chip, core_hwid), "pir", &error_fatal); - object_property_add_const_link(OBJECT(pnv_core), "chip", - OBJECT(chip), &error_fatal); + object_property_set_link(OBJECT(pnv_core), OBJECT(chip), "chip", + &error_abort); object_property_set_bool(OBJECT(pnv_core), true, "realized", &error_fatal); /* Each core has an XSCOM MMIO region */ - if (!pnv_chip_is_power9(chip)) { - xscom_core_base = PNV_XSCOM_EX_BASE(core_hwid); - } else { - xscom_core_base = PNV9_XSCOM_EC_BASE(core_hwid); - } + xscom_core_base = pcc->xscom_core_base(chip, core_hwid); pnv_xscom_add_subregion(chip, xscom_core_base, - &PNV_CORE(pnv_core)->xscom_regs); + &pnv_core->xscom_regs); i++; } } @@ -1362,6 +1583,23 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data) dc->desc = "PowerNV Chip"; } +PowerPCCPU *pnv_chip_find_cpu(PnvChip *chip, uint32_t pir) +{ + int i, j; + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pc = chip->cores[i]; + CPUCore *cc = CPU_CORE(pc); + + for (j = 0; j < cc->nr_threads; j++) { + if (ppc_cpu_pir(pc->threads[j]) == pir) { + return pc->threads[j]; + } + } + } + return NULL; +} + static ICSState *pnv_ics_get(XICSFabric *xi, int irq) { PnvMachineState *pnv = PNV_MACHINE(xi); @@ -1405,11 +1643,9 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - if (pnv_chip_is_power9(pnv->chips[0])) { - xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); - } else { - icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); - } + /* XXX: loop on each chip/core/thread instead of CPU_FOREACH() */ + PNV_CHIP_GET_CLASS(pnv->chips[0])->intc_print_info(pnv->chips[0], cpu, + mon); } for (i = 0; i < pnv->num_chips; i++) { @@ -1417,6 +1653,49 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, } } +static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + 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, cam_ignore, + priority, logic_serv, match); + + if (count < 0) { + return count; + } + + total_count += count; + } + + return total_count; +} + +PnvChip *pnv_get_chip(uint32_t chip_id) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + PnvChip *chip = pnv->chips[i]; + if (chip->chip_id == chip_id) { + return chip; + } + } + return NULL; +} + static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -1468,6 +1747,8 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv8\0qemu,powernv\0ibm,powernv"; mc->desc = "IBM PowerNV (Non-Virtualized) POWER8"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); @@ -1475,16 +1756,41 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) xic->icp_get = pnv_icp_get; xic->ics_get = pnv_ics_get; xic->ics_resend = pnv_ics_resend; + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); } static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv9\0ibm,powernv"; mc->desc = "IBM PowerNV (Non-Virtualized) POWER9"; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0"); + xfc->match_nvt = pnv_match_nvt; mc->alias = "powernv"; + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); + pmc->dt_power_mgt = pnv_dt_power_mgt; +} + +static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); + static const char compat[] = "qemu,powernv10\0ibm,powernv"; + + mc->desc = "IBM PowerNV (Non-Virtualized) POWER10"; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power10_v1.0"); + + pmc->compat = compat; + pmc->compat_size = sizeof(compat); + pmc->dt_power_mgt = pnv_dt_power_mgt; } static void pnv_machine_class_init(ObjectClass *oc, void *data) @@ -1524,11 +1830,27 @@ static void pnv_machine_class_init(ObjectClass *oc, void *data) .parent = TYPE_PNV9_CHIP, \ } +#define DEFINE_PNV10_CHIP_TYPE(type, class_initfn) \ + { \ + .name = type, \ + .class_init = class_initfn, \ + .parent = TYPE_PNV10_CHIP, \ + } + static const TypeInfo types[] = { { + .name = MACHINE_TYPE_NAME("powernv10"), + .parent = TYPE_PNV_MACHINE, + .class_init = pnv_machine_power10_class_init, + }, + { .name = MACHINE_TYPE_NAME("powernv9"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power9_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_FABRIC }, + { }, + }, }, { .name = MACHINE_TYPE_NAME("powernv8"), @@ -1546,6 +1868,7 @@ static const TypeInfo types[] = { .instance_size = sizeof(PnvMachineState), .instance_init = pnv_machine_instance_init, .class_init = pnv_machine_class_init, + .class_size = sizeof(PnvMachineClass), .interfaces = (InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { }, @@ -1561,6 +1884,17 @@ static const TypeInfo types[] = { }, /* + * P10 chip and variants + */ + { + .name = TYPE_PNV10_CHIP, + .parent = TYPE_PNV_CHIP, + .instance_init = pnv_chip_power10_instance_init, + .instance_size = sizeof(Pnv10Chip), + }, + DEFINE_PNV10_CHIP_TYPE(TYPE_PNV_CHIP_POWER10, pnv_chip_power10_class_init), + + /* * P9 chip and variants */ { diff --git a/hw/ppc/pnv_bmc.c b/hw/ppc/pnv_bmc.c index dc5e918cb7..07fa1e1c7e 100644 --- a/hw/ppc/pnv_bmc.c +++ b/hw/ppc/pnv_bmc.c @@ -17,6 +17,8 @@ */ #include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/error.h" #include "target/ppc/cpu.h" #include "qemu/log.h" #include "hw/ipmi/ipmi.h" @@ -114,3 +116,117 @@ void pnv_dt_bmc_sensors(IPMIBmc *bmc, void *fdt) sdr->sensor_type))); } } + +/* + * HIOMAP protocol handler + */ +#define HIOMAP_C_RESET 1 +#define HIOMAP_C_GET_INFO 2 +#define HIOMAP_C_GET_FLASH_INFO 3 +#define HIOMAP_C_CREATE_READ_WINDOW 4 +#define HIOMAP_C_CLOSE_WINDOW 5 +#define HIOMAP_C_CREATE_WRITE_WINDOW 6 +#define HIOMAP_C_MARK_DIRTY 7 +#define HIOMAP_C_FLUSH 8 +#define HIOMAP_C_ACK 9 +#define HIOMAP_C_ERASE 10 +#define HIOMAP_C_DEVICE_NAME 11 +#define HIOMAP_C_LOCK 12 + +#define BLOCK_SHIFT 12 /* 4K */ + +static uint16_t bytes_to_blocks(uint32_t bytes) +{ + return bytes >> BLOCK_SHIFT; +} + +static void hiomap_cmd(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + PnvPnor *pnor = pnv->pnor; + uint32_t pnor_size = pnor->size; + uint32_t pnor_addr = PNOR_SPI_OFFSET; + bool readonly = false; + + rsp_buffer_push(rsp, cmd[2]); + rsp_buffer_push(rsp, cmd[3]); + + switch (cmd[2]) { + case HIOMAP_C_MARK_DIRTY: + case HIOMAP_C_FLUSH: + case HIOMAP_C_ERASE: + case HIOMAP_C_ACK: + break; + + case HIOMAP_C_GET_INFO: + rsp_buffer_push(rsp, 2); /* Version 2 */ + rsp_buffer_push(rsp, BLOCK_SHIFT); /* block size */ + rsp_buffer_push(rsp, 0); /* Timeout */ + rsp_buffer_push(rsp, 0); /* Timeout */ + break; + + case HIOMAP_C_GET_FLASH_INFO: + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x01); /* erase size */ + rsp_buffer_push(rsp, 0x00); /* erase size */ + break; + + case HIOMAP_C_CREATE_READ_WINDOW: + readonly = true; + /* Fall through */ + + case HIOMAP_C_CREATE_WRITE_WINDOW: + memory_region_set_readonly(&pnor->mmio, readonly); + memory_region_set_enabled(&pnor->mmio, true); + + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_addr) >> 8); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) & 0xFF); + rsp_buffer_push(rsp, bytes_to_blocks(pnor_size) >> 8); + rsp_buffer_push(rsp, 0x00); /* offset */ + rsp_buffer_push(rsp, 0x00); /* offset */ + break; + + case HIOMAP_C_CLOSE_WINDOW: + memory_region_set_enabled(&pnor->mmio, false); + break; + + case HIOMAP_C_DEVICE_NAME: + case HIOMAP_C_RESET: + case HIOMAP_C_LOCK: + default: + qemu_log_mask(LOG_GUEST_ERROR, "HIOMAP: unknow command %02X\n", cmd[2]); + break; + } +} + +#define HIOMAP 0x5a + +static const IPMICmdHandler hiomap_cmds[] = { + [HIOMAP] = { hiomap_cmd, 3 }, +}; + +static const IPMINetfn hiomap_netfn = { + .cmd_nums = ARRAY_SIZE(hiomap_cmds), + .cmd_handlers = hiomap_cmds +}; + +/* + * Instantiate the machine BMC. PowerNV uses the QEMU internal + * simulator but it could also be external. + */ +IPMIBmc *pnv_bmc_create(void) +{ + Object *obj; + + obj = object_new(TYPE_IPMI_BMC_SIMULATOR); + object_property_set_bool(obj, true, "realized", &error_fatal); + + /* Install the HIOMAP protocol handlers to access the PNOR */ + ipmi_sim_register_netfn(IPMI_BMC_SIMULATOR(obj), IPMI_NETFN_OEM, + &hiomap_netfn); + + return IPMI_BMC(obj); +} diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 61b3d3ce22..2651044278 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -217,15 +217,8 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) void *obj; int i, j; char name[32]; - Object *chip; - chip = object_property_get_link(OBJECT(dev), "chip", &local_err); - if (!chip) { - error_propagate_prepend(errp, local_err, - "required link 'chip' not found: "); - return; - } - pc->chip = PNV_CHIP(chip); + assert(pc->chip); pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { @@ -254,6 +247,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id); + /* TODO: check PNV_XSCOM_EX_SIZE for p10 */ pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops, pc, name, PNV_XSCOM_EX_SIZE); @@ -297,6 +291,7 @@ static void pnv_core_unrealize(DeviceState *dev, Error **errp) static Property pnv_core_properties[] = { DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), + DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), DEFINE_PROP_END_OF_LIST(), }; @@ -314,6 +309,14 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data) pcc->xscom_ops = &pnv_core_power9_xscom_ops; } +static void pnv_core_power10_class_init(ObjectClass *oc, void *data) +{ + PnvCoreClass *pcc = PNV_CORE_CLASS(oc); + + /* TODO: Use the P9 XSCOMs for now on P10 */ + pcc->xscom_ops = &pnv_core_power9_xscom_ops; +} + static void pnv_core_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -343,6 +346,7 @@ static const TypeInfo pnv_core_infos[] = { DEFINE_PNV_CORE_TYPE(power8, "power8_v2.0"), DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"), DEFINE_PNV_CORE_TYPE(power9, "power9_v2.0"), + DEFINE_PNV_CORE_TYPE(power10, "power10_v1.0"), }; DEFINE_TYPES(pnv_core_infos) diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index cc881a3b32..a08b7914f7 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -17,13 +17,16 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "exec/hwaddr.h" #include "exec/memory.h" #include "sysemu/cpus.h" #include "hw/qdev-core.h" +#include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_homer.h" +#include "hw/ppc/pnv_xscom.h" static bool core_max_array(PnvHomer *homer, hwaddr addr) @@ -113,10 +116,67 @@ static const MemoryRegionOps pnv_power8_homer_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +/* P8 PBA BARs */ +#define PBA_BAR0 0x00 +#define PBA_BAR1 0x01 +#define PBA_BAR2 0x02 +#define PBA_BAR3 0x03 +#define PBA_BARMASK0 0x04 +#define PBA_BARMASK1 0x05 +#define PBA_BARMASK2 0x06 +#define PBA_BARMASK3 0x07 + +static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvHomer *homer = PNV_HOMER(opaque); + PnvChip *chip = homer->chip; + uint32_t reg = addr >> 3; + uint64_t val = 0; + + switch (reg) { + case PBA_BAR0: + val = PNV_HOMER_BASE(chip); + break; + case PBA_BARMASK0: /* P8 homer region mask */ + val = (PNV_HOMER_SIZE - 1) & 0x300000; + break; + case PBA_BAR3: /* P8 occ common area */ + val = PNV_OCC_COMMON_AREA_BASE; + break; + case PBA_BARMASK3: /* P8 occ common area mask */ + val = (PNV_OCC_COMMON_AREA_SIZE - 1) & 0x700000; + break; + default: + qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + return val; +} + +static void pnv_homer_power8_pba_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); +} + +static const MemoryRegionOps pnv_homer_power8_pba_ops = { + .read = pnv_homer_power8_pba_read, + .write = pnv_homer_power8_pba_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->pba_size = PNV_XSCOM_PBA_SIZE; + homer->pba_ops = &pnv_homer_power8_pba_ops; homer->homer_size = PNV_HOMER_SIZE; homer->homer_ops = &pnv_power8_homer_ops; homer->core_max_base = PNV8_CORE_MAX_BASE; @@ -209,10 +269,57 @@ static const MemoryRegionOps pnv_power9_homer_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvHomer *homer = PNV_HOMER(opaque); + PnvChip *chip = homer->chip; + uint32_t reg = addr >> 3; + uint64_t val = 0; + + switch (reg) { + case PBA_BAR0: + val = PNV9_HOMER_BASE(chip); + break; + case PBA_BARMASK0: /* P9 homer region mask */ + val = (PNV9_HOMER_SIZE - 1) & 0x300000; + break; + case PBA_BAR2: /* P9 occ common area */ + val = PNV9_OCC_COMMON_AREA_BASE; + break; + case PBA_BARMASK2: /* P9 occ common area size */ + val = (PNV9_OCC_COMMON_AREA_SIZE - 1) & 0x700000; + break; + default: + qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + return val; +} + +static void pnv_homer_power9_pba_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); +} + +static const MemoryRegionOps pnv_homer_power9_pba_ops = { + .read = pnv_homer_power9_pba_read, + .write = pnv_homer_power9_pba_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); + homer->pba_size = PNV9_XSCOM_PBA_SIZE; + homer->pba_ops = &pnv_homer_power9_pba_ops; homer->homer_size = PNV9_HOMER_SIZE; homer->homer_ops = &pnv_power9_homer_ops; homer->core_max_base = PNV9_CORE_MAX_BASE; @@ -229,28 +336,30 @@ static void pnv_homer_realize(DeviceState *dev, Error **errp) { PnvHomer *homer = PNV_HOMER(dev); PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); - Object *obj; - Error *local_err = NULL; - - obj = object_property_get_link(OBJECT(dev), "chip", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'chip' not found: "); - return; - } - homer->chip = PNV_CHIP(obj); + + assert(homer->chip); + + pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops, + homer, "xscom-pba", hmrc->pba_size); + /* homer region */ memory_region_init_io(&homer->regs, OBJECT(dev), hmrc->homer_ops, homer, "homer-main-memory", hmrc->homer_size); } +static Property pnv_homer_properties[] = { + DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_homer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_homer_realize; dc->desc = "PowerNV HOMER Memory"; + dc->props = pnv_homer_properties; } static const TypeInfo pnv_homer_type_info = { diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index 9466d4a1be..18256d9ba3 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -24,7 +24,7 @@ #include "qemu/module.h" #include "hw/irq.h" #include "hw/isa/isa.h" - +#include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_xscom.h" @@ -86,7 +86,7 @@ enum { #define ISA_FW_SIZE 0x10000000 #define LPC_IO_OPB_ADDR 0xd0010000 #define LPC_IO_OPB_SIZE 0x00010000 -#define LPC_MEM_OPB_ADDR 0xe0010000 +#define LPC_MEM_OPB_ADDR 0xe0000000 #define LPC_MEM_OPB_SIZE 0x10000000 #define LPC_FW_OPB_ADDR 0xf0000000 #define LPC_FW_OPB_SIZE 0x10000000 @@ -122,26 +122,36 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) } /* POWER9 only */ -int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset) +int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset, uint64_t lpcm_addr, + uint64_t lpcm_size) { const char compat[] = "ibm,power9-lpcm-opb\0simple-bus"; const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc"; char *name; int offset, lpcm_offset; - uint64_t lpcm_addr = PNV9_LPCM_BASE(chip); uint32_t opb_ranges[8] = { 0, cpu_to_be32(lpcm_addr >> 32), cpu_to_be32((uint32_t)lpcm_addr), - cpu_to_be32(PNV9_LPCM_SIZE / 2), - cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(lpcm_size / 2), + cpu_to_be32(lpcm_size / 2), cpu_to_be32(lpcm_addr >> 32), - cpu_to_be32(PNV9_LPCM_SIZE / 2), - cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(lpcm_size / 2), + cpu_to_be32(lpcm_size / 2), }; uint32_t opb_reg[4] = { cpu_to_be32(lpcm_addr >> 32), cpu_to_be32((uint32_t)lpcm_addr), - cpu_to_be32(PNV9_LPCM_SIZE >> 32), - cpu_to_be32((uint32_t)PNV9_LPCM_SIZE), + cpu_to_be32(lpcm_size >> 32), + cpu_to_be32((uint32_t)lpcm_size), + }; + uint32_t lpc_ranges[12] = { 0, 0, + cpu_to_be32(LPC_MEM_OPB_ADDR), + cpu_to_be32(LPC_MEM_OPB_SIZE), + cpu_to_be32(1), 0, + cpu_to_be32(LPC_IO_OPB_ADDR), + cpu_to_be32(LPC_IO_OPB_SIZE), + cpu_to_be32(3), 0, + cpu_to_be32(LPC_FW_OPB_ADDR), + cpu_to_be32(LPC_FW_OPB_SIZE), }; uint32_t reg[2]; @@ -211,6 +221,8 @@ int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset) _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat, sizeof(lpc_compat)))); + _FDT((fdt_setprop(fdt, offset, "ranges", lpc_ranges, + sizeof(lpc_ranges)))); return 0; } @@ -679,20 +691,24 @@ 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) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "PowerNV LPC Controller POWER10"; +} + +static const TypeInfo pnv_lpc_power10_info = { + .name = TYPE_PNV10_LPC, + .parent = TYPE_PNV9_LPC, + .class_init = pnv_lpc_power10_class_init, +}; + static void pnv_lpc_realize(DeviceState *dev, Error **errp) { PnvLpcController *lpc = PNV_LPC(dev); - Object *obj; - Error *local_err = NULL; - obj = object_property_get_link(OBJECT(dev), "psi", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'psi' not found: "); - return; - } - /* The LPC controller needs PSI to generate interrupts */ - lpc->psi = PNV_PSI(obj); + assert(lpc->psi); /* Reg inits */ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; @@ -734,12 +750,18 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) &lpc->lpc_hc_regs); } +static Property pnv_lpc_properties[] = { + DEFINE_PROP_LINK("psi", PnvLpcController, psi, TYPE_PNV_PSI, PnvPsi *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_lpc_realize; dc->desc = "PowerNV LPC Controller"; + dc->props = pnv_lpc_properties; } static const TypeInfo pnv_lpc_info = { @@ -755,6 +777,7 @@ static void pnv_lpc_register_types(void) type_register_static(&pnv_lpc_info); type_register_static(&pnv_lpc_power8_info); type_register_static(&pnv_lpc_power9_info); + type_register_static(&pnv_lpc_power10_info); } type_init(pnv_lpc_register_types) @@ -801,6 +824,7 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) ISABus *isa_bus; qemu_irq *irqs; qemu_irq_handler handler; + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); /* let isa_bus_new() create its own bridge on SysBus otherwise * devices speficied on the command line won't find the bus and @@ -825,5 +849,17 @@ ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp) irqs = qemu_allocate_irqs(handler, lpc, ISA_NUM_IRQS); isa_bus_irqs(isa_bus, irqs); + + /* + * TODO: Map PNOR on the LPC FW address space on demand ? + */ + memory_region_add_subregion(&lpc->isa_fw, PNOR_SPI_OFFSET, + &pnv->pnor->mmio); + /* + * Start disabled. The HIOMAP protocol will activate the mapping + * with HIOMAP_C_CREATE_WRITE_WINDOW + */ + memory_region_set_enabled(&pnv->pnor->mmio, false); + return isa_bus; } diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 785653bb67..924fdabc9e 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/module.h" - +#include "hw/qdev-properties.h" #include "hw/ppc/pnv.h" #include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_occ.h" @@ -167,9 +167,7 @@ static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) PnvOCCClass *poc = PNV_OCC_CLASS(klass); poc->xscom_size = PNV_XSCOM_OCC_SIZE; - poc->sram_size = PNV_OCC_COMMON_AREA_SIZE; poc->xscom_ops = &pnv_occ_power8_xscom_ops; - poc->sram_ops = &pnv_occ_sram_ops; poc->psi_irq = PSIHB_IRQ_OCC; } @@ -240,9 +238,7 @@ static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) PnvOCCClass *poc = PNV_OCC_CLASS(klass); poc->xscom_size = PNV9_XSCOM_OCC_SIZE; - poc->sram_size = PNV9_OCC_COMMON_AREA_SIZE; poc->xscom_ops = &pnv_occ_power9_xscom_ops; - poc->sram_ops = &pnv_occ_sram_ops; poc->psi_irq = PSIHB9_IRQ_OCC; } @@ -257,34 +253,33 @@ static void pnv_occ_realize(DeviceState *dev, Error **errp) { PnvOCC *occ = PNV_OCC(dev); PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); - Object *obj; - Error *local_err = NULL; - occ->occmisc = 0; + assert(occ->psi); - obj = object_property_get_link(OBJECT(dev), "psi", &local_err); - if (!obj) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'psi' not found: "); - return; - } - occ->psi = PNV_PSI(obj); + occ->occmisc = 0; /* XScom region for OCC registers */ pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), poc->xscom_ops, occ, "xscom-occ", poc->xscom_size); - /* XScom region for OCC SRAM registers */ - pnv_xscom_region_init(&occ->sram_regs, OBJECT(dev), poc->sram_ops, - occ, "occ-common-area", poc->sram_size); + /* OCC common area mmio region for OCC SRAM registers */ + memory_region_init_io(&occ->sram_regs, OBJECT(dev), &pnv_occ_sram_ops, + occ, "occ-common-area", + PNV_OCC_SENSOR_DATA_BLOCK_SIZE); } +static Property pnv_occ_properties[] = { + DEFINE_PROP_LINK("psi", PnvOCC, psi, TYPE_PNV_PSI, PnvPsi *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pnv_occ_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_occ_realize; dc->desc = "PowerNV OCC Controller"; + dc->props = pnv_occ_properties; } static const TypeInfo pnv_occ_type_info = { diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c new file mode 100644 index 0000000000..bfb1e92b03 --- /dev/null +++ b/hw/ppc/pnv_pnor.c @@ -0,0 +1,135 @@ +/* + * QEMU PowerNV PNOR simple model + * + * Copyright (c) 2015-2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "sysemu/block-backend.h" +#include "sysemu/blockdev.h" +#include "hw/loader.h" +#include "hw/ppc/pnv_pnor.h" +#include "hw/qdev-properties.h" + +static uint64_t pnv_pnor_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPnor *s = PNV_PNOR(opaque); + uint64_t ret = 0; + int i; + + for (i = 0; i < size; i++) { + ret |= (uint64_t) s->storage[addr + i] << (8 * (size - i - 1)); + } + + return ret; +} + +static void pnv_pnor_update(PnvPnor *s, int offset, int size) +{ + int offset_end; + + if (s->blk) { + return; + } + + offset_end = offset + size; + offset = QEMU_ALIGN_DOWN(offset, BDRV_SECTOR_SIZE); + offset_end = QEMU_ALIGN_UP(offset_end, BDRV_SECTOR_SIZE); + + blk_pwrite(s->blk, offset, s->storage + offset, + offset_end - offset, 0); +} + +static void pnv_pnor_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + PnvPnor *s = PNV_PNOR(opaque); + int i; + + for (i = 0; i < size; i++) { + s->storage[addr + i] = (data >> (8 * (size - i - 1))) & 0xFF; + } + pnv_pnor_update(s, addr, size); +} + +/* + * TODO: Check endianness: skiboot is BIG, Aspeed AHB is LITTLE, flash + * is BIG. + */ +static const MemoryRegionOps pnv_pnor_ops = { + .read = pnv_pnor_read, + .write = pnv_pnor_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static void pnv_pnor_realize(DeviceState *dev, Error **errp) +{ + PnvPnor *s = PNV_PNOR(dev); + int ret; + + if (s->blk) { + uint64_t perm = BLK_PERM_CONSISTENT_READ | + (blk_is_read_only(s->blk) ? 0 : BLK_PERM_WRITE); + ret = blk_set_perm(s->blk, perm, BLK_PERM_ALL, errp); + if (ret < 0) { + return; + } + + s->size = blk_getlength(s->blk); + if (s->size <= 0) { + error_setg(errp, "failed to get flash size"); + return; + } + + s->storage = blk_blockalign(s->blk, s->size); + + if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) { + error_setg(errp, "failed to read the initial flash content"); + return; + } + } else { + s->storage = blk_blockalign(NULL, s->size); + memset(s->storage, 0xFF, s->size); + } + + memory_region_init_io(&s->mmio, OBJECT(s), &pnv_pnor_ops, s, + TYPE_PNV_PNOR, s->size); +} + +static Property pnv_pnor_properties[] = { + DEFINE_PROP_UINT32("size", PnvPnor, size, 128 << 20), + DEFINE_PROP_DRIVE("drive", PnvPnor, blk), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_pnor_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_pnor_realize; + dc->props = pnv_pnor_properties; +} + +static const TypeInfo pnv_pnor_info = { + .name = TYPE_PNV_PNOR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PnvPnor), + .class_init = pnv_pnor_class_init, +}; + +static void pnv_pnor_register_types(void) +{ + type_register_static(&pnv_pnor_info); +} + +type_init(pnv_pnor_register_types) diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 68d0dfacfe..75e20d9da0 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -497,8 +497,7 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) } /* Create PSI interrupt control source */ - object_property_add_const_link(OBJECT(ics), ICS_PROP_XICS, obj, - &error_abort); + object_property_set_link(OBJECT(ics), obj, ICS_PROP_XICS, &error_abort); object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); if (err) { error_propagate(errp, err); @@ -537,9 +536,6 @@ static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) qemu_register_reset(pnv_psi_reset, dev); } -static const char compat_p8[] = "ibm,power8-psihb-x\0ibm,psihb-x"; -static const char compat_p9[] = "ibm,power9-psihb-x\0ibm,psihb-x"; - static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) { PnvPsiClass *ppc = PNV_PSI_GET_CLASS(dev); @@ -558,13 +554,8 @@ static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) _FDT(fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))); _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", 2)); _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", 1)); - if (ppc->chip_type == PNV_CHIP_POWER9) { - _FDT(fdt_setprop(fdt, offset, "compatible", compat_p9, - sizeof(compat_p9))); - } else { - _FDT(fdt_setprop(fdt, offset, "compatible", compat_p8, - sizeof(compat_p8))); - } + _FDT(fdt_setprop(fdt, offset, "compatible", ppc->compat, + ppc->compat_size)); return 0; } @@ -578,15 +569,17 @@ static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + static const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; dc->desc = "PowerNV PSI Controller POWER8"; dc->realize = pnv_psi_power8_realize; - ppc->chip_type = PNV_CHIP_POWER8; ppc->xscom_pcba = PNV_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV_XSCOM_PSIHB_SIZE; ppc->bar_mask = PSIHB_BAR_MASK; ppc->irq_set = pnv_psi_power8_irq_set; + ppc->compat = compat; + ppc->compat_size = sizeof(compat); } static const TypeInfo pnv_psi_power8_info = { @@ -609,9 +602,12 @@ static const TypeInfo pnv_psi_power8_info = { #define PSIHB9_IRQ_METHOD PPC_BIT(0) #define PSIHB9_IRQ_RESET PPC_BIT(1) #define PSIHB9_ESB_CI_BASE 0x60 -#define PSIHB9_ESB_CI_VALID 1 +#define PSIHB9_ESB_CI_64K PPC_BIT(1) +#define PSIHB9_ESB_CI_ADDR_MASK PPC_BITMASK(8, 47) +#define PSIHB9_ESB_CI_VALID PPC_BIT(63) #define PSIHB9_ESB_NOTIF_ADDR 0x68 -#define PSIHB9_ESB_NOTIF_VALID 1 +#define PSIHB9_ESB_NOTIF_ADDR_MASK PPC_BITMASK(8, 60) +#define PSIHB9_ESB_NOTIF_VALID PPC_BIT(63) #define PSIHB9_IVT_OFFSET 0x70 #define PSIHB9_IVT_OFF_SHIFT 32 @@ -851,8 +847,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) &error_fatal); object_property_set_int(OBJECT(xsrc), PSIHB9_NUM_IRQS, "nr-irqs", &error_fatal); - object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(psi), - &error_fatal); + object_property_set_link(OBJECT(xsrc), OBJECT(psi), "xive", &error_abort); object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -883,15 +878,17 @@ static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); + static const char compat[] = "ibm,power9-psihb-x\0ibm,psihb-x"; dc->desc = "PowerNV PSI Controller POWER9"; dc->realize = pnv_psi_power9_realize; - ppc->chip_type = PNV_CHIP_POWER9; ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE; ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE; ppc->bar_mask = PSIHB9_BAR_MASK; ppc->irq_set = pnv_psi_power9_irq_set; + ppc->compat = compat; + ppc->compat_size = sizeof(compat); xfc->notify = pnv_psi_notify; } @@ -908,6 +905,26 @@ static const TypeInfo pnv_psi_power9_info = { }, }; +static void pnv_psi_power10_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + static const char compat[] = "ibm,power10-psihb-x\0ibm,psihb-x"; + + dc->desc = "PowerNV PSI Controller POWER10"; + + ppc->xscom_pcba = PNV10_XSCOM_PSIHB_BASE; + ppc->xscom_size = PNV10_XSCOM_PSIHB_SIZE; + ppc->compat = compat; + ppc->compat_size = sizeof(compat); +} + +static const TypeInfo pnv_psi_power10_info = { + .name = TYPE_PNV10_PSI, + .parent = TYPE_PNV9_PSI, + .class_init = pnv_psi_power10_class_init, +}; + static void pnv_psi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -937,6 +954,7 @@ static void pnv_psi_register_types(void) type_register_static(&pnv_psi_info); type_register_static(&pnv_psi_power8_info); type_register_static(&pnv_psi_power9_info); + type_register_static(&pnv_psi_power10_info); } type_init(pnv_psi_register_types); diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index f01d788a65..b681c72575 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -36,16 +36,6 @@ #define PRD_P9_IPOLL_REG_MASK 0x000F0033 #define PRD_P9_IPOLL_REG_STATUS 0x000F0034 -/* PBA BARs */ -#define P8_PBA_BAR0 0x2013f00 -#define P8_PBA_BAR2 0x2013f02 -#define P8_PBA_BARMASK0 0x2013f04 -#define P8_PBA_BARMASK2 0x2013f06 -#define P9_PBA_BAR0 0x5012b00 -#define P9_PBA_BAR2 0x5012b02 -#define P9_PBA_BARMASK0 0x5012b04 -#define P9_PBA_BARMASK2 0x5012b06 - static void xscom_complete(CPUState *cs, uint64_t hmer_bits) { /* @@ -67,13 +57,7 @@ static void xscom_complete(CPUState *cs, uint64_t hmer_bits) static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr) { - addr &= (PNV_XSCOM_SIZE - 1); - - if (pnv_chip_is_power9(chip)) { - return addr >> 3; - } else { - return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); - } + return PNV_CHIP_GET_CLASS(chip)->xscom_pcba(chip, addr); } static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) @@ -84,26 +68,6 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case 0x18002: /* ECID2 */ return 0; - case P9_PBA_BAR0: - return PNV9_HOMER_BASE(chip); - case P8_PBA_BAR0: - return PNV_HOMER_BASE(chip); - - case P9_PBA_BARMASK0: /* P9 homer region size */ - return PNV9_HOMER_SIZE; - case P8_PBA_BARMASK0: /* P8 homer region size */ - return PNV_HOMER_SIZE; - - case P9_PBA_BAR2: /* P9 occ common area */ - return PNV9_OCC_COMMON_AREA(chip); - case P8_PBA_BAR2: /* P8 occ common area */ - return PNV_OCC_COMMON_AREA(chip); - - case P9_PBA_BARMASK2: /* P9 occ common area size */ - return PNV9_OCC_COMMON_AREA_SIZE; - case P8_PBA_BARMASK2: /* P8 occ common area size */ - return PNV_OCC_COMMON_AREA_SIZE; - case 0x1010c00: /* PIBAM FIR */ case 0x1010c03: /* PIBAM FIR MASK */ @@ -124,9 +88,7 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) case 0x202000f: /* ADU stuff, receive status register*/ return 0; case 0x2013f01: /* PBA stuff */ - case 0x2013f03: /* PBA stuff */ case 0x2013f05: /* PBA stuff */ - case 0x2013f07: /* PBA stuff */ return 0; case 0x2013028: /* CAPP stuff */ case 0x201302a: /* CAPP stuff */ @@ -298,31 +260,25 @@ static int xscom_dt_child(Object *child, void *opaque) PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child); PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd); - if (xc->dt_xscom) { + /* + * Only "realized" devices should be configured in the DT + */ + if (xc->dt_xscom && DEVICE(child)->realized) { _FDT((xc->dt_xscom(xd, args->fdt, args->xscom_offset))); } } return 0; } -static const char compat_p8[] = "ibm,power8-xscom\0ibm,xscom"; -static const char compat_p9[] = "ibm,power9-xscom\0ibm,xscom"; - -int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset) +int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset, + uint64_t xscom_base, uint64_t xscom_size, + const char *compat, int compat_size) { - uint64_t reg[2]; + uint64_t reg[] = { xscom_base, xscom_size }; int xscom_offset; ForeachPopulateArgs args; char *name; - if (pnv_chip_is_power9(chip)) { - reg[0] = cpu_to_be64(PNV9_XSCOM_BASE(chip)); - reg[1] = cpu_to_be64(PNV9_XSCOM_SIZE); - } else { - reg[0] = cpu_to_be64(PNV_XSCOM_BASE(chip)); - reg[1] = cpu_to_be64(PNV_XSCOM_SIZE); - } - name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0])); xscom_offset = fdt_add_subnode(fdt, root_offset, name); _FDT(xscom_offset); @@ -331,21 +287,18 @@ int pnv_dt_xscom(PnvChip *chip, void *fdt, int root_offset) _FDT((fdt_setprop_cell(fdt, xscom_offset, "#address-cells", 1))); _FDT((fdt_setprop_cell(fdt, xscom_offset, "#size-cells", 1))); _FDT((fdt_setprop(fdt, xscom_offset, "reg", reg, sizeof(reg)))); - - if (pnv_chip_is_power9(chip)) { - _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p9, - sizeof(compat_p9)))); - } else { - _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat_p8, - sizeof(compat_p8)))); - } - + _FDT((fdt_setprop(fdt, xscom_offset, "compatible", compat, compat_size))); _FDT((fdt_setprop(fdt, xscom_offset, "scom-controller", NULL, 0))); args.fdt = fdt; args.xscom_offset = xscom_offset; - object_child_foreach(OBJECT(chip), xscom_dt_child, &args); + /* + * Loop on the whole object hierarchy to catch all + * PnvXScomInterface objects which can lie a bit deeper than the + * first layer. + */ + object_child_foreach_recursive(OBJECT(chip), xscom_dt_child, &args); return 0; } diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 52a18eb7d7..4c5fa29399 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -275,10 +275,9 @@ void ppc970_irq_init(PowerPCCPU *cpu) static void power7_set_irq(void *opaque, int pin, int level) { PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + &cpu->env, pin, level); switch (pin) { case POWER7_INPUT_INT: @@ -292,11 +291,6 @@ static void power7_set_irq(void *opaque, int pin, int level) LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); return; } - if (level) { - env->irq_input_state |= 1 << pin; - } else { - env->irq_input_state &= ~(1 << pin); - } } void ppcPOWER7_irq_init(PowerPCCPU *cpu) @@ -311,10 +305,9 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu) static void power9_set_irq(void *opaque, int pin, int level) { PowerPCCPU *cpu = opaque; - CPUPPCState *env = &cpu->env; LOG_IRQ("%s: env %p pin %d level %d\n", __func__, - env, pin, level); + &cpu->env, pin, level); switch (pin) { case POWER9_INPUT_INT: @@ -334,11 +327,6 @@ static void power9_set_irq(void *opaque, int pin, int level) LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); return; } - if (level) { - env->irq_input_state |= 1 << pin; - } else { - env->irq_input_state &= ~(1 << pin); - } } void ppcPOWER9_irq_init(PowerPCCPU *cpu) @@ -694,6 +682,35 @@ void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value) &tb_env->atb_offset, ((uint64_t)value << 32) | tb); } +uint64_t cpu_ppc_load_vtb(CPUPPCState *env) +{ + ppc_tb_t *tb_env = env->tb_env; + + return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->vtb_offset); +} + +void cpu_ppc_store_vtb(CPUPPCState *env, uint64_t value) +{ + ppc_tb_t *tb_env = env->tb_env; + + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + &tb_env->vtb_offset, value); +} + +void cpu_ppc_store_tbu40(CPUPPCState *env, uint64_t value) +{ + ppc_tb_t *tb_env = env->tb_env; + uint64_t tb; + + tb = cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->tb_offset); + tb &= 0xFFFFFFUL; + tb |= (value & ~0xFFFFFFUL); + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + &tb_env->tb_offset, tb); +} + static void cpu_ppc_tb_stop (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; @@ -805,12 +822,9 @@ target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) uint64_t cpu_ppc_load_purr (CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; - uint64_t diff; - - diff = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - tb_env->purr_start; - return tb_env->purr_load + - muldiv64(diff, tb_env->tb_freq, NANOSECONDS_PER_SECOND); + return cpu_ppc_get_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + tb_env->purr_offset); } /* When decrementer expires, @@ -969,12 +983,12 @@ static void cpu_ppc_hdecr_cb(void *opaque) cpu_ppc_hdecr_excp(cpu); } -static void cpu_ppc_store_purr(PowerPCCPU *cpu, uint64_t value) +void cpu_ppc_store_purr(CPUPPCState *env, uint64_t value) { - ppc_tb_t *tb_env = cpu->env.tb_env; + ppc_tb_t *tb_env = env->tb_env; - tb_env->purr_load = value; - tb_env->purr_start = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + cpu_ppc_store_tb(tb_env, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + &tb_env->purr_offset, value); } static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) @@ -991,7 +1005,7 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) */ _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); - cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); + cpu_ppc_store_purr(env, 0x0000000000000000ULL); } static void timebase_save(PPCTimebase *tb) @@ -1495,18 +1509,31 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) } } +int ppc_cpu_pir(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + return env->spr_cb[SPR_PIR].default_value; +} + PowerPCCPU *ppc_get_vcpu_by_pir(int pir) { CPUState *cs; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - if (env->spr_cb[SPR_PIR].default_value == pir) { + if (ppc_cpu_pir(cpu) == pir) { return cpu; } } return NULL; } + +void ppc_irq_reset(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + + env->irq_input_state = 0; + kvmppc_set_interrupt(cpu, PPC_INTERRUPT_EXT, 0); +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 3ae7db1563..f11422fc41 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -76,7 +76,6 @@ #include "hw/nmi.h" #include "hw/intc/intc.h" -#include "qemu/cutils.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/mem/memory-device.h" #include "hw/ppc/spapr_tpm_proxy.h" @@ -897,69 +896,6 @@ out: return ret; } -static bool spapr_hotplugged_dev_before_cas(void) -{ - Object *drc_container, *obj; - ObjectProperty *prop; - ObjectPropertyIterator iter; - - drc_container = container_get(object_get_root(), "/dr-connector"); - object_property_iter_init(&iter, drc_container); - while ((prop = object_property_iter_next(&iter))) { - if (!strstart(prop->type, "link<", NULL)) { - continue; - } - obj = object_property_get_link(drc_container, prop->name, NULL); - if (spapr_drc_needed(obj)) { - return true; - } - } - return false; -} - -static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset); - -int spapr_h_cas_compose_response(SpaprMachineState *spapr, - target_ulong addr, target_ulong size, - SpaprOptionVector *ov5_updates) -{ - void *fdt; - SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 }; - - if (spapr_hotplugged_dev_before_cas()) { - return 1; - } - - if (size < sizeof(hdr) || size > FW_MAX_SIZE) { - error_report("SLOF provided an unexpected CAS buffer size " - TARGET_FMT_lu " (min: %zu, max: %u)", - size, sizeof(hdr), FW_MAX_SIZE); - exit(EXIT_FAILURE); - } - - size -= sizeof(hdr); - - fdt = spapr_build_fdt(spapr, false); - _FDT((fdt_pack(fdt))); - - if (fdt_totalsize(fdt) + sizeof(hdr) > size) { - g_free(fdt); - trace_spapr_cas_failed(size); - return -1; - } - - cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); - cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); - trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); - - g_free(spapr->fdt_blob); - spapr->fdt_size = fdt_totalsize(fdt); - spapr->fdt_initial_size = spapr->fdt_size; - spapr->fdt_blob = fdt; - - return 0; -} - static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) { MachineState *ms = MACHINE(spapr); @@ -1197,7 +1133,7 @@ static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) } } -static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) +void *spapr_build_fdt(SpaprMachineState *spapr, bool reset, size_t space) { MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); @@ -1207,8 +1143,8 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) SpaprPhbState *phb; char *buf; - fdt = g_malloc0(FDT_MAX_SIZE); - _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + fdt = g_malloc0(space); + _FDT((fdt_create_empty_tree(fdt, space))); /* Root node */ _FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp")); @@ -1723,19 +1659,13 @@ static void spapr_machine_reset(MachineState *machine) */ fdt_addr = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FDT_MAX_SIZE; - fdt = spapr_build_fdt(spapr, true); + fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE); rc = fdt_pack(fdt); /* Should only fail if we've built a corrupted tree */ assert(rc == 0); - if (fdt_totalsize(fdt) > FDT_MAX_SIZE) { - error_report("FDT too big ! 0x%x bytes (max is 0x%x)", - fdt_totalsize(fdt), FDT_MAX_SIZE); - exit(1); - } - /* Load the fdt */ qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); @@ -1910,8 +1840,6 @@ static bool spapr_ov5_cas_needed(void *opaque) { SpaprMachineState *spapr = opaque; SpaprOptionVector *ov5_mask = spapr_ovec_new(); - SpaprOptionVector *ov5_legacy = spapr_ovec_new(); - SpaprOptionVector *ov5_removed = spapr_ovec_new(); bool cas_needed; /* Prior to the introduction of SpaprOptionVector, we had two option @@ -1943,17 +1871,11 @@ static bool spapr_ov5_cas_needed(void *opaque) spapr_ovec_set(ov5_mask, OV5_DRCONF_MEMORY); spapr_ovec_set(ov5_mask, OV5_DRMEM_V2); - /* spapr_ovec_diff returns true if bits were removed. we avoid using - * the mask itself since in the future it's possible "legacy" bits may be - * removed via machine options, which could generate a false positive - * that breaks migration. - */ - spapr_ovec_intersect(ov5_legacy, spapr->ov5, ov5_mask); - cas_needed = spapr_ovec_diff(ov5_removed, spapr->ov5, ov5_legacy); + /* We need extra information if we have any bits outside the mask + * defined above */ + cas_needed = !spapr_ovec_subset(spapr->ov5, ov5_mask); spapr_ovec_cleanup(ov5_mask); - spapr_ovec_cleanup(ov5_legacy); - spapr_ovec_cleanup(ov5_removed); return cas_needed; } @@ -2564,7 +2486,7 @@ static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) " requires the use of VSMT mode %d.\n", smp_threads, kvm_smt, spapr->vsmt); } - kvmppc_hint_smt_possible(&local_err); + kvmppc_error_append_smt_possible_hint(&local_err); goto out; } } @@ -4275,6 +4197,42 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, kvm_irqchip_in_kernel() ? "in-kernel" : "emulated"); } +static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(xfb); + XivePresenter *xptr = XIVE_PRESENTER(spapr->xive); + XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); + int count; + + /* This is a XIVE only operation */ + assert(spapr->active_intc == SPAPR_INTC(spapr->xive)); + + count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, cam_ignore, + priority, logic_serv, match); + if (count < 0) { + return count; + } + + /* + * When we implement the save and restore of the thread interrupt + * contexts in the enter/exit CPU handlers of the machine and the + * escalations in QEMU, we should be able to handle non dispatched + * vCPUs. + * + * Until this is done, the sPAPR machine should find at least one + * matching context always. + */ + if (count == 0) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n", + nvt_blk, nvt_idx); + } + + return count; +} + int spapr_get_vcpu_id(PowerPCCPU *cpu) { return cpu->vcpu_id; @@ -4371,6 +4329,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); + XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant)"; mc->ignore_boot_device_suffixes = true; @@ -4447,6 +4406,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->linux_pci_probe = true; smc->smp_threads_vsmt = true; smc->nr_xirqs = SPAPR_NR_XIRQS; + xfc->match_nvt = spapr_match_nvt; } static const TypeInfo spapr_machine_info = { @@ -4465,6 +4425,7 @@ static const TypeInfo spapr_machine_info = { { TYPE_PPC_VIRTUAL_HYPERVISOR }, { TYPE_XICS_FABRIC }, { TYPE_INTERRUPT_STATS_PROVIDER }, + { TYPE_XIVE_FABRIC }, { } }, }; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 140f05c1c6..f1799b1b70 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qapi/error.h" #include "sysemu/hw_accel.h" #include "sysemu/runstate.h" @@ -15,6 +16,7 @@ #include "cpu-models.h" #include "trace.h" #include "kvm_ppc.h" +#include "hw/ppc/fdt.h" #include "hw/ppc/spapr_ovec.h" #include "mmu-book3s-v3.h" #include "hw/mem/memory-device.h" @@ -1638,6 +1640,26 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu, return best_compat; } +static bool spapr_hotplugged_dev_before_cas(void) +{ + Object *drc_container, *obj; + ObjectProperty *prop; + ObjectPropertyIterator iter; + + drc_container = container_get(object_get_root(), "/dr-connector"); + object_property_iter_init(&iter, drc_container); + while ((prop = object_property_iter_next(&iter))) { + if (!strstart(prop->type, "link<", NULL)) { + continue; + } + obj = object_property_get_link(drc_container, prop->name, NULL); + if (spapr_drc_needed(obj)) { + return true; + } + } + return false; +} + static target_ulong h_client_architecture_support(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, @@ -1645,9 +1667,11 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, { /* Working address in data buffer */ target_ulong addr = ppc64_phys_to_real(args[0]); + target_ulong fdt_buf = args[1]; + target_ulong fdt_bufsize = args[2]; target_ulong ov_table; uint32_t cas_pvr; - SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; + SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old; bool guest_radix; Error *local_err = NULL; bool raw_mode_supported = false; @@ -1746,9 +1770,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, /* capabilities that have been added since CAS-generated guest reset. * if capabilities have since been removed, generate another reset */ - ov5_updates = spapr_ovec_new(); - spapr->cas_reboot = spapr_ovec_diff(ov5_updates, - ov5_cas_old, spapr->ov5_cas); + spapr->cas_reboot = !spapr_ovec_subset(ov5_cas_old, spapr->ov5_cas); spapr_ovec_cleanup(ov5_cas_old); /* Now that processing is finished, set the radix/hash bit for the * guest if it requested a valid mode; otherwise terminate the boot. */ @@ -1767,21 +1789,10 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, } spapr->cas_pre_isa3_guest = !spapr_ovec_test(ov1_guest, OV1_PPC_3_00); spapr_ovec_cleanup(ov1_guest); - if (!spapr->cas_reboot) { - /* If spapr_machine_reset() did not set up a HPT but one is necessary - * (because the guest isn't going to use radix) then set it up here. */ - if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { - /* legacy hash or new hash: */ - spapr_setup_hpt_and_vrma(spapr); - } - spapr->cas_reboot = - (spapr_h_cas_compose_response(spapr, args[1], args[2], - ov5_updates) != 0); - } /* - * Ensure the guest asks for an interrupt mode we support; otherwise - * terminate the boot. + * Ensure the guest asks for an interrupt mode we support; + * otherwise terminate the boot. */ if (guest_xive) { if (!spapr->irq->xive) { @@ -1797,17 +1808,44 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, } } - /* - * Generate a machine reset when we have an update of the - * interrupt mode. Only required when the machine supports both - * modes. - */ - if (!spapr->cas_reboot) { - spapr->cas_reboot = spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT) - && spapr->irq->xics && spapr->irq->xive; + spapr_irq_update_active_intc(spapr); + + if (spapr_hotplugged_dev_before_cas()) { + spapr->cas_reboot = true; } - spapr_ovec_cleanup(ov5_updates); + if (!spapr->cas_reboot) { + void *fdt; + SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 }; + + /* If spapr_machine_reset() did not set up a HPT but one is necessary + * (because the guest isn't going to use radix) then set it up here. */ + if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { + /* legacy hash or new hash: */ + spapr_setup_hpt_and_vrma(spapr); + } + + if (fdt_bufsize < sizeof(hdr)) { + error_report("SLOF provided insufficient CAS buffer " + TARGET_FMT_lu " (min: %zu)", fdt_bufsize, sizeof(hdr)); + exit(EXIT_FAILURE); + } + + fdt_bufsize -= sizeof(hdr); + + fdt = spapr_build_fdt(spapr, false, fdt_bufsize); + _FDT((fdt_pack(fdt))); + + cpu_physical_memory_write(fdt_buf, &hdr, sizeof(hdr)); + cpu_physical_memory_write(fdt_buf + sizeof(hdr), fdt, + fdt_totalsize(fdt)); + trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); + + g_free(spapr->fdt_blob); + spapr->fdt_size = fdt_totalsize(fdt); + spapr->fdt_initial_size = spapr->fdt_size; + spapr->fdt_blob = fdt; + } if (spapr->cas_reboot) { qemu_system_reset_request(SHUTDOWN_CAUSE_SUBSYSTEM_RESET); diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c index d6bb7fd2d6..07e08d6544 100644 --- a/hw/ppc/spapr_irq.c +++ b/hw/ppc/spapr_irq.c @@ -70,15 +70,16 @@ void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num) bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num); } -int spapr_irq_init_kvm(int (*fn)(SpaprInterruptController *, Error **), +int spapr_irq_init_kvm(SpaprInterruptControllerInitKvm fn, SpaprInterruptController *intc, + uint32_t nr_servers, Error **errp) { MachineState *machine = MACHINE(qdev_get_machine()); Error *local_err = NULL; if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) { - if (fn(intc, &local_err) < 0) { + if (fn(intc, nr_servers, &local_err) < 0) { if (machine_kernel_irqchip_required(machine)) { error_prepend(&local_err, "kernel_irqchip requested but unavailable: "); @@ -313,25 +314,11 @@ void spapr_irq_init(SpaprMachineState *spapr, Error **errp) Object *obj; obj = object_new(TYPE_ICS_SPAPR); - object_property_add_child(OBJECT(spapr), "ics", obj, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr), - &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } + object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort); + object_property_set_link(obj, OBJECT(spapr), ICS_PROP_XICS, + &error_abort); + object_property_set_int(obj, smc->nr_xirqs, "nr-irqs", &error_abort); object_property_set_bool(obj, true, "realized", &local_err); if (local_err) { error_propagate(errp, local_err); @@ -495,6 +482,7 @@ static void set_active_intc(SpaprMachineState *spapr, SpaprInterruptController *new_intc) { SpaprInterruptControllerClass *sicc; + uint32_t nr_servers = spapr_max_server_number(spapr); assert(new_intc); @@ -512,7 +500,7 @@ static void set_active_intc(SpaprMachineState *spapr, sicc = SPAPR_INTC_GET_CLASS(new_intc); if (sicc->activate) { - sicc->activate(new_intc, &error_fatal); + sicc->activate(new_intc, nr_servers, &error_fatal); } spapr->active_intc = new_intc; diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 811fadf143..0ff6d1aeae 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -76,31 +76,21 @@ void spapr_ovec_intersect(SpaprOptionVector *ov, bitmap_and(ov->bitmap, ov1->bitmap, ov2->bitmap, OV_MAXBITS); } -/* returns true if options bits were removed, false otherwise */ -bool spapr_ovec_diff(SpaprOptionVector *ov, - SpaprOptionVector *ov_old, - SpaprOptionVector *ov_new) +/* returns true if ov1 has a subset of bits in ov2 */ +bool spapr_ovec_subset(SpaprOptionVector *ov1, SpaprOptionVector *ov2) { - unsigned long *change_mask = bitmap_new(OV_MAXBITS); - unsigned long *removed_bits = bitmap_new(OV_MAXBITS); - bool bits_were_removed = false; + unsigned long *tmp = bitmap_new(OV_MAXBITS); + bool result; - g_assert(ov); - g_assert(ov_old); - g_assert(ov_new); - - bitmap_xor(change_mask, ov_old->bitmap, ov_new->bitmap, OV_MAXBITS); - bitmap_and(ov->bitmap, ov_new->bitmap, change_mask, OV_MAXBITS); - bitmap_and(removed_bits, ov_old->bitmap, change_mask, OV_MAXBITS); + g_assert(ov1); + g_assert(ov2); - if (!bitmap_empty(removed_bits, OV_MAXBITS)) { - bits_were_removed = true; - } + bitmap_andnot(tmp, ov1->bitmap, ov2->bitmap, OV_MAXBITS); + result = bitmap_empty(tmp, OV_MAXBITS); - g_free(change_mask); - g_free(removed_bits); + g_free(tmp); - return bits_were_removed; + return result; } void spapr_ovec_cleanup(SpaprOptionVector *ov) |