#include "exec/address-spaces.h"
#include "qemu/cutils.h"
#include "qapi/visitor.h"
+#include "monitor/monitor.h"
+#include "hw/intc/intc.h"
+#include "hw/ipmi/ipmi.h"
+#include "target/ppc/mmu-hash64.h"
+#include "hw/ppc/xics.h"
#include "hw/ppc/pnv_xscom.h"
#include "hw/isa/isa.h"
#define FW_MAX_SIZE 0x00400000
#define KERNEL_LOAD_ADDR 0x20000000
-#define INITRD_LOAD_ADDR 0x40000000
+#define INITRD_LOAD_ADDR 0x60000000
+
+static const char *pnv_chip_core_typename(const PnvChip *o)
+{
+ const char *chip_type = object_class_get_name(object_get_class(OBJECT(o)));
+ int len = strlen(chip_type) - strlen(PNV_CHIP_TYPE_SUFFIX);
+ char *s = g_strdup_printf(PNV_CORE_TYPE_NAME("%.*s"), len, chip_type);
+ const char *core_type = object_class_get_name(object_class_by_name(s));
+ g_free(s);
+ return core_type;
+}
/*
* On Power Systems E880 (POWER8), the max cpus (threads) should be :
* that has a different "affinity". In practice, it means one range
* per chip.
*/
-static void powernv_populate_memory_node(void *fdt, int chip_id, hwaddr start,
- hwaddr size)
+static void pnv_dt_memory(void *fdt, int chip_id, hwaddr start, hwaddr size)
{
char *mem_name;
uint64_t mem_reg_property[2];
int cpus_offset = fdt_path_offset(fdt, "/cpus");
if (cpus_offset < 0) {
- cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"),
- "cpus");
+ cpus_offset = fdt_add_subnode(fdt, 0, "cpus");
if (cpus_offset) {
_FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1)));
_FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0)));
* device tree, used in XSCOM to address cores and in interrupt
* servers.
*/
-static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt)
+static void pnv_dt_core(PnvChip *chip, PnvCore *pc, void *fdt)
{
CPUState *cs = CPU(DEVICE(pc->threads));
DeviceClass *dc = DEVICE_GET_CLASS(cs);
_FDT((fdt_setprop_cell(fdt, offset, "d-cache-size",
pcc->l1_dcache_size)));
} else {
- error_report("Warning: Unknown L1 dcache size for cpu");
+ warn_report("Unknown L1 dcache size for cpu");
}
if (pcc->l1_icache_size) {
_FDT((fdt_setprop_cell(fdt, offset, "i-cache-size",
pcc->l1_icache_size)));
} else {
- error_report("Warning: Unknown L1 icache size for cpu");
+ warn_report("Unknown L1 icache size for cpu");
}
_FDT((fdt_setprop_cell(fdt, offset, "timebase-frequency", tbfreq)));
_FDT((fdt_setprop_cell(fdt, offset, "clock-frequency", cpufreq)));
- _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", env->slb_nr)));
+ _FDT((fdt_setprop_cell(fdt, offset, "ibm,slb-size", cpu->hash64_opts->slb_size)));
_FDT((fdt_setprop_string(fdt, offset, "status", "okay")));
_FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0)));
_FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0)));
}
- if (env->mmu_model & POWERPC_MMU_1TSEG) {
+ if (ppc_hash64_has(cpu, PPC_HASH64_1TSEG)) {
_FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes",
segs, sizeof(segs))));
}
_FDT((fdt_setprop_cell(fdt, offset, "ibm,dfp", 1)));
}
- page_sizes_prop_size = ppc_create_page_sizes_prop(env, page_sizes_prop,
- sizeof(page_sizes_prop));
+ page_sizes_prop_size = ppc_create_page_sizes_prop(cpu, page_sizes_prop,
+ sizeof(page_sizes_prop));
if (page_sizes_prop_size) {
_FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes",
page_sizes_prop, page_sizes_prop_size)));
servers_prop, sizeof(servers_prop))));
}
-static void powernv_populate_chip(PnvChip *chip, void *fdt)
+static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir,
+ uint32_t nr_threads)
{
- PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
- char *typename = pnv_core_typename(pcc->cpu_model);
+ uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12);
+ char *name;
+ const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp";
+ uint32_t irange[2], i, rsize;
+ uint64_t *reg;
+ int offset;
+
+ irange[0] = cpu_to_be32(pir);
+ irange[1] = cpu_to_be32(nr_threads);
+
+ rsize = sizeof(uint64_t) * 2 * nr_threads;
+ reg = g_malloc(rsize);
+ for (i = 0; i < nr_threads; i++) {
+ reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000));
+ reg[i * 2 + 1] = cpu_to_be64(0x1000);
+ }
+
+ name = g_strdup_printf("interrupt-controller@%"PRIX64, addr);
+ offset = fdt_add_subnode(fdt, 0, name);
+ _FDT(offset);
+ g_free(name);
+
+ _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
+ _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize)));
+ _FDT((fdt_setprop_string(fdt, offset, "device_type",
+ "PowerPC-External-Interrupt-Presentation")));
+ _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0)));
+ _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges",
+ irange, sizeof(irange))));
+ _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1)));
+ _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0)));
+ g_free(reg);
+}
+
+static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt)
+{
+ char *name;
+ int offset;
+
+ name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x",
+ (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE);
+ offset = fdt_path_offset(fdt, name);
+ g_free(name);
+ return offset;
+}
+
+static void pnv_dt_chip(PnvChip *chip, void *fdt)
+{
+ const char *typename = pnv_chip_core_typename(chip);
size_t typesize = object_type_get_instance_size(typename);
int i;
- pnv_xscom_populate(chip, fdt, 0);
+ pnv_dt_xscom(chip, fdt, 0);
+
+ /* The default LPC bus of a multichip system is on chip 0. It's
+ * recognized by the firmware (skiboot) using a "primary"
+ * property.
+ */
+ if (chip->chip_id == 0x0) {
+ int lpc_offset = pnv_chip_lpc_offset(chip, fdt);
+
+ _FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0)));
+ }
for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
- powernv_create_core_node(chip, pnv_core, fdt);
+ pnv_dt_core(chip, pnv_core, fdt);
+
+ /* Interrupt Control Presenters (ICP). One per core. */
+ pnv_dt_icp(chip, fdt, pnv_core->pir, CPU_CORE(pnv_core)->nr_threads);
}
if (chip->ram_size) {
- powernv_populate_memory_node(fdt, chip->chip_id, chip->ram_start,
- chip->ram_size);
+ pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size);
}
- g_free(typename);
}
-static void *powernv_create_fdt(MachineState *machine)
+static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off)
+{
+ uint32_t io_base = d->ioport_id;
+ uint32_t io_regs[] = {
+ cpu_to_be32(1),
+ cpu_to_be32(io_base),
+ cpu_to_be32(2)
+ };
+ char *name;
+ int node;
+
+ name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base);
+ node = fdt_add_subnode(fdt, lpc_off, name);
+ _FDT(node);
+ g_free(name);
+
+ _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs))));
+ _FDT((fdt_setprop_string(fdt, node, "compatible", "pnpPNP,b00")));
+}
+
+static void pnv_dt_serial(ISADevice *d, void *fdt, int lpc_off)
+{
+ const char compatible[] = "ns16550\0pnpPNP,501";
+ uint32_t io_base = d->ioport_id;
+ uint32_t io_regs[] = {
+ cpu_to_be32(1),
+ cpu_to_be32(io_base),
+ cpu_to_be32(8)
+ };
+ char *name;
+ int node;
+
+ name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base);
+ node = fdt_add_subnode(fdt, lpc_off, name);
+ _FDT(node);
+ g_free(name);
+
+ _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs))));
+ _FDT((fdt_setprop(fdt, node, "compatible", compatible,
+ sizeof(compatible))));
+
+ _FDT((fdt_setprop_cell(fdt, node, "clock-frequency", 1843200)));
+ _FDT((fdt_setprop_cell(fdt, node, "current-speed", 115200)));
+ _FDT((fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0])));
+ _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent",
+ fdt_get_phandle(fdt, lpc_off))));
+
+ /* This is needed by Linux */
+ _FDT((fdt_setprop_string(fdt, node, "device_type", "serial")));
+}
+
+static void pnv_dt_ipmi_bt(ISADevice *d, void *fdt, int lpc_off)
+{
+ const char compatible[] = "bt\0ipmi-bt";
+ uint32_t io_base;
+ uint32_t io_regs[] = {
+ cpu_to_be32(1),
+ 0, /* 'io_base' retrieved from the 'ioport' property of 'isa-ipmi-bt' */
+ cpu_to_be32(3)
+ };
+ uint32_t irq;
+ char *name;
+ int node;
+
+ io_base = object_property_get_int(OBJECT(d), "ioport", &error_fatal);
+ io_regs[1] = cpu_to_be32(io_base);
+
+ irq = object_property_get_int(OBJECT(d), "irq", &error_fatal);
+
+ name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base);
+ node = fdt_add_subnode(fdt, lpc_off, name);
+ _FDT(node);
+ g_free(name);
+
+ _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs))));
+ _FDT((fdt_setprop(fdt, node, "compatible", compatible,
+ sizeof(compatible))));
+
+ /* Mark it as reserved to avoid Linux trying to claim it */
+ _FDT((fdt_setprop_string(fdt, node, "status", "reserved")));
+ _FDT((fdt_setprop_cell(fdt, node, "interrupts", irq)));
+ _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent",
+ fdt_get_phandle(fdt, lpc_off))));
+}
+
+typedef struct ForeachPopulateArgs {
+ void *fdt;
+ int offset;
+} ForeachPopulateArgs;
+
+static int pnv_dt_isa_device(DeviceState *dev, void *opaque)
+{
+ ForeachPopulateArgs *args = opaque;
+ ISADevice *d = ISA_DEVICE(dev);
+
+ if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) {
+ pnv_dt_rtc(d, args->fdt, args->offset);
+ } else if (object_dynamic_cast(OBJECT(dev), TYPE_ISA_SERIAL)) {
+ pnv_dt_serial(d, args->fdt, args->offset);
+ } else if (object_dynamic_cast(OBJECT(dev), "isa-ipmi-bt")) {
+ pnv_dt_ipmi_bt(d, args->fdt, args->offset);
+ } else {
+ error_report("unknown isa device %s@i%x", qdev_fw_name(dev),
+ d->ioport_id);
+ }
+
+ return 0;
+}
+
+static void pnv_dt_isa(ISABus *bus, void *fdt, int lpc_offset)
+{
+ ForeachPopulateArgs args = {
+ .fdt = fdt,
+ .offset = lpc_offset,
+ };
+
+ /* ISA devices are not necessarily parented to the ISA bus so we
+ * can not use object_child_foreach() */
+ qbus_walk_children(BUS(bus), pnv_dt_isa_device, NULL, NULL, NULL, &args);
+}
+
+static void *pnv_dt_create(MachineState *machine)
{
const char plat_compat[] = "qemu,powernv\0ibm,powernv";
- PnvMachineState *pnv = POWERNV_MACHINE(machine);
+ PnvMachineState *pnv = PNV_MACHINE(machine);
void *fdt;
char *buf;
int off;
int i;
+ int lpc_offset;
fdt = g_malloc0(FDT_MAX_SIZE);
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE)));
/* Populate device tree for each chip */
for (i = 0; i < pnv->num_chips; i++) {
- powernv_populate_chip(pnv->chips[i], fdt);
+ pnv_dt_chip(pnv->chips[i], fdt);
+ }
+
+ /* Populate ISA devices on chip 0 */
+ lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt);
+ pnv_dt_isa(pnv->isa_bus, fdt, lpc_offset);
+
+ if (pnv->bmc) {
+ pnv_dt_bmc_sensors(pnv->bmc, fdt);
}
+
return fdt;
}
-static void ppc_powernv_reset(void)
+static void pnv_powerdown_notify(Notifier *n, void *opaque)
+{
+ PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine());
+
+ if (pnv->bmc) {
+ pnv_bmc_powerdown(pnv->bmc);
+ }
+}
+
+static void pnv_reset(void)
{
MachineState *machine = MACHINE(qdev_get_machine());
+ PnvMachineState *pnv = PNV_MACHINE(machine);
void *fdt;
+ Object *obj;
qemu_devices_reset();
- fdt = powernv_create_fdt(machine);
+ /* 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 */
_FDT((fdt_pack(fdt)));
cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
}
-/* If we don't use the built-in LPC interrupt deserializer, we need
- * to provide a set of qirqs for the ISA bus or things will go bad.
- *
- * Most machines using pre-Naples chips (without said deserializer)
- * have a CPLD that will collect the SerIRQ and shoot them as a
- * single level interrupt to the P8 chip. So let's setup a hook
- * for doing just that.
- *
- * Note: The actual interrupt input isn't emulated yet, this will
- * come with the PSI bridge model.
- */
-static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
-{
- /* We don't yet emulate the PSI bridge which provides the external
- * interrupt, so just drop interrupts on the floor
- */
-}
-
-static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
-{
- /* XXX TODO */
-}
-
static ISABus *pnv_isa_create(PnvChip *chip)
{
PnvLpcController *lpc = &chip->lpc;
isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
&error_fatal);
- /* Not all variants have a working serial irq decoder. If not,
- * handling of LPC interrupts becomes a platform issue (some
- * platforms have a CPLD to do it).
- */
- if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
- irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS);
- } else {
- irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip,
- ISA_NUM_IRQS);
- }
+ irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS);
isa_bus_irqs(isa_bus, irqs);
return isa_bus;
}
-static void ppc_powernv_init(MachineState *machine)
+static void pnv_init(MachineState *machine)
{
- PnvMachineState *pnv = POWERNV_MACHINE(machine);
+ PnvMachineState *pnv = PNV_MACHINE(machine);
MemoryRegion *ram;
char *fw_filename;
long fw_size;
/* allocate RAM */
if (machine->ram_size < (1 * G_BYTE)) {
- error_report("Warning: skiboot may not work with < 1GB of RAM");
+ warn_report("skiboot may not work with < 1GB of RAM");
}
ram = g_new(MemoryRegion, 1);
- memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram",
+ memory_region_allocate_system_memory(ram, NULL, "pnv.ram",
machine->ram_size);
memory_region_add_subregion(get_system_memory(), 0, ram);
}
fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+ if (!fw_filename) {
+ error_report("Could not find OPAL firmware '%s'", bios_name);
+ exit(1);
+ }
fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE);
if (fw_size < 0) {
- error_report("Could not load OPAL '%s'", fw_filename);
+ error_report("Could not load OPAL firmware '%s'", fw_filename);
exit(1);
}
g_free(fw_filename);
}
}
- /* We need some cpu model to instantiate the PnvChip class */
- if (machine->cpu_model == NULL) {
- machine->cpu_model = "POWER8";
- }
-
/* Create the processor chips */
- chip_typename = g_strdup_printf(TYPE_PNV_CHIP "-%s", machine->cpu_model);
+ i = strlen(machine->cpu_type) - strlen(POWERPC_CPU_TYPE_SUFFIX);
+ chip_typename = g_strdup_printf(PNV_CHIP_TYPE_NAME("%.*s"),
+ i, machine->cpu_type);
if (!object_class_by_name(chip_typename)) {
- error_report("qemu: invalid CPU model '%s' for %s machine",
- machine->cpu_model, MACHINE_GET_CLASS(machine)->name);
+ error_report("invalid CPU model '%.*s' for %s machine",
+ i, machine->cpu_type, MACHINE_GET_CLASS(machine)->name);
exit(1);
}
pnv->isa_bus = pnv_isa_create(pnv->chips[0]);
/* Create serial port */
- serial_hds_isa_init(pnv->isa_bus, 0, MAX_SERIAL_PORTS);
+ serial_hds_isa_init(pnv->isa_bus, 0, MAX_ISA_SERIAL_PORTS);
/* Create an RTC ISA device too */
- rtc_init(pnv->isa_bus, 2000, NULL);
+ mc146818_rtc_init(pnv->isa_bus, 2000, NULL);
+
+ /* OpenPOWER systems use a IPMI SEL Event message to notify the
+ * host to powerdown */
+ pnv->powerdown_notifier.notify = pnv_powerdown_notify;
+ qemu_register_powerdown_notifier(&pnv->powerdown_notifier);
}
/*
#define POWER8_CORE_MASK (0x7e7eull)
/*
- * POWER9 has 24 cores, ids starting at 0x20
+ * POWER9 has 24 cores, ids starting at 0x0
*/
-#define POWER9_CORE_MASK (0xffffff00000000ull)
+#define POWER9_CORE_MASK (0xffffffffffffffull)
static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PnvChipClass *k = PNV_CHIP_CLASS(klass);
- k->cpu_model = "POWER8E";
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->xscom_base = 0x003fc0000000000ull;
- k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8E";
}
-static const TypeInfo pnv_chip_power8e_info = {
- .name = TYPE_PNV_CHIP_POWER8E,
- .parent = TYPE_PNV_CHIP,
- .instance_size = sizeof(PnvChip),
- .class_init = pnv_chip_power8e_class_init,
-};
-
static void pnv_chip_power8_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PnvChipClass *k = PNV_CHIP_CLASS(klass);
- k->cpu_model = "POWER8";
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->xscom_base = 0x003fc0000000000ull;
- k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8";
}
-static const TypeInfo pnv_chip_power8_info = {
- .name = TYPE_PNV_CHIP_POWER8,
- .parent = TYPE_PNV_CHIP,
- .instance_size = sizeof(PnvChip),
- .class_init = pnv_chip_power8_class_init,
-};
-
static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PnvChipClass *k = PNV_CHIP_CLASS(klass);
- k->cpu_model = "POWER8NVL";
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->xscom_base = 0x003fc0000000000ull;
- k->xscom_core_base = 0x10000000ull;
dc->desc = "PowerNV Chip POWER8NVL";
}
-static const TypeInfo pnv_chip_power8nvl_info = {
- .name = TYPE_PNV_CHIP_POWER8NVL,
- .parent = TYPE_PNV_CHIP,
- .instance_size = sizeof(PnvChip),
- .class_init = pnv_chip_power8nvl_class_init,
-};
-
static void pnv_chip_power9_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PnvChipClass *k = PNV_CHIP_CLASS(klass);
- k->cpu_model = "POWER9";
k->chip_type = PNV_CHIP_POWER9;
- k->chip_cfam_id = 0x100d104980000000ull; /* P9 Nimbus DD1.0 */
+ k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */
k->cores_mask = POWER9_CORE_MASK;
k->core_pir = pnv_chip_core_pir_p9;
k->xscom_base = 0x00603fc00000000ull;
- k->xscom_core_base = 0x0ull;
dc->desc = "PowerNV Chip POWER9";
}
-static const TypeInfo pnv_chip_power9_info = {
- .name = TYPE_PNV_CHIP_POWER9,
- .parent = TYPE_PNV_CHIP,
- .instance_size = sizeof(PnvChip),
- .class_init = pnv_chip_power9_class_init,
-};
-
static void pnv_chip_core_sanitize(PnvChip *chip, Error **errp)
{
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL);
+
+ object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI);
+ object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL);
+ object_property_add_const_link(OBJECT(&chip->psi), "xics",
+ OBJECT(qdev_get_machine()), &error_abort);
+
+ object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC);
+ object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL);
+ object_property_add_const_link(OBJECT(&chip->occ), "psi",
+ OBJECT(&chip->psi), &error_abort);
+
+ /* The LPC controller needs PSI to generate interrupts */
+ object_property_add_const_link(OBJECT(&chip->lpc), "psi",
+ OBJECT(&chip->psi), &error_abort);
+}
+
+static void pnv_chip_icp_realize(PnvChip *chip, Error **errp)
+{
+ 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());
+
+ name = g_strdup_printf("icp-%x", chip->chip_id);
+ memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE);
+ sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio);
+ g_free(name);
+
+ sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip));
+
+ /* Map the ICP registers for each thread */
+ for (i = 0; i < chip->nr_cores; i++) {
+ PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
+ int core_hwid = CPU_CORE(pnv_core)->core_id;
+
+ for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) {
+ uint32_t pir = pcc->core_pir(chip, core_hwid) + j;
+ PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir));
+
+ memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio);
+ }
+ }
}
static void pnv_chip_realize(DeviceState *dev, Error **errp)
PnvChip *chip = PNV_CHIP(dev);
Error *error = NULL;
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip);
- char *typename = pnv_core_typename(pcc->cpu_model);
+ const char *typename = pnv_chip_core_typename(chip);
size_t typesize = object_type_get_instance_size(typename);
int i, core_hwid;
&& (i < chip->nr_cores); core_hwid++) {
char core_name[32];
void *pnv_core = chip->cores + i * typesize;
+ uint64_t xscom_core_base;
if (!(chip->cores_mask & (1ull << core_hwid))) {
continue;
object_property_set_int(OBJECT(pnv_core),
pcc->core_pir(chip, core_hwid),
"pir", &error_fatal);
+ object_property_add_const_link(OBJECT(pnv_core), "xics",
+ qdev_get_machine(), &error_fatal);
object_property_set_bool(OBJECT(pnv_core), true, "realized",
&error_fatal);
object_unref(OBJECT(pnv_core));
/* Each core has an XSCOM MMIO region */
- pnv_xscom_add_subregion(chip,
- PNV_XSCOM_EX_CORE_BASE(pcc->xscom_core_base,
- core_hwid),
+ if (!pnv_chip_is_power9(chip)) {
+ xscom_core_base = PNV_XSCOM_EX_BASE(core_hwid);
+ } else {
+ xscom_core_base = PNV_XSCOM_P9_EC_BASE(core_hwid);
+ }
+
+ pnv_xscom_add_subregion(chip, xscom_core_base,
&PNV_CORE(pnv_core)->xscom_regs);
i++;
}
- g_free(typename);
/* Create LPC controller */
object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
&error_fatal);
pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs);
+
+ /* Interrupt Management Area. This is the memory region holding
+ * all the Interrupt Control Presenter (ICP) registers */
+ pnv_chip_icp_realize(chip, &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+
+ /* Processor Service Interface (PSI) Host Bridge */
+ object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip),
+ "bar", &error_fatal);
+ object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs);
+
+ /* Create the simplified OCC model */
+ object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error);
+ if (error) {
+ error_propagate(errp, error);
+ return;
+ }
+ pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs);
}
static Property pnv_chip_properties[] = {
dc->desc = "PowerNV Chip";
}
-static const TypeInfo pnv_chip_info = {
- .name = TYPE_PNV_CHIP,
- .parent = TYPE_SYS_BUS_DEVICE,
- .class_init = pnv_chip_class_init,
- .instance_init = pnv_chip_init,
- .class_size = sizeof(PnvChipClass),
- .abstract = true,
-};
+static ICSState *pnv_ics_get(XICSFabric *xi, int irq)
+{
+ PnvMachineState *pnv = PNV_MACHINE(xi);
+ int i;
+
+ for (i = 0; i < pnv->num_chips; i++) {
+ if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) {
+ return &pnv->chips[i]->psi.ics;
+ }
+ }
+ return NULL;
+}
+
+static void pnv_ics_resend(XICSFabric *xi)
+{
+ PnvMachineState *pnv = PNV_MACHINE(xi);
+ int i;
+
+ for (i = 0; i < pnv->num_chips; i++) {
+ ics_resend(&pnv->chips[i]->psi.ics);
+ }
+}
+
+static 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) {
+ return cpu;
+ }
+ }
+
+ return NULL;
+}
+
+static ICPState *pnv_icp_get(XICSFabric *xi, int pir)
+{
+ PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir);
+
+ return cpu ? ICP(cpu->intc) : NULL;
+}
+
+static void pnv_pic_print_info(InterruptStatsProvider *obj,
+ Monitor *mon)
+{
+ PnvMachineState *pnv = PNV_MACHINE(obj);
+ int i;
+ CPUState *cs;
+
+ CPU_FOREACH(cs) {
+ PowerPCCPU *cpu = POWERPC_CPU(cs);
+
+ icp_pic_print_info(ICP(cpu->intc), mon);
+ }
+
+ for (i = 0; i < pnv->num_chips; i++) {
+ ics_pic_print_info(&pnv->chips[i]->psi.ics, mon);
+ }
+}
static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- visit_type_uint32(v, name, &POWERNV_MACHINE(obj)->num_chips, errp);
+ visit_type_uint32(v, name, &PNV_MACHINE(obj)->num_chips, errp);
}
static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
- PnvMachineState *pnv = POWERNV_MACHINE(obj);
+ PnvMachineState *pnv = PNV_MACHINE(obj);
uint32_t num_chips;
Error *local_err = NULL;
pnv->num_chips = num_chips;
}
-static void powernv_machine_initfn(Object *obj)
+static void pnv_machine_initfn(Object *obj)
{
- PnvMachineState *pnv = POWERNV_MACHINE(obj);
+ PnvMachineState *pnv = PNV_MACHINE(obj);
pnv->num_chips = 1;
}
-static void powernv_machine_class_props_init(ObjectClass *oc)
+static void pnv_machine_class_props_init(ObjectClass *oc)
{
- object_class_property_add(oc, "num-chips", "uint32_t",
+ object_class_property_add(oc, "num-chips", "uint32",
pnv_get_num_chips, pnv_set_num_chips,
NULL, NULL, NULL);
object_class_property_set_description(oc, "num-chips",
NULL);
}
-static void powernv_machine_class_init(ObjectClass *oc, void *data)
+static void pnv_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
+ XICSFabricClass *xic = XICS_FABRIC_CLASS(oc);
+ InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc);
mc->desc = "IBM PowerNV (Non-Virtualized)";
- mc->init = ppc_powernv_init;
- mc->reset = ppc_powernv_reset;
+ mc->init = pnv_init;
+ mc->reset = pnv_reset;
mc->max_cpus = MAX_CPUS;
+ mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0");
mc->block_default_type = IF_IDE; /* Pnv provides a AHCI device for
* storage */
mc->no_parallel = 1;
mc->default_boot_order = NULL;
mc->default_ram_size = 1 * G_BYTE;
+ xic->icp_get = pnv_icp_get;
+ xic->ics_get = pnv_ics_get;
+ xic->ics_resend = pnv_ics_resend;
+ ispc->print_info = pnv_pic_print_info;
- powernv_machine_class_props_init(oc);
+ pnv_machine_class_props_init(oc);
}
-static const TypeInfo powernv_machine_info = {
- .name = TYPE_POWERNV_MACHINE,
- .parent = TYPE_MACHINE,
- .instance_size = sizeof(PnvMachineState),
- .instance_init = powernv_machine_initfn,
- .class_init = powernv_machine_class_init,
-};
+#define DEFINE_PNV_CHIP_TYPE(type, class_initfn) \
+ { \
+ .name = type, \
+ .class_init = class_initfn, \
+ .parent = TYPE_PNV_CHIP, \
+ }
-static void powernv_machine_register_types(void)
-{
- type_register_static(&powernv_machine_info);
- type_register_static(&pnv_chip_info);
- type_register_static(&pnv_chip_power8e_info);
- type_register_static(&pnv_chip_power8_info);
- type_register_static(&pnv_chip_power8nvl_info);
- type_register_static(&pnv_chip_power9_info);
-}
+static const TypeInfo types[] = {
+ {
+ .name = TYPE_PNV_MACHINE,
+ .parent = TYPE_MACHINE,
+ .instance_size = sizeof(PnvMachineState),
+ .instance_init = pnv_machine_initfn,
+ .class_init = pnv_machine_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_XICS_FABRIC },
+ { TYPE_INTERRUPT_STATS_PROVIDER },
+ { },
+ },
+ },
+ {
+ .name = TYPE_PNV_CHIP,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .class_init = pnv_chip_class_init,
+ .instance_init = pnv_chip_init,
+ .instance_size = sizeof(PnvChip),
+ .class_size = sizeof(PnvChipClass),
+ .abstract = true,
+ },
+ DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER9, pnv_chip_power9_class_init),
+ DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8, pnv_chip_power8_class_init),
+ DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8E, pnv_chip_power8e_class_init),
+ DEFINE_PNV_CHIP_TYPE(TYPE_PNV_CHIP_POWER8NVL,
+ pnv_chip_power8nvl_class_init),
+};
-type_init(powernv_machine_register_types)
+DEFINE_TYPES(types)