diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2016-10-28 16:31:59 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2016-10-28 16:31:59 +0100 |
commit | 66a77ea676aea48092500bcddb015aa0aee42388 (patch) | |
tree | 9fe8c7f1a6bf8828a8ec239a6ced021486e883d3 | |
parent | 01b601f06154c0d35f945b1321ddb3f39530cc43 (diff) | |
parent | 10c21b5c20bf3d20b7b0ad279db37ae89cc7937d (diff) |
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.8-20161028' into staging
ppc patch queue 2016-10-28
This pull request supersedes and extends the one from 2016-10-26
(which had a build bug).
Highlights:
* SLOF (pseries guest firmware) update
* Enable a number of extra testcases on ppc / pseries
* Added the 'powernv' machine type
- Almost enough to be minimally usable
- But still missing necessary interrupt controller updates
* Cleanup and consolidation of NVRAM handling on several platforms
with related firmware
* Substantial cleanup to device tree construction
* Some more POWER9 instruction emulation
* Cleanup to handling of pseries option vectors and CAS reboot
handling (host/guest feature negotiation mechanism)
* Significant cleanups to handling of PCI devices in test cases
* New hotplug event infrastructure
* Memory hot unplug support for pseries
* Several bug fixes
The NVRAM cleanup affects some Sun sparc platforms as well as ppc
ones, but have been tested by the sparc maintainer (Mark Cave-Ayland).
The test additions also include substantial general changes to the
test framework that aren't strictly ppc related. They don't seem to
break tests on other platforms, they're for the benefit of enabling
tests on ppc and there isn't a specific maintainer for them, so
they're included in this tree.
# gpg: Signature made Fri 28 Oct 2016 02:37:19 BST
# gpg: using RSA key 0x6C38CACA20D9B392
# gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>"
# gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>"
# gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>"
# gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>"
# Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392
* remotes/dgibson/tags/ppc-for-2.8-20161028: (73 commits)
ppc: allow certain HV interrupts to be delivered to guests
spapr: Memory hot-unplug support
spapr: use count+index for memory hotplug
spapr: Add DRC count indexed hotplug identifier type
spapr: add hotplug interrupt machine options
spapr_events: add support for dedicated hotplug event source
spapr: update spapr hotplug documentation
target-ppc: Add xvcmpnesp, xvcmpnedp instructions
target-ppc: add xscmp[eq,gt,ge,ne]dp instructions
tests: Add pseries machine to the prom-env-test, too
spapr_nvram: Pre-initialize the NVRAM to support the -prom-env parameter
libqos: Change PCI accessors to take opaque BAR handle
tests: Don't assume structure of PCI IO base in ahci-test
tests: Use qpci_mem{read,write} in ivshmem-test
libqos: Add 64-bit PCI IO accessors
tests: Clean up IO handling in ide-test
libqos: Implement mmio accessors in terms of mem{read,write}
libqos: Add streaming accessors for PCI MMIO
tests: Adjust tco-test to use qpci_legacy_iomap()
libqos: Better handling of PCI legacy IO
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
87 files changed, 4981 insertions, 1713 deletions
diff --git a/.gitmodules b/.gitmodules index 9da9ede261..ca323b4d87 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "roms/u-boot"] path = roms/u-boot url = git://git.qemu-project.org/u-boot.git +[submodule "roms/skiboot"] + path = roms/skiboot + url = git://git.qemu.org/skiboot.git diff --git a/MAINTAINERS b/MAINTAINERS index b01fec0a7b..280ee1fcd2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -656,6 +656,7 @@ F: include/hw/*/xics* F: pc-bios/spapr-rtas/* F: pc-bios/spapr-rtas.bin F: pc-bios/slof.bin +F: pc-bios/skiboot.lid F: docs/specs/ppc-spapr-hcalls.txt F: docs/specs/ppc-spapr-hotplug.txt F: tests/spapr* @@ -421,7 +421,7 @@ qemu-icon.bmp qemu_logo_no_text.svg \ bamboo.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ s390-ccw.img \ -spapr-rtas.bin slof.bin \ +spapr-rtas.bin slof.bin skiboot.lid \ palcode-clipper \ u-boot.e500 else @@ -6131,6 +6131,7 @@ FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" FILES="$FILES pc-bios/qemu-icon.bmp" for bios_file in \ $source_path/pc-bios/*.bin \ + $source_path/pc-bios/*.lid \ $source_path/pc-bios/*.aml \ $source_path/pc-bios/*.rom \ $source_path/pc-bios/*.dtb \ diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index db5a4d6f5e..67a9bcaa67 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -39,6 +39,7 @@ CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_PSERIES=y +CONFIG_POWERNV=y CONFIG_PREP=y CONFIG_MAC=y CONFIG_E500=y diff --git a/docs/specs/ppc-spapr-hotplug.txt b/docs/specs/ppc-spapr-hotplug.txt index 631b0cadae..f57e2a09c6 100644 --- a/docs/specs/ppc-spapr-hotplug.txt +++ b/docs/specs/ppc-spapr-hotplug.txt @@ -233,12 +233,27 @@ tools by host-level management such as an HMC. This level of management is not applicable to PowerKVM, hence the reason for extending the notification framework to support hotplug events. -Note that these events are not yet formally part of the PAPR+ specification, -but support for this format has already been implemented in DR-related -guest tools such as powerpc-utils/librtas, as well as kernel patches that have -been submitted to handle in-kernel processing of memory/cpu-related hotplug -events[1], and is planned for formal inclusion is PAPR+ specification. The -hotplug-specific payload is QEMU implemented as follows (with all values +The format for these EPOW-signalled events is described below under +"hotplug/unplug event structure". Note that these events are not +formally part of the PAPR+ specification, and have been superseded by a +newer format, also described below under "hotplug/unplug event structure", +and so are now deemed a "legacy" format. The formats are similar, but the +"modern" format contains additional fields/flags, which are denoted for the +purposes of this documentation with "#ifdef GUEST_SUPPORTS_MODERN" guards. + +QEMU should assume support only for "legacy" fields/flags unless the guest +advertises support for the "modern" format via ibm,client-architecture-support +hcall by setting byte 5, bit 6 of it's ibm,architecture-vec-5 option vector +structure (as described by LoPAPR v11, B.6.2.3). As with "legacy" format events, +"modern" format events are surfaced to the guest via check-exception RTAS calls, +but use a dedicated event source to signal the guest. This event source is +advertised to the guest by the addition of a "hot-plug-events" node under +"/event-sources" node of the guest's device tree using the standard format +described in LoPAPR v11, B.6.12.1. + +== hotplug/unplug event structure == + +The hotplug-specific payload in QEMU is implemented as follows (with all values encoded in big-endian format): struct rtas_event_log_v6_hp { @@ -263,14 +278,23 @@ struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_HP_ACTION_ADD 1 #define RTAS_LOG_V6_HP_ACTION_REMOVE 2 uint8_t hotplug_action; /* action (add/remove) */ -#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 -#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 -#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 +#define RTAS_LOG_V6_HP_ID_DRC_NAME 1 +#define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 +#define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 +#ifdef GUEST_SUPPORTS_MODERN +#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4 +#endif uint8_t hotplug_identifier; /* type of the resource identifier, * which serves as the discriminator * for the 'drc' union field below */ +#ifdef GUEST_SUPPORTS_MODERN + uint8_t capabilities; /* capability flags, currently unused + * by QEMU + */ +#else uint8_t reserved; +#endif union { uint32_t index; /* DRC index of resource to take action * on @@ -278,6 +302,19 @@ struct rtas_event_log_v6_hp { uint32_t count; /* number of DR resources to take * action on (guest chooses which) */ +#ifdef GUEST_SUPPORTS_MODERN + struct { + uint32_t count; /* number of DR resources to take + * action on + */ + uint32_t index; /* DRC index of first resource to take + * action on. guest will take action + * on DRC index <index> through + * DRC index <index + count - 1> in + * sequential order + */ + } count_indexed; +#endif char name[1]; /* string representing the name of the * DRC to take action on */ diff --git a/hw/input/adb.c b/hw/input/adb.c index 3d39368909..43d3205472 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -396,9 +396,15 @@ static int adb_kbd_request(ADBDevice *d, uint8_t *obuf, d->devaddr = buf[1] & 0xf; break; default: - /* XXX: check this */ d->devaddr = buf[1] & 0xf; - d->handler = buf[2]; + /* we support handlers: + * 1: Apple Standard Keyboard + * 2: Apple Extended Keyboard (LShift = RShift) + * 3: Apple Extended Keyboard (LShift != RShift) + */ + if (buf[2] == 1 || buf[2] == 2 || buf[2] == 3) { + d->handler = buf[2]; + } break; } } @@ -437,6 +443,7 @@ static void adb_keyboard_event(DeviceState *dev, QemuConsole *src, if (qcode >= ARRAY_SIZE(qcode_to_adb_keycode)) { return; } + /* FIXME: take handler into account when translating qcode */ keycode = qcode_to_adb_keycode[qcode]; if (keycode == NO_KEY) { /* We don't want to send this to the guest */ ADB_DPRINTF("Ignoring NO_KEY\n"); @@ -631,8 +638,21 @@ static int adb_mouse_request(ADBDevice *d, uint8_t *obuf, d->devaddr = buf[1] & 0xf; break; default: - /* XXX: check this */ d->devaddr = buf[1] & 0xf; + /* we support handlers: + * 0x01: Classic Apple Mouse Protocol / 100 cpi operations + * 0x02: Classic Apple Mouse Protocol / 200 cpi operations + * we don't support handlers (at least): + * 0x03: Mouse systems A3 trackball + * 0x04: Extended Apple Mouse Protocol + * 0x2f: Microspeed mouse + * 0x42: Macally + * 0x5f: Microspeed mouse + * 0x66: Microspeed mouse + */ + if (buf[2] == 1 || buf[2] == 2) { + d->handler = buf[2]; + } break; } } diff --git a/hw/intc/xics.c b/hw/intc/xics.c index f40b00003a..095c16a300 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -35,6 +35,8 @@ #include "hw/ppc/xics.h" #include "qemu/error-report.h" #include "qapi/visitor.h" +#include "monitor/monitor.h" +#include "hw/intc/intc.h" int xics_get_cpu_index_by_dt_id(int cpu_dt_id) { @@ -90,6 +92,47 @@ void xics_cpu_setup(XICSState *xics, PowerPCCPU *cpu) } } +static void xics_common_pic_print_info(InterruptStatsProvider *obj, + Monitor *mon) +{ + XICSState *xics = XICS_COMMON(obj); + ICSState *ics; + uint32_t i; + + for (i = 0; i < xics->nr_servers; i++) { + ICPState *icp = &xics->ss[i]; + + if (!icp->output) { + continue; + } + monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", + i, icp->xirr, icp->xirr_owner, + icp->pending_priority, icp->mfrr); + } + + QLIST_FOREACH(ics, &xics->ics, list) { + monitor_printf(mon, "ICS %4x..%4x %p\n", + ics->offset, ics->offset + ics->nr_irqs - 1, ics); + + if (!ics->irqs) { + continue; + } + + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = ics->irqs + i; + + if (!(irq->flags & XICS_FLAGS_IRQ_MASK)) { + continue; + } + monitor_printf(mon, " %4x %s %02x %02x\n", + ics->offset + i, + (irq->flags & XICS_FLAGS_IRQ_LSI) ? + "LSI" : "MSI", + irq->priority, irq->status); + } + } +} + /* * XICS Common class - parent for emulated XICS and KVM-XICS */ @@ -140,6 +183,25 @@ static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, const char *name, info->set_nr_irqs(xics, value, errp); } +void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, + const char *typename, Error **errp) +{ + int i; + + xics->nr_servers = nr_servers; + + xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState)); + for (i = 0; i < xics->nr_servers; i++) { + char name[32]; + ICPState *icp = &xics->ss[i]; + + object_initialize(icp, sizeof(*icp), typename); + snprintf(name, sizeof(name), "icp[%d]", i); + object_property_add_child(OBJECT(xics), name, OBJECT(icp), errp); + icp->xics = xics; + } +} + static void xics_prop_get_nr_servers(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -155,7 +217,7 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v, Error **errp) { XICSState *xics = XICS_COMMON(obj); - XICSStateClass *info = XICS_COMMON_GET_CLASS(xics); + XICSStateClass *xsc = XICS_COMMON_GET_CLASS(xics); Error *error = NULL; int64_t value; @@ -170,8 +232,8 @@ static void xics_prop_set_nr_servers(Object *obj, Visitor *v, return; } - assert(info->set_nr_servers); - info->set_nr_servers(xics, value, errp); + assert(xsc->set_nr_servers); + xsc->set_nr_servers(xics, value, errp); } static void xics_common_initfn(Object *obj) @@ -190,8 +252,10 @@ static void xics_common_initfn(Object *obj) static void xics_common_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); dc->reset = xics_common_reset; + ic->print_info = xics_common_pic_print_info; } static const TypeInfo xics_common_info = { @@ -201,6 +265,10 @@ static const TypeInfo xics_common_info = { .class_size = sizeof(XICSStateClass), .instance_init = xics_common_initfn, .class_init = xics_common_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_INTERRUPT_STATS_PROVIDER }, + { } + }, }; /* @@ -258,22 +326,20 @@ static void icp_check_ipi(ICPState *ss) qemu_irq_raise(ss->output); } -static void icp_resend(XICSState *xics, int server) +static void icp_resend(ICPState *ss) { - ICPState *ss = xics->ss + server; ICSState *ics; if (ss->mfrr < CPPR(ss)) { icp_check_ipi(ss); } - QLIST_FOREACH(ics, &xics->ics, list) { + QLIST_FOREACH(ics, &ss->xics->ics, list) { ics_resend(ics); } } -void icp_set_cppr(XICSState *xics, int server, uint8_t cppr) +void icp_set_cppr(ICPState *ss, uint8_t cppr) { - ICPState *ss = xics->ss + server; uint8_t old_cppr; uint32_t old_xisr; @@ -293,15 +359,13 @@ void icp_set_cppr(XICSState *xics, int server, uint8_t cppr) } } else { if (!XISR(ss)) { - icp_resend(xics, server); + icp_resend(ss); } } } -void icp_set_mfrr(XICSState *xics, int server, uint8_t mfrr) +void icp_set_mfrr(ICPState *ss, uint8_t mfrr) { - ICPState *ss = xics->ss + server; - ss->mfrr = mfrr; if (mfrr < CPPR(ss)) { icp_check_ipi(ss); @@ -330,23 +394,22 @@ uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr) return ss->xirr; } -void icp_eoi(XICSState *xics, int server, uint32_t xirr) +void icp_eoi(ICPState *ss, uint32_t xirr) { - ICPState *ss = xics->ss + server; ICSState *ics; uint32_t irq; /* Send EOI -> ICS */ ss->xirr = (ss->xirr & ~CPPR_MASK) | (xirr & CPPR_MASK); - trace_xics_icp_eoi(server, xirr, ss->xirr); + trace_xics_icp_eoi(ss->cs->cpu_index, xirr, ss->xirr); irq = xirr & XISR_MASK; - QLIST_FOREACH(ics, &xics->ics, list) { + QLIST_FOREACH(ics, &ss->xics->ics, list) { if (ics_valid_irq(ics, irq)) { ics_eoi(ics, irq); } } if (!XISR(ss)) { - icp_resend(xics, server); + icp_resend(ss); } } @@ -605,7 +668,7 @@ static int ics_simple_post_load(ICSState *ics, int version_id) int i; for (i = 0; i < ics->xics->nr_servers; i++) { - icp_resend(ics->xics, i); + icp_resend(&ics->xics->ss[i]); } return 0; diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 9c2f198fd1..17694eaa87 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -373,18 +373,7 @@ static void xics_kvm_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, static void xics_kvm_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp) { - int i; - - xics->nr_servers = nr_servers; - - xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState)); - for (i = 0; i < xics->nr_servers; i++) { - char buffer[32]; - object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_KVM_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]), - errp); - } + xics_set_nr_servers(xics, nr_servers, TYPE_KVM_ICP, errp); } static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index e8d0623c2c..2e3f1c5e95 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -32,6 +32,7 @@ #include "qemu/timer.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" +#include "hw/ppc/fdt.h" #include "qapi/visitor.h" #include "qapi/error.h" @@ -43,9 +44,10 @@ static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; target_ulong cppr = args[0]; - icp_set_cppr(spapr->xics, cs->cpu_index, cppr); + icp_set_cppr(icp, cppr); return H_SUCCESS; } @@ -59,7 +61,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } - icp_set_mfrr(spapr->xics, server, mfrr); + icp_set_mfrr(spapr->xics->ss + server, mfrr); return H_SUCCESS; } @@ -67,7 +69,8 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); - uint32_t xirr = icp_accept(spapr->xics->ss + cs->cpu_index); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; + uint32_t xirr = icp_accept(icp); args[0] = xirr; return H_SUCCESS; @@ -77,8 +80,8 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); - ICPState *ss = &spapr->xics->ss[cs->cpu_index]; - uint32_t xirr = icp_accept(ss); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; + uint32_t xirr = icp_accept(icp); args[0] = xirr; args[1] = cpu_get_host_ticks(); @@ -89,9 +92,10 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; target_ulong xirr = args[0]; - icp_eoi(spapr->xics, cs->cpu_index, xirr); + icp_eoi(icp, xirr); return H_SUCCESS; } @@ -99,8 +103,9 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); + ICPState *icp = &spapr->xics->ss[cs->cpu_index]; uint32_t mfrr; - uint32_t xirr = icp_ipoll(spapr->xics->ss + cs->cpu_index, &mfrr); + uint32_t xirr = icp_ipoll(icp, &mfrr); args[0] = xirr; args[1] = mfrr; @@ -249,18 +254,7 @@ static void xics_spapr_set_nr_irqs(XICSState *xics, uint32_t nr_irqs, static void xics_spapr_set_nr_servers(XICSState *xics, uint32_t nr_servers, Error **errp) { - int i; - - xics->nr_servers = nr_servers; - - xics->ss = g_malloc0(xics->nr_servers * sizeof(ICPState)); - for (i = 0; i < xics->nr_servers; i++) { - char buffer[32]; - object_initialize(&xics->ss[i], sizeof(xics->ss[i]), TYPE_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(xics), buffer, OBJECT(&xics->ss[i]), - errp); - } + xics_set_nr_servers(xics, nr_servers, TYPE_ICP, errp); } static void xics_spapr_realize(DeviceState *dev, Error **errp) @@ -456,6 +450,27 @@ void xics_spapr_free(XICSState *xics, int irq, int num) } } +void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle) +{ + uint32_t interrupt_server_ranges_prop[] = { + 0, cpu_to_be32(xics->nr_servers), + }; + int node; + + _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); + + _FDT(fdt_setprop_string(fdt, node, "device_type", + "PowerPC-External-Interrupt-Presentation")); + _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,ppc-xicp")); + _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); + _FDT(fdt_setprop(fdt, node, "ibm,interrupt-server-ranges", + interrupt_server_ranges_prop, + sizeof(interrupt_server_ranges_prop))); + _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); + _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle)); + _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle)); +} + static void xics_spapr_register_types(void) { type_register_static(&xics_spapr_info); diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index e9a66940e0..c018f6b2ff 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -1,5 +1,6 @@ common-obj-$(CONFIG_DS1225Y) += ds1225y.o common-obj-y += eeprom93xx.o common-obj-y += fw_cfg.o +common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o obj-$(CONFIG_PSERIES) += spapr_nvram.o diff --git a/hw/nvram/chrp_nvram.c b/hw/nvram/chrp_nvram.c new file mode 100644 index 0000000000..3837510dd2 --- /dev/null +++ b/hw/nvram/chrp_nvram.c @@ -0,0 +1,85 @@ +/* + * Common Hardware Reference Platform NVRAM helper functions. + * + * The CHRP NVRAM layout is used by OpenBIOS and SLOF. See CHRP + * specification, chapter 8, or the LoPAPR specification for details + * about the NVRAM layout. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include "hw/hw.h" +#include "hw/nvram/chrp_nvram.h" +#include "sysemu/sysemu.h" + +static int chrp_nvram_set_var(uint8_t *nvram, int addr, const char *str) +{ + int len; + + len = strlen(str) + 1; + memcpy(&nvram[addr], str, len); + + return addr + len; +} + +/** + * Create a "system partition", used for the Open Firmware + * environment variables. + */ +int chrp_nvram_create_system_partition(uint8_t *data, int min_len) +{ + ChrpNvramPartHdr *part_header; + unsigned int i; + int end; + + part_header = (ChrpNvramPartHdr *)data; + part_header->signature = CHRP_NVPART_SYSTEM; + pstrcpy(part_header->name, sizeof(part_header->name), "system"); + + end = sizeof(ChrpNvramPartHdr); + for (i = 0; i < nb_prom_envs; i++) { + end = chrp_nvram_set_var(data, end, prom_envs[i]); + } + + /* End marker */ + data[end++] = '\0'; + + end = (end + 15) & ~15; + /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for + new variables. */ + if (end < min_len) { + end = min_len; + } + chrp_nvram_finish_partition(part_header, end); + + return end; +} + +/** + * Create a "free space" partition + */ +int chrp_nvram_create_free_partition(uint8_t *data, int len) +{ + ChrpNvramPartHdr *part_header; + + part_header = (ChrpNvramPartHdr *)data; + part_header->signature = CHRP_NVPART_FREE; + pstrcpy(part_header->name, sizeof(part_header->name), "free"); + + chrp_nvram_finish_partition(part_header, len); + + return len; +} diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 24f61212ba..63f9ed1d82 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -24,8 +24,7 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" -#include "hw/nvram/openbios_firmware_abi.h" -#include "sysemu/sysemu.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/ppc/mac.h" #include "qemu/cutils.h" #include <zlib.h> @@ -146,38 +145,14 @@ static void macio_nvram_register_types(void) static void pmac_format_nvram_partition_of(MacIONVRAMState *nvr, int off, int len) { - unsigned int i; - uint32_t start = off, end; - struct OpenBIOS_nvpart_v1 *part_header; - - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(nvr->data, end, prom_envs[i]); - - // End marker - nvr->data[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - /* XXX: OpenBIOS is not able to grow up a partition. Leave some space for - new variables. */ - if (end < DEF_SYSTEM_SIZE) - end = DEF_SYSTEM_SIZE; - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&nvr->data[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = len; - OpenBIOS_finish_partition(part_header, end - start); + int sysp_end; + + /* OpenBIOS nvram variables partition */ + sysp_end = chrp_nvram_create_system_partition(&nvr->data[off], + DEF_SYSTEM_SIZE) + off; + + /* Free space partition */ + chrp_nvram_create_free_partition(&nvr->data[sysp_end], len - sysp_end); } #define OSX_NVRAM_SIGNATURE (0x5A) @@ -187,15 +162,15 @@ static void pmac_format_nvram_partition_osx(MacIONVRAMState *nvr, int off, int len) { uint32_t start = off; - struct OpenBIOS_nvpart_v1 *part_header; + ChrpNvramPartHdr *part_header; unsigned char *data = &nvr->data[start]; /* empty partition */ - part_header = (struct OpenBIOS_nvpart_v1 *)data; + part_header = (ChrpNvramPartHdr *)data; part_header->signature = OSX_NVRAM_SIGNATURE; pstrcpy(part_header->name, sizeof(part_header->name), "wwwwwwwwwwww"); - OpenBIOS_finish_partition(part_header, len); + chrp_nvram_finish_partition(part_header, len); /* Generation */ stl_be_p(&data[20], 2); diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index 4de5f705d8..eb42ea323f 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -31,6 +31,7 @@ #include "sysemu/block-backend.h" #include "sysemu/device_tree.h" #include "hw/sysbus.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -162,6 +163,11 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) error_setg(errp, "can't read spapr-nvram contents"); return; } + } else if (nb_prom_envs > 0) { + /* Create a system partition to pass the -prom-env variables */ + chrp_nvram_create_system_partition(nvram->buf, MIN_NVRAM_SIZE / 4); + chrp_nvram_create_free_partition(&nvram->buf[MIN_NVRAM_SIZE / 4], + nvram->size - MIN_NVRAM_SIZE / 4); } spapr_rtas_register(RTAS_NVRAM_FETCH, "nvram-fetch", rtas_nvram_fetch); diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 99a0d4e581..8025129377 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -4,7 +4,9 @@ obj-y += ppc.o ppc_booke.o fdt.o obj-$(CONFIG_PSERIES) += spapr.o spapr_vio.o spapr_events.o obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_iommu.o spapr_rtas.o obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o -obj-$(CONFIG_PSERIES) += spapr_cpu_core.o +obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o +# IBM PowerNV +obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) obj-y += spapr_pci_vfio.o endif diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c new file mode 100644 index 0000000000..82276e0857 --- /dev/null +++ b/hw/ppc/pnv.c @@ -0,0 +1,819 @@ +/* + * QEMU PowerPC PowerNV machine model + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "sysemu/numa.h" +#include "hw/hw.h" +#include "target-ppc/cpu.h" +#include "qemu/log.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_core.h" +#include "hw/loader.h" +#include "exec/address-spaces.h" +#include "qemu/cutils.h" +#include "qapi/visitor.h" + +#include "hw/ppc/pnv_xscom.h" + +#include "hw/isa/isa.h" +#include "hw/char/serial.h" +#include "hw/timer/mc146818rtc.h" + +#include <libfdt.h> + +#define FDT_MAX_SIZE 0x00100000 + +#define FW_FILE_NAME "skiboot.lid" +#define FW_LOAD_ADDR 0x0 +#define FW_MAX_SIZE 0x00400000 + +#define KERNEL_LOAD_ADDR 0x20000000 +#define INITRD_LOAD_ADDR 0x40000000 + +/* + * On Power Systems E880 (POWER8), the max cpus (threads) should be : + * 4 * 4 sockets * 12 cores * 8 threads = 1536 + * Let's make it 2^11 + */ +#define MAX_CPUS 2048 + +/* + * Memory nodes are created by hostboot, one for each range of memory + * 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) +{ + char *mem_name; + uint64_t mem_reg_property[2]; + int off; + + mem_reg_property[0] = cpu_to_be64(start); + mem_reg_property[1] = cpu_to_be64(size); + + mem_name = g_strdup_printf("memory@%"HWADDR_PRIx, start); + off = fdt_add_subnode(fdt, 0, mem_name); + g_free(mem_name); + + _FDT((fdt_setprop_string(fdt, off, "device_type", "memory"))); + _FDT((fdt_setprop(fdt, off, "reg", mem_reg_property, + sizeof(mem_reg_property)))); + _FDT((fdt_setprop_cell(fdt, off, "ibm,chip-id", chip_id))); +} + +static int get_cpus_node(void *fdt) +{ + int cpus_offset = fdt_path_offset(fdt, "/cpus"); + + if (cpus_offset < 0) { + cpus_offset = fdt_add_subnode(fdt, fdt_path_offset(fdt, "/"), + "cpus"); + if (cpus_offset) { + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#address-cells", 0x1))); + _FDT((fdt_setprop_cell(fdt, cpus_offset, "#size-cells", 0x0))); + } + } + _FDT(cpus_offset); + return cpus_offset; +} + +/* + * The PowerNV cores (and threads) need to use real HW ids and not an + * incremental index like it has been done on other platforms. This HW + * id is stored in the CPU PIR, it is used to create cpu nodes in the + * device tree, used in XSCOM to address cores and in interrupt + * servers. + */ +static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) +{ + CPUState *cs = CPU(DEVICE(pc->threads)); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + PowerPCCPU *cpu = POWERPC_CPU(cs); + int smt_threads = ppc_get_compat_smt_threads(cpu); + CPUPPCState *env = &cpu->env; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + uint32_t servers_prop[smt_threads]; + int i; + uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40), + 0xffffffff, 0xffffffff}; + uint32_t tbfreq = PNV_TIMEBASE_FREQ; + uint32_t cpufreq = 1000000000; + uint32_t page_sizes_prop[64]; + size_t page_sizes_prop_size; + const uint8_t pa_features[] = { 24, 0, + 0xf6, 0x3f, 0xc7, 0xc0, 0x80, 0xf0, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, + 0x80, 0x00, 0x80, 0x00, 0x80, 0x00 }; + int offset; + char *nodename; + int cpus_offset = get_cpus_node(fdt); + + nodename = g_strdup_printf("%s@%x", dc->fw_name, pc->pir); + offset = fdt_add_subnode(fdt, cpus_offset, nodename); + _FDT(offset); + g_free(nodename); + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", chip->chip_id))); + + _FDT((fdt_setprop_cell(fdt, offset, "reg", pc->pir))); + _FDT((fdt_setprop_cell(fdt, offset, "ibm,pir", pc->pir))); + _FDT((fdt_setprop_string(fdt, offset, "device_type", "cpu"))); + + _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", env->spr[SPR_PVR]))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-block-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-line-size", + env->dcache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-block-size", + env->icache_line_size))); + _FDT((fdt_setprop_cell(fdt, offset, "i-cache-line-size", + env->icache_line_size))); + + if (pcc->l1_dcache_size) { + _FDT((fdt_setprop_cell(fdt, offset, "d-cache-size", + pcc->l1_dcache_size))); + } else { + error_report("Warning: 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"); + } + + _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_string(fdt, offset, "status", "okay"))); + _FDT((fdt_setprop(fdt, offset, "64-bit", NULL, 0))); + + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_setprop(fdt, offset, "ibm,purr", NULL, 0))); + } + + if (env->mmu_model & POWERPC_MMU_1TSEG) { + _FDT((fdt_setprop(fdt, offset, "ibm,processor-segment-sizes", + segs, sizeof(segs)))); + } + + /* Advertise VMX/VSX (vector extensions) if available + * 0 / no property == no vector extensions + * 1 == VMX / Altivec available + * 2 == VSX available */ + if (env->insns_flags & PPC_ALTIVEC) { + uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1; + + _FDT((fdt_setprop_cell(fdt, offset, "ibm,vmx", vmx))); + } + + /* Advertise DFP (Decimal Floating Point) if available + * 0 / no property == no DFP + * 1 == DFP available */ + if (env->insns_flags2 & PPC2_DFP) { + _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)); + if (page_sizes_prop_size) { + _FDT((fdt_setprop(fdt, offset, "ibm,segment-page-sizes", + page_sizes_prop, page_sizes_prop_size))); + } + + _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", + pa_features, sizeof(pa_features)))); + + if (cpu->cpu_version) { + _FDT((fdt_setprop_cell(fdt, offset, "cpu-version", cpu->cpu_version))); + } + + /* Build interrupt servers properties */ + for (i = 0; i < smt_threads; i++) { + servers_prop[i] = cpu_to_be32(pc->pir + i); + } + _FDT((fdt_setprop(fdt, offset, "ibm,ppc-interrupt-server#s", + servers_prop, sizeof(servers_prop)))); +} + +static void powernv_populate_chip(PnvChip *chip, void *fdt) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + char *typename = pnv_core_typename(pcc->cpu_model); + size_t typesize = object_type_get_instance_size(typename); + int i; + + pnv_xscom_populate(chip, fdt, 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); + } + + if (chip->ram_size) { + powernv_populate_memory_node(fdt, chip->chip_id, chip->ram_start, + chip->ram_size); + } + g_free(typename); +} + +static void *powernv_create_fdt(MachineState *machine) +{ + const char plat_compat[] = "qemu,powernv\0ibm,powernv"; + PnvMachineState *pnv = POWERNV_MACHINE(machine); + void *fdt; + char *buf; + int off; + int i; + + fdt = g_malloc0(FDT_MAX_SIZE); + _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + + /* 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)"))); + _FDT((fdt_setprop(fdt, 0, "compatible", plat_compat, + sizeof(plat_compat)))); + + buf = qemu_uuid_unparse_strdup(&qemu_uuid); + _FDT((fdt_setprop_string(fdt, 0, "vm,uuid", buf))); + if (qemu_uuid_set) { + _FDT((fdt_property_string(fdt, "system-id", buf))); + } + g_free(buf); + + off = fdt_add_subnode(fdt, 0, "chosen"); + if (machine->kernel_cmdline) { + _FDT((fdt_setprop_string(fdt, off, "bootargs", + machine->kernel_cmdline))); + } + + if (pnv->initrd_size) { + uint32_t start_prop = cpu_to_be32(pnv->initrd_base); + uint32_t end_prop = cpu_to_be32(pnv->initrd_base + pnv->initrd_size); + + _FDT((fdt_setprop(fdt, off, "linux,initrd-start", + &start_prop, sizeof(start_prop)))); + _FDT((fdt_setprop(fdt, off, "linux,initrd-end", + &end_prop, sizeof(end_prop)))); + } + + /* Populate device tree for each chip */ + for (i = 0; i < pnv->num_chips; i++) { + powernv_populate_chip(pnv->chips[i], fdt); + } + return fdt; +} + +static void ppc_powernv_reset(void) +{ + MachineState *machine = MACHINE(qdev_get_machine()); + void *fdt; + + qemu_devices_reset(); + + fdt = powernv_create_fdt(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; + ISABus *isa_bus; + qemu_irq *irqs; + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + /* let isa_bus_new() create its own bridge on SysBus otherwise + * devices speficied on the command line won't find the bus and + * will fail to create. + */ + 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); + } + + isa_bus_irqs(isa_bus, irqs); + return isa_bus; +} + +static void ppc_powernv_init(MachineState *machine) +{ + PnvMachineState *pnv = POWERNV_MACHINE(machine); + MemoryRegion *ram; + char *fw_filename; + long fw_size; + int i; + char *chip_typename; + + /* allocate RAM */ + if (machine->ram_size < (1 * G_BYTE)) { + error_report("Warning: skiboot may not work with < 1GB of RAM"); + } + + ram = g_new(MemoryRegion, 1); + memory_region_allocate_system_memory(ram, NULL, "ppc_powernv.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), 0, ram); + + /* load skiboot firmware */ + if (bios_name == NULL) { + bios_name = FW_FILE_NAME; + } + + fw_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + + fw_size = load_image_targphys(fw_filename, FW_LOAD_ADDR, FW_MAX_SIZE); + if (fw_size < 0) { + hw_error("qemu: could not load OPAL '%s'\n", fw_filename); + exit(1); + } + g_free(fw_filename); + + /* load kernel */ + if (machine->kernel_filename) { + long kernel_size; + + kernel_size = load_image_targphys(machine->kernel_filename, + KERNEL_LOAD_ADDR, 0x2000000); + if (kernel_size < 0) { + hw_error("qemu: could not load kernel'%s'\n", + machine->kernel_filename); + exit(1); + } + } + + /* load initrd */ + if (machine->initrd_filename) { + pnv->initrd_base = INITRD_LOAD_ADDR; + pnv->initrd_size = load_image_targphys(machine->initrd_filename, + pnv->initrd_base, 0x10000000); /* 128MB max */ + if (pnv->initrd_size < 0) { + error_report("qemu: could not load initial ram disk '%s'", + machine->initrd_filename); + exit(1); + } + } + + /* 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); + 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); + exit(1); + } + + pnv->chips = g_new0(PnvChip *, pnv->num_chips); + for (i = 0; i < pnv->num_chips; i++) { + char chip_name[32]; + Object *chip = object_new(chip_typename); + + pnv->chips[i] = PNV_CHIP(chip); + + /* TODO: put all the memory in one node on chip 0 until we find a + * way to specify different ranges for each chip + */ + if (i == 0) { + object_property_set_int(chip, machine->ram_size, "ram-size", + &error_fatal); + } + + snprintf(chip_name, sizeof(chip_name), "chip[%d]", PNV_CHIP_HWID(i)); + object_property_add_child(OBJECT(pnv), chip_name, chip, &error_fatal); + object_property_set_int(chip, PNV_CHIP_HWID(i), "chip-id", + &error_fatal); + object_property_set_int(chip, smp_cores, "nr-cores", &error_fatal); + object_property_set_bool(chip, true, "realized", &error_fatal); + } + g_free(chip_typename); + + /* Instantiate ISA bus on chip 0 */ + pnv->isa_bus = pnv_isa_create(pnv->chips[0]); + + /* Create serial port */ + serial_hds_isa_init(pnv->isa_bus, 0, MAX_SERIAL_PORTS); + + /* Create an RTC ISA device too */ + rtc_init(pnv->isa_bus, 2000, NULL); +} + +/* + * 0:21 Reserved - Read as zeros + * 22:24 Chip ID + * 25:28 Core number + * 29:31 Thread ID + */ +static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) +{ + return (chip->chip_id << 7) | (core_id << 3); +} + +/* + * 0:48 Reserved - Read as zeroes + * 49:52 Node ID + * 53:55 Chip ID + * 56 Reserved - Read as zero + * 57:61 Core number + * 62:63 Thread ID + * + * We only care about the lower bits. uint32_t is fine for the moment. + */ +static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) +{ + return (chip->chip_id << 8) | (core_id << 2); +} + +/* Allowed core identifiers on a POWER8 Processor Chip : + * + * <EX0 reserved> + * EX1 - Venice only + * EX2 - Venice only + * EX3 - Venice only + * EX4 + * EX5 + * EX6 + * <EX7,8 reserved> <reserved> + * EX9 - Venice only + * EX10 - Venice only + * EX11 - Venice only + * EX12 + * EX13 + * EX14 + * <EX15 reserved> + */ +#define POWER8E_CORE_MASK (0x7070ull) +#define POWER8_CORE_MASK (0x7e7eull) + +/* + * POWER9 has 24 cores, ids starting at 0x20 + */ +#define POWER9_CORE_MASK (0xffffff00000000ull) + +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; + 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; + 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; + 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->cores_mask = POWER9_CORE_MASK; + k->core_pir = pnv_chip_core_pir_p9; + k->xscom_base = 0x00603fc00000000ull; + 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); + int cores_max; + + /* + * No custom mask for this chip, let's use the default one from * + * the chip class + */ + if (!chip->cores_mask) { + chip->cores_mask = pcc->cores_mask; + } + + /* filter alien core ids ! some are reserved */ + if ((chip->cores_mask & pcc->cores_mask) != chip->cores_mask) { + error_setg(errp, "warning: invalid core mask for chip Ox%"PRIx64" !", + chip->cores_mask); + return; + } + chip->cores_mask &= pcc->cores_mask; + + /* now that we have a sane layout, let check the number of cores */ + cores_max = hweight_long(chip->cores_mask); + if (chip->nr_cores > cores_max) { + error_setg(errp, "warning: too many cores for chip ! Limit is %d", + cores_max); + return; + } +} + +static void pnv_chip_init(Object *obj) +{ + PnvChip *chip = PNV_CHIP(obj); + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + chip->xscom_base = pcc->xscom_base; + + object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); + object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); +} + +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); + size_t typesize = object_type_get_instance_size(typename); + int i, core_hwid; + + if (!object_class_by_name(typename)) { + error_setg(errp, "Unable to find PowerNV CPU Core '%s'", typename); + return; + } + + /* XSCOM bridge */ + pnv_xscom_realize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(chip), 0, PNV_XSCOM_BASE(chip)); + + /* Cores */ + pnv_chip_core_sanitize(chip, &error); + if (error) { + error_propagate(errp, error); + return; + } + + chip->cores = g_malloc0(typesize * 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; + + if (!(chip->cores_mask & (1ull << core_hwid))) { + continue; + } + + object_initialize(pnv_core, typesize, typename); + snprintf(core_name, sizeof(core_name), "core[%d]", core_hwid); + object_property_add_child(OBJECT(chip), core_name, OBJECT(pnv_core), + &error_fatal); + object_property_set_int(OBJECT(pnv_core), smp_threads, "nr-threads", + &error_fatal); + object_property_set_int(OBJECT(pnv_core), core_hwid, + CPU_CORE_PROP_CORE_ID, &error_fatal); + object_property_set_int(OBJECT(pnv_core), + pcc->core_pir(chip, core_hwid), + "pir", &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(core_hwid), + &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); +} + +static Property pnv_chip_properties[] = { + DEFINE_PROP_UINT32("chip-id", PnvChip, chip_id, 0), + DEFINE_PROP_UINT64("ram-start", PnvChip, ram_start, 0), + DEFINE_PROP_UINT64("ram-size", PnvChip, ram_size, 0), + DEFINE_PROP_UINT32("nr-cores", PnvChip, nr_cores, 1), + DEFINE_PROP_UINT64("cores-mask", PnvChip, cores_mask, 0x0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_chip_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = pnv_chip_realize; + dc->props = 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 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); +} + +static void pnv_set_num_chips(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + PnvMachineState *pnv = POWERNV_MACHINE(obj); + uint32_t num_chips; + Error *local_err = NULL; + + visit_type_uint32(v, name, &num_chips, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * TODO: should we decide on how many chips we can create based + * on #cores and Venice vs. Murano vs. Naples chip type etc..., + */ + if (!is_power_of_2(num_chips) || num_chips > 4) { + error_setg(errp, "invalid number of chips: '%d'", num_chips); + return; + } + + pnv->num_chips = num_chips; +} + +static void powernv_machine_initfn(Object *obj) +{ + PnvMachineState *pnv = POWERNV_MACHINE(obj); + pnv->num_chips = 1; +} + +static void powernv_machine_class_props_init(ObjectClass *oc) +{ + object_class_property_add(oc, "num-chips", "uint32_t", + pnv_get_num_chips, pnv_set_num_chips, + NULL, NULL, NULL); + object_class_property_set_description(oc, "num-chips", + "Specifies the number of processor chips", + NULL); +} + +static void powernv_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "IBM PowerNV (Non-Virtualized)"; + mc->init = ppc_powernv_init; + mc->reset = ppc_powernv_reset; + mc->max_cpus = MAX_CPUS; + 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; + + powernv_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, +}; + +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); +} + +type_init(powernv_machine_register_types) diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c new file mode 100644 index 0000000000..2acda9637d --- /dev/null +++ b/hw/ppc/pnv_core.c @@ -0,0 +1,232 @@ +/* + * QEMU PowerPC PowerNV CPU Core model + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "qapi/error.h" +#include "qemu/log.h" +#include "target-ppc/cpu.h" +#include "hw/ppc/ppc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_core.h" + +static void powernv_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + + cpu_reset(cs); + + /* + * the skiboot firmware elects a primary thread to initialize the + * system and it can be any. + */ + env->gpr[3] = PNV_FDT_ADDR; + env->nip = 0x10; + env->msr |= MSR_HVB; /* Hypervisor mode */ +} + +static void powernv_cpu_init(PowerPCCPU *cpu, Error **errp) +{ + CPUPPCState *env = &cpu->env; + int core_pir; + int thread_index = 0; /* TODO: TCG supports only one thread */ + ppc_spr_t *pir = &env->spr_cb[SPR_PIR]; + + core_pir = object_property_get_int(OBJECT(cpu), "core-pir", &error_abort); + + /* + * The PIR of a thread is the core PIR + the thread index. We will + * need to find a way to get the thread index when TCG supports + * more than 1. We could use the object name ? + */ + pir->default_value = core_pir + thread_index; + + /* Set time-base frequency to 512 MHz */ + cpu_ppc_tb_init(env, PNV_TIMEBASE_FREQ); + + qemu_register_reset(powernv_cpu_reset, cpu); +} + +/* + * These values are read by the PowerNV HW monitors under Linux + */ +#define PNV_XSCOM_EX_DTS_RESULT0 0x50000 +#define PNV_XSCOM_EX_DTS_RESULT1 0x50001 + +static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = 0; + + /* The result should be 38 C */ + switch (offset) { + case PNV_XSCOM_EX_DTS_RESULT0: + val = 0x26f024f023f0000ull; + break; + case PNV_XSCOM_EX_DTS_RESULT1: + val = 0x24f000000000000ull; + break; + default: + qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx, + addr); + } + + return val; +} + +static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int width) +{ + qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx, + addr); +} + +static const MemoryRegionOps pnv_core_xscom_ops = { + .read = pnv_core_xscom_read, + .write = pnv_core_xscom_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_core_realize_child(Object *child, Error **errp) +{ + Error *local_err = NULL; + CPUState *cs = CPU(child); + PowerPCCPU *cpu = POWERPC_CPU(cs); + + object_property_set_bool(child, true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + powernv_cpu_init(cpu, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static void pnv_core_realize(DeviceState *dev, Error **errp) +{ + PnvCore *pc = PNV_CORE(OBJECT(dev)); + CPUCore *cc = CPU_CORE(OBJECT(dev)); + PnvCoreClass *pcc = PNV_CORE_GET_CLASS(OBJECT(dev)); + const char *typename = object_class_get_name(pcc->cpu_oc); + size_t size = object_type_get_instance_size(typename); + Error *local_err = NULL; + void *obj; + int i, j; + char name[32]; + + pc->threads = g_malloc0(size * cc->nr_threads); + for (i = 0; i < cc->nr_threads; i++) { + obj = pc->threads + i * size; + + object_initialize(obj, size, typename); + + snprintf(name, sizeof(name), "thread[%d]", i); + object_property_add_child(OBJECT(pc), name, obj, &local_err); + object_property_add_alias(obj, "core-pir", OBJECT(pc), + "pir", &local_err); + if (local_err) { + goto err; + } + object_unref(obj); + } + + for (j = 0; j < cc->nr_threads; j++) { + obj = pc->threads + j * size; + + pnv_core_realize_child(obj, &local_err); + if (local_err) { + goto err; + } + } + + snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id); + pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops, + pc, name, PNV_XSCOM_EX_CORE_SIZE); + return; + +err: + while (--i >= 0) { + obj = pc->threads + i * size; + object_unparent(obj); + } + g_free(pc->threads); + error_propagate(errp, local_err); +} + +static Property pnv_core_properties[] = { + DEFINE_PROP_UINT32("pir", PnvCore, pir, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_core_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + PnvCoreClass *pcc = PNV_CORE_CLASS(oc); + + dc->realize = pnv_core_realize; + dc->props = pnv_core_properties; + pcc->cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, data); +} + +static const TypeInfo pnv_core_info = { + .name = TYPE_PNV_CORE, + .parent = TYPE_CPU_CORE, + .instance_size = sizeof(PnvCore), + .class_size = sizeof(PnvCoreClass), + .abstract = true, +}; + +static const char *pnv_core_models[] = { + "POWER8E", "POWER8", "POWER8NVL", "POWER9" +}; + +static void pnv_core_register_types(void) +{ + int i ; + + type_register_static(&pnv_core_info); + for (i = 0; i < ARRAY_SIZE(pnv_core_models); ++i) { + TypeInfo ti = { + .parent = TYPE_PNV_CORE, + .instance_size = sizeof(PnvCore), + .class_init = pnv_core_class_init, + .class_data = (void *) pnv_core_models[i], + }; + ti.name = pnv_core_typename(pnv_core_models[i]); + type_register(&ti); + g_free((void *)ti.name); + } +} + +type_init(pnv_core_register_types) + +char *pnv_core_typename(const char *model) +{ + return g_strdup_printf(TYPE_PNV_CORE "-%s", model); +} diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c new file mode 100644 index 0000000000..00dbd8b07b --- /dev/null +++ b/hw/ppc/pnv_lpc.c @@ -0,0 +1,471 @@ +/* + * QEMU PowerPC PowerNV LPC controller + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "sysemu/sysemu.h" +#include "target-ppc/cpu.h" +#include "qapi/error.h" +#include "qemu/log.h" + +#include "hw/ppc/pnv_lpc.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/fdt.h" + +#include <libfdt.h> + +enum { + ECCB_CTL = 0, + ECCB_RESET = 1, + ECCB_STAT = 2, + ECCB_DATA = 3, +}; + +/* OPB Master LS registers */ +#define OPB_MASTER_LS_IRQ_STAT 0x50 +#define OPB_MASTER_IRQ_LPC 0x00000800 +#define OPB_MASTER_LS_IRQ_MASK 0x54 +#define OPB_MASTER_LS_IRQ_POL 0x58 +#define OPB_MASTER_LS_IRQ_INPUT 0x5c + +/* LPC HC registers */ +#define LPC_HC_FW_SEG_IDSEL 0x24 +#define LPC_HC_FW_RD_ACC_SIZE 0x28 +#define LPC_HC_FW_RD_1B 0x00000000 +#define LPC_HC_FW_RD_2B 0x01000000 +#define LPC_HC_FW_RD_4B 0x02000000 +#define LPC_HC_FW_RD_16B 0x04000000 +#define LPC_HC_FW_RD_128B 0x07000000 +#define LPC_HC_IRQSER_CTRL 0x30 +#define LPC_HC_IRQSER_EN 0x80000000 +#define LPC_HC_IRQSER_QMODE 0x40000000 +#define LPC_HC_IRQSER_START_MASK 0x03000000 +#define LPC_HC_IRQSER_START_4CLK 0x00000000 +#define LPC_HC_IRQSER_START_6CLK 0x01000000 +#define LPC_HC_IRQSER_START_8CLK 0x02000000 +#define LPC_HC_IRQMASK 0x34 /* same bit defs as LPC_HC_IRQSTAT */ +#define LPC_HC_IRQSTAT 0x38 +#define LPC_HC_IRQ_SERIRQ0 0x80000000 /* all bits down to ... */ +#define LPC_HC_IRQ_SERIRQ16 0x00008000 /* IRQ16=IOCHK#, IRQ2=SMI# */ +#define LPC_HC_IRQ_SERIRQ_ALL 0xffff8000 +#define LPC_HC_IRQ_LRESET 0x00000400 +#define LPC_HC_IRQ_SYNC_ABNORM_ERR 0x00000080 +#define LPC_HC_IRQ_SYNC_NORESP_ERR 0x00000040 +#define LPC_HC_IRQ_SYNC_NORM_ERR 0x00000020 +#define LPC_HC_IRQ_SYNC_TIMEOUT_ERR 0x00000010 +#define LPC_HC_IRQ_SYNC_TARG_TAR_ERR 0x00000008 +#define LPC_HC_IRQ_SYNC_BM_TAR_ERR 0x00000004 +#define LPC_HC_IRQ_SYNC_BM0_REQ 0x00000002 +#define LPC_HC_IRQ_SYNC_BM1_REQ 0x00000001 +#define LPC_HC_ERROR_ADDRESS 0x40 + +#define LPC_OPB_SIZE 0x100000000ull + +#define ISA_IO_SIZE 0x00010000 +#define ISA_MEM_SIZE 0x10000000 +#define LPC_IO_OPB_ADDR 0xd0010000 +#define LPC_IO_OPB_SIZE 0x00010000 +#define LPC_MEM_OPB_ADDR 0xe0010000 +#define LPC_MEM_OPB_SIZE 0x10000000 +#define LPC_FW_OPB_ADDR 0xf0000000 +#define LPC_FW_OPB_SIZE 0x10000000 + +#define LPC_OPB_REGS_OPB_ADDR 0xc0010000 +#define LPC_OPB_REGS_OPB_SIZE 0x00002000 +#define LPC_HC_REGS_OPB_ADDR 0xc0012000 +#define LPC_HC_REGS_OPB_SIZE 0x00001000 + + +/* + * TODO: the "primary" cell should only be added on chip 0. This is + * how skiboot chooses the default LPC controller on multichip + * systems. + * + * It would be easly done if we can change the populate() interface to + * replace the PnvXScomInterface parameter by a PnvChip one + */ +static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) +{ + const char compat[] = "ibm,power8-lpc\0ibm,lpc"; + char *name; + int offset; + uint32_t lpc_pcba = PNV_XSCOM_LPC_BASE; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV_XSCOM_LPC_SIZE) + }; + + name = g_strdup_printf("isa@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _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))); + _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); + return 0; +} + +/* + * These read/write handlers of the OPB address space should be common + * with the P9 LPC Controller which uses direct MMIOs. + * + * TODO: rework to use address_space_stq() and address_space_ldq() + * instead. + */ +static bool opb_read(PnvLpcController *lpc, uint32_t addr, uint8_t *data, + int sz) +{ + bool success; + + /* XXX Handle access size limits and FW read caching here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, false); + + return success; +} + +static bool opb_write(PnvLpcController *lpc, uint32_t addr, uint8_t *data, + int sz) +{ + bool success; + + /* XXX Handle access size limits here */ + success = !address_space_rw(&lpc->opb_as, addr, MEMTXATTRS_UNSPECIFIED, + data, sz, true); + + return success; +} + +#define ECCB_CTL_READ (1ull << (63 - 15)) +#define ECCB_CTL_SZ_LSH (63 - 7) +#define ECCB_CTL_SZ_MASK (0xfull << ECCB_CTL_SZ_LSH) +#define ECCB_CTL_ADDR_MASK 0xffffffffu; + +#define ECCB_STAT_OP_DONE (1ull << (63 - 52)) +#define ECCB_STAT_OP_ERR (1ull << (63 - 52)) +#define ECCB_STAT_RD_DATA_LSH (63 - 37) +#define ECCB_STAT_RD_DATA_MASK (0xffffffff << ECCB_STAT_RD_DATA_LSH) + +static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) +{ + /* XXX Check for magic bits at the top, addr size etc... */ + unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; + uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; + uint8_t data[4]; + bool success; + + if (cmd & ECCB_CTL_READ) { + success = opb_read(lpc, opb_addr, data, sz); + if (success) { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (((uint64_t)data[0]) << 24 | + ((uint64_t)data[1]) << 16 | + ((uint64_t)data[2]) << 8 | + ((uint64_t)data[3])) << ECCB_STAT_RD_DATA_LSH; + } else { + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE | + (0xffffffffull << ECCB_STAT_RD_DATA_LSH); + } + } else { + data[0] = lpc->eccb_data_reg >> 24; + data[1] = lpc->eccb_data_reg >> 16; + data[2] = lpc->eccb_data_reg >> 8; + data[3] = lpc->eccb_data_reg; + + success = opb_write(lpc, opb_addr, data, sz); + lpc->eccb_stat_reg = ECCB_STAT_OP_DONE; + } + /* XXX Which error bit (if any) to signal OPB error ? */ +} + +static uint64_t pnv_lpc_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset & 3) { + case ECCB_CTL: + case ECCB_RESET: + val = 0; + break; + case ECCB_STAT: + val = lpc->eccb_stat_reg; + lpc->eccb_stat_reg = 0; + break; + case ECCB_DATA: + val = ((uint64_t)lpc->eccb_data_reg) << 32; + break; + } + return val; +} + +static void pnv_lpc_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t offset = addr >> 3; + + switch (offset & 3) { + case ECCB_CTL: + pnv_lpc_do_eccb(lpc, val); + break; + case ECCB_RESET: + /* XXXX */ + break; + case ECCB_STAT: + break; + case ECCB_DATA: + lpc->eccb_data_reg = val >> 32; + break; + } +} + +static const MemoryRegionOps pnv_lpc_xscom_ops = { + .read = pnv_lpc_xscom_read, + .write = pnv_lpc_xscom_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 uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + uint64_t val = 0xfffffffffffffffful; + + switch (addr) { + case LPC_HC_FW_SEG_IDSEL: + val = lpc->lpc_hc_fw_seg_idsel; + break; + case LPC_HC_FW_RD_ACC_SIZE: + val = lpc->lpc_hc_fw_rd_acc_size; + break; + case LPC_HC_IRQSER_CTRL: + val = lpc->lpc_hc_irqser_ctrl; + break; + case LPC_HC_IRQMASK: + val = lpc->lpc_hc_irqmask; + break; + case LPC_HC_IRQSTAT: + val = lpc->lpc_hc_irqstat; + break; + case LPC_HC_ERROR_ADDRESS: + val = lpc->lpc_hc_error_addr; + break; + default: + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } + return val; +} + +static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PnvLpcController *lpc = opaque; + + /* XXX Filter out reserved bits */ + + switch (addr) { + case LPC_HC_FW_SEG_IDSEL: + /* XXX Actually figure out how that works as this impact + * memory regions/aliases + */ + lpc->lpc_hc_fw_seg_idsel = val; + break; + case LPC_HC_FW_RD_ACC_SIZE: + lpc->lpc_hc_fw_rd_acc_size = val; + break; + case LPC_HC_IRQSER_CTRL: + lpc->lpc_hc_irqser_ctrl = val; + break; + case LPC_HC_IRQMASK: + lpc->lpc_hc_irqmask = val; + break; + case LPC_HC_IRQSTAT: + lpc->lpc_hc_irqstat &= ~val; + break; + case LPC_HC_ERROR_ADDRESS: + break; + default: + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps lpc_hc_ops = { + .read = lpc_hc_read, + .write = lpc_hc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = opaque; + uint64_t val = 0xfffffffffffffffful; + + switch (addr) { + case OPB_MASTER_LS_IRQ_STAT: + val = lpc->opb_irq_stat; + break; + case OPB_MASTER_LS_IRQ_MASK: + val = lpc->opb_irq_mask; + break; + case OPB_MASTER_LS_IRQ_POL: + val = lpc->opb_irq_pol; + break; + case OPB_MASTER_LS_IRQ_INPUT: + val = lpc->opb_irq_input; + break; + default: + qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } + + return val; +} + +static void opb_master_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = opaque; + + switch (addr) { + case OPB_MASTER_LS_IRQ_STAT: + lpc->opb_irq_stat &= ~val; + break; + case OPB_MASTER_LS_IRQ_MASK: + /* XXX Filter out reserved bits */ + lpc->opb_irq_mask = val; + break; + case OPB_MASTER_LS_IRQ_POL: + /* XXX Filter out reserved bits */ + lpc->opb_irq_pol = val; + break; + case OPB_MASTER_LS_IRQ_INPUT: + /* Read only */ + break; + default: + qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps opb_master_ops = { + .read = opb_master_read, + .write = opb_master_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void pnv_lpc_realize(DeviceState *dev, Error **errp) +{ + PnvLpcController *lpc = PNV_LPC(dev); + + /* Reg inits */ + lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; + + /* Create address space and backing MR for the OPB bus */ + memory_region_init(&lpc->opb_mr, OBJECT(dev), "lpc-opb", 0x100000000ull); + address_space_init(&lpc->opb_as, &lpc->opb_mr, "lpc-opb"); + + /* Create ISA IO and Mem space regions which are the root of + * the ISA bus (ie, ISA address spaces). We don't create a + * separate one for FW which we alias to memory. + */ + memory_region_init(&lpc->isa_io, OBJECT(dev), "isa-io", ISA_IO_SIZE); + memory_region_init(&lpc->isa_mem, OBJECT(dev), "isa-mem", ISA_MEM_SIZE); + + /* Create windows from the OPB space to the ISA space */ + memory_region_init_alias(&lpc->opb_isa_io, OBJECT(dev), "lpc-isa-io", + &lpc->isa_io, 0, LPC_IO_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_IO_OPB_ADDR, + &lpc->opb_isa_io); + memory_region_init_alias(&lpc->opb_isa_mem, OBJECT(dev), "lpc-isa-mem", + &lpc->isa_mem, 0, LPC_MEM_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_MEM_OPB_ADDR, + &lpc->opb_isa_mem); + memory_region_init_alias(&lpc->opb_isa_fw, OBJECT(dev), "lpc-isa-fw", + &lpc->isa_mem, 0, LPC_FW_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_FW_OPB_ADDR, + &lpc->opb_isa_fw); + + /* Create MMIO regions for LPC HC and OPB registers */ + memory_region_init_io(&lpc->opb_master_regs, OBJECT(dev), &opb_master_ops, + lpc, "lpc-opb-master", LPC_OPB_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_OPB_REGS_OPB_ADDR, + &lpc->opb_master_regs); + memory_region_init_io(&lpc->lpc_hc_regs, OBJECT(dev), &lpc_hc_ops, lpc, + "lpc-hc", LPC_HC_REGS_OPB_SIZE); + memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, + &lpc->lpc_hc_regs); + + /* XScom region for LPC registers */ + pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), + &pnv_lpc_xscom_ops, lpc, "xscom-lpc", + PNV_XSCOM_LPC_SIZE); +} + +static void pnv_lpc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xdc->populate = pnv_lpc_populate; + + dc->realize = pnv_lpc_realize; +} + +static const TypeInfo pnv_lpc_info = { + .name = TYPE_PNV_LPC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvLpcController), + .class_init = pnv_lpc_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_lpc_register_types(void) +{ + type_register_static(&pnv_lpc_info); +} + +type_init(pnv_lpc_register_types) diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c new file mode 100644 index 0000000000..5aaa264bd7 --- /dev/null +++ b/hw/ppc/pnv_xscom.c @@ -0,0 +1,275 @@ +/* + * QEMU PowerPC PowerNV XSCOM bus + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "qemu/log.h" +#include "sysemu/kvm.h" +#include "target-ppc/cpu.h" +#include "hw/sysbus.h" + +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv.h" + +#include <libfdt.h> + +static void xscom_complete(CPUState *cs, uint64_t hmer_bits) +{ + /* + * TODO: When the read/write comes from the monitor, NULL is + * passed for the cpu, and no CPU completion is generated. + */ + if (cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + /* + * TODO: Need a CPU helper to set HMER, also handle generation + * of HMIs + */ + cpu_synchronize_state(cs); + env->spr[SPR_HMER] |= hmer_bits; + } +} + +static uint32_t pnv_xscom_pcba(PnvChip *chip, uint64_t addr) +{ + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + addr &= (PNV_XSCOM_SIZE - 1); + if (pcc->chip_type == PNV_CHIP_POWER9) { + return addr >> 3; + } else { + return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); + } +} + +static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) +{ + switch (pcba) { + case 0xf000f: + return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id; + case 0x1010c00: /* PIBAM FIR */ + case 0x1010c03: /* PIBAM FIR MASK */ + case 0x2020007: /* ADU stuff */ + case 0x2020009: /* ADU stuff */ + case 0x202000f: /* ADU stuff */ + return 0; + case 0x2013f00: /* PBA stuff */ + case 0x2013f01: /* PBA stuff */ + case 0x2013f02: /* PBA stuff */ + case 0x2013f03: /* PBA stuff */ + case 0x2013f04: /* PBA stuff */ + case 0x2013f05: /* PBA stuff */ + case 0x2013f06: /* PBA stuff */ + case 0x2013f07: /* PBA stuff */ + return 0; + case 0x2013028: /* CAPP stuff */ + case 0x201302a: /* CAPP stuff */ + case 0x2013801: /* CAPP stuff */ + case 0x2013802: /* CAPP stuff */ + return 0; + default: + return -1; + } +} + +static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val) +{ + /* We ignore writes to these */ + switch (pcba) { + case 0xf000f: /* chip id is RO */ + case 0x1010c00: /* PIBAM FIR */ + case 0x1010c01: /* PIBAM FIR */ + case 0x1010c02: /* PIBAM FIR */ + case 0x1010c03: /* PIBAM FIR MASK */ + case 0x1010c04: /* PIBAM FIR MASK */ + case 0x1010c05: /* PIBAM FIR MASK */ + case 0x2020007: /* ADU stuff */ + case 0x2020009: /* ADU stuff */ + case 0x202000f: /* ADU stuff */ + return true; + default: + return false; + } +} + +static uint64_t xscom_read(void *opaque, hwaddr addr, unsigned width) +{ + PnvChip *chip = opaque; + uint32_t pcba = pnv_xscom_pcba(chip, addr); + uint64_t val = 0; + MemTxResult result; + + /* Handle some SCOMs here before dispatch */ + val = xscom_read_default(chip, pcba); + if (val != -1) { + goto complete; + } + + val = address_space_ldq(&chip->xscom_as, pcba << 3, MEMTXATTRS_UNSPECIFIED, + &result); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "XSCOM read failed at @0x%" + HWADDR_PRIx " pcba=0x%08x\n", addr, pcba); + xscom_complete(current_cpu, HMER_XSCOM_FAIL | HMER_XSCOM_DONE); + return 0; + } + +complete: + xscom_complete(current_cpu, HMER_XSCOM_DONE); + return val; +} + +static void xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned width) +{ + PnvChip *chip = opaque; + uint32_t pcba = pnv_xscom_pcba(chip, addr); + MemTxResult result; + + /* Handle some SCOMs here before dispatch */ + if (xscom_write_default(chip, pcba, val)) { + goto complete; + } + + address_space_stq(&chip->xscom_as, pcba << 3, val, MEMTXATTRS_UNSPECIFIED, + &result); + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "XSCOM write failed at @0x%" + HWADDR_PRIx " pcba=0x%08x data=0x%" PRIx64 "\n", + addr, pcba, val); + xscom_complete(current_cpu, HMER_XSCOM_FAIL | HMER_XSCOM_DONE); + return; + } + +complete: + xscom_complete(current_cpu, HMER_XSCOM_DONE); +} + +const MemoryRegionOps pnv_xscom_ops = { + .read = xscom_read, + .write = xscom_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, +}; + +void pnv_xscom_realize(PnvChip *chip, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(chip); + char *name; + + name = g_strdup_printf("xscom-%x", chip->chip_id); + memory_region_init_io(&chip->xscom_mmio, OBJECT(chip), &pnv_xscom_ops, + chip, name, PNV_XSCOM_SIZE); + sysbus_init_mmio(sbd, &chip->xscom_mmio); + + memory_region_init(&chip->xscom, OBJECT(chip), name, PNV_XSCOM_SIZE); + address_space_init(&chip->xscom_as, &chip->xscom, name); + g_free(name); +} + +static const TypeInfo pnv_xscom_interface_info = { + .name = TYPE_PNV_XSCOM_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(PnvXScomInterfaceClass), +}; + +static void pnv_xscom_register_types(void) +{ + type_register_static(&pnv_xscom_interface_info); +} + +type_init(pnv_xscom_register_types) + +typedef struct ForeachPopulateArgs { + void *fdt; + int xscom_offset; +} ForeachPopulateArgs; + +static int xscom_populate_child(Object *child, void *opaque) +{ + if (object_dynamic_cast(child, TYPE_PNV_XSCOM_INTERFACE)) { + ForeachPopulateArgs *args = opaque; + PnvXScomInterface *xd = PNV_XSCOM_INTERFACE(child); + PnvXScomInterfaceClass *xc = PNV_XSCOM_INTERFACE_GET_CLASS(xd); + + if (xc->populate) { + _FDT((xc->populate(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_xscom_populate(PnvChip *chip, void *fdt, int root_offset) +{ + uint64_t reg[] = { cpu_to_be64(PNV_XSCOM_BASE(chip)), + cpu_to_be64(PNV_XSCOM_SIZE) }; + int xscom_offset; + ForeachPopulateArgs args; + char *name; + PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); + + name = g_strdup_printf("xscom@%" PRIx64, be64_to_cpu(reg[0])); + xscom_offset = fdt_add_subnode(fdt, root_offset, name); + _FDT(xscom_offset); + g_free(name); + _FDT((fdt_setprop_cell(fdt, xscom_offset, "ibm,chip-id", chip->chip_id))); + _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 (pcc->chip_type == PNV_CHIP_POWER9) { + _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, "scom-controller", NULL, 0))); + + args.fdt = fdt; + args.xscom_offset = xscom_offset; + + object_child_foreach(OBJECT(chip), xscom_populate_child, &args); + return 0; +} + +void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, MemoryRegion *mr) +{ + memory_region_add_subregion(&chip->xscom, offset << 3, mr); +} + +void pnv_xscom_region_init(MemoryRegion *mr, + struct Object *owner, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size) +{ + memory_region_init_io(mr, owner, ops, opaque, name, size << 3); +} diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 486f57d6f6..c8e29212cb 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -271,205 +271,6 @@ static void add_str(GString *s, const gchar *s1) g_string_append_len(s, s1, strlen(s1) + 1); } -static void *spapr_create_fdt_skel(hwaddr initrd_base, - hwaddr initrd_size, - hwaddr kernel_size, - bool little_endian, - const char *kernel_cmdline, - uint32_t epow_irq) -{ - void *fdt; - uint32_t start_prop = cpu_to_be32(initrd_base); - uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size); - GString *hypertas = g_string_sized_new(256); - GString *qemu_hypertas = g_string_sized_new(256); - uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)}; - uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(max_cpus)}; - unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80}; - char *buf; - - add_str(hypertas, "hcall-pft"); - add_str(hypertas, "hcall-term"); - add_str(hypertas, "hcall-dabr"); - add_str(hypertas, "hcall-interrupt"); - add_str(hypertas, "hcall-tce"); - add_str(hypertas, "hcall-vio"); - add_str(hypertas, "hcall-splpar"); - add_str(hypertas, "hcall-bulk"); - add_str(hypertas, "hcall-set-mode"); - add_str(hypertas, "hcall-sprg0"); - add_str(hypertas, "hcall-copy"); - add_str(hypertas, "hcall-debug"); - add_str(qemu_hypertas, "hcall-memop1"); - - fdt = g_malloc0(FDT_MAX_SIZE); - _FDT((fdt_create(fdt, FDT_MAX_SIZE))); - - if (kernel_size) { - _FDT((fdt_add_reservemap_entry(fdt, KERNEL_LOAD_ADDR, kernel_size))); - } - if (initrd_size) { - _FDT((fdt_add_reservemap_entry(fdt, initrd_base, initrd_size))); - } - _FDT((fdt_finish_reservemap(fdt))); - - /* Root node */ - _FDT((fdt_begin_node(fdt, ""))); - _FDT((fdt_property_string(fdt, "device_type", "chrp"))); - _FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)"))); - _FDT((fdt_property_string(fdt, "compatible", "qemu,pseries"))); - - /* - * Add info to guest to indentify which host is it being run on - * and what is the uuid of the guest - */ - if (kvmppc_get_host_model(&buf)) { - _FDT((fdt_property_string(fdt, "host-model", buf))); - g_free(buf); - } - if (kvmppc_get_host_serial(&buf)) { - _FDT((fdt_property_string(fdt, "host-serial", buf))); - g_free(buf); - } - - buf = qemu_uuid_unparse_strdup(&qemu_uuid); - - _FDT((fdt_property_string(fdt, "vm,uuid", buf))); - if (qemu_uuid_set) { - _FDT((fdt_property_string(fdt, "system-id", buf))); - } - g_free(buf); - - if (qemu_get_vm_name()) { - _FDT((fdt_property_string(fdt, "ibm,partition-name", - qemu_get_vm_name()))); - } - - _FDT((fdt_property_cell(fdt, "#address-cells", 0x2))); - _FDT((fdt_property_cell(fdt, "#size-cells", 0x2))); - - /* /chosen */ - _FDT((fdt_begin_node(fdt, "chosen"))); - - /* Set Form1_affinity */ - _FDT((fdt_property(fdt, "ibm,architecture-vec-5", vec5, sizeof(vec5)))); - - _FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline))); - _FDT((fdt_property(fdt, "linux,initrd-start", - &start_prop, sizeof(start_prop)))); - _FDT((fdt_property(fdt, "linux,initrd-end", - &end_prop, sizeof(end_prop)))); - if (kernel_size) { - uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR), - cpu_to_be64(kernel_size) }; - - _FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop)))); - if (little_endian) { - _FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0))); - } - } - if (boot_menu) { - _FDT((fdt_property_cell(fdt, "qemu,boot-menu", boot_menu))); - } - _FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width))); - _FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height))); - _FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth))); - - _FDT((fdt_end_node(fdt))); - - /* RTAS */ - _FDT((fdt_begin_node(fdt, "rtas"))); - - if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { - add_str(hypertas, "hcall-multi-tce"); - } - _FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas->str, - hypertas->len))); - g_string_free(hypertas, TRUE); - _FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas->str, - qemu_hypertas->len))); - g_string_free(qemu_hypertas, TRUE); - - _FDT((fdt_property(fdt, "ibm,associativity-reference-points", - refpoints, sizeof(refpoints)))); - - _FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX))); - _FDT((fdt_property_cell(fdt, "rtas-event-scan-rate", - RTAS_EVENT_SCAN_RATE))); - - if (msi_nonbroken) { - _FDT((fdt_property(fdt, "ibm,change-msix-capable", NULL, 0))); - } - - /* - * According to PAPR, rtas ibm,os-term does not guarantee a return - * back to the guest cpu. - * - * While an additional ibm,extended-os-term property indicates that - * rtas call return will always occur. Set this property. - */ - _FDT((fdt_property(fdt, "ibm,extended-os-term", NULL, 0))); - - _FDT((fdt_end_node(fdt))); - - /* interrupt controller */ - _FDT((fdt_begin_node(fdt, "interrupt-controller"))); - - _FDT((fdt_property_string(fdt, "device_type", - "PowerPC-External-Interrupt-Presentation"))); - _FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp"))); - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - _FDT((fdt_property(fdt, "ibm,interrupt-server-ranges", - interrupt_server_ranges_prop, - sizeof(interrupt_server_ranges_prop)))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); - _FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP))); - _FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP))); - - _FDT((fdt_end_node(fdt))); - - /* vdevice */ - _FDT((fdt_begin_node(fdt, "vdevice"))); - - _FDT((fdt_property_string(fdt, "device_type", "vdevice"))); - _FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice"))); - _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); - _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2))); - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - - _FDT((fdt_end_node(fdt))); - - /* event-sources */ - spapr_events_fdt_skel(fdt, epow_irq); - - /* /hypervisor node */ - if (kvm_enabled()) { - uint8_t hypercall[16]; - - /* indicate KVM hypercall interface */ - _FDT((fdt_begin_node(fdt, "hypervisor"))); - _FDT((fdt_property_string(fdt, "compatible", "linux,kvm"))); - if (kvmppc_has_cap_fixup_hcalls()) { - /* - * Older KVM versions with older guest kernels were broken with the - * magic page, don't allow the guest to map it. - */ - if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, - sizeof(hypercall))) { - _FDT((fdt_property(fdt, "hcall-instructions", hypercall, - sizeof(hypercall)))); - } - } - _FDT((fdt_end_node(fdt))); - } - - _FDT((fdt_end_node(fdt))); /* close root node */ - _FDT((fdt_finish(fdt))); - - return fdt; -} - static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, hwaddr size) { @@ -854,13 +655,42 @@ out: return ret; } +static int spapr_dt_cas_updates(sPAPRMachineState *spapr, void *fdt, + sPAPROptionVector *ov5_updates) +{ + sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + int ret = 0, offset; + + /* Generate ibm,dynamic-reconfiguration-memory node if required */ + if (spapr_ovec_test(ov5_updates, OV5_DRCONF_MEMORY)) { + g_assert(smc->dr_lmb_enabled); + ret = spapr_populate_drconf_memory(spapr, fdt); + if (ret) { + goto out; + } + } + + offset = fdt_path_offset(fdt, "/chosen"); + if (offset < 0) { + offset = fdt_add_subnode(fdt, 0, "chosen"); + if (offset < 0) { + return offset; + } + } + ret = spapr_ovec_populate_dt(fdt, offset, spapr->ov5_cas, + "ibm,architecture-vec-5"); + +out: + return ret; +} + int spapr_h_cas_compose_response(sPAPRMachineState *spapr, target_ulong addr, target_ulong size, - bool cpu_update, bool memory_update) + bool cpu_update, + sPAPROptionVector *ov5_updates) { void *fdt, *fdt_skel; sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(qdev_get_machine()); size -= sizeof(hdr); @@ -879,9 +709,8 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, _FDT((spapr_fixup_cpu_dt(fdt, spapr))); } - /* Generate ibm,dynamic-reconfiguration-memory node if required */ - if (memory_update && smc->dr_lmb_enabled) { - _FDT((spapr_populate_drconf_memory(spapr, fdt))); + if (spapr_dt_cas_updates(spapr, fdt, ov5_updates)) { + return -1; } /* Pack resulting tree */ @@ -900,25 +729,206 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, return 0; } -static void spapr_finalize_fdt(sPAPRMachineState *spapr, - hwaddr fdt_addr, - hwaddr rtas_addr, - hwaddr rtas_size) +static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) +{ + int rtas; + GString *hypertas = g_string_sized_new(256); + GString *qemu_hypertas = g_string_sized_new(256); + uint32_t refpoints[] = { cpu_to_be32(0x4), cpu_to_be32(0x4) }; + uint64_t max_hotplug_addr = spapr->hotplug_memory.base + + memory_region_size(&spapr->hotplug_memory.mr); + uint32_t lrdr_capacity[] = { + cpu_to_be32(max_hotplug_addr >> 32), + cpu_to_be32(max_hotplug_addr & 0xffffffff), + 0, cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE), + cpu_to_be32(max_cpus / smp_threads), + }; + + _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); + + /* hypertas */ + add_str(hypertas, "hcall-pft"); + add_str(hypertas, "hcall-term"); + add_str(hypertas, "hcall-dabr"); + add_str(hypertas, "hcall-interrupt"); + add_str(hypertas, "hcall-tce"); + add_str(hypertas, "hcall-vio"); + add_str(hypertas, "hcall-splpar"); + add_str(hypertas, "hcall-bulk"); + add_str(hypertas, "hcall-set-mode"); + add_str(hypertas, "hcall-sprg0"); + add_str(hypertas, "hcall-copy"); + add_str(hypertas, "hcall-debug"); + add_str(qemu_hypertas, "hcall-memop1"); + + if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { + add_str(hypertas, "hcall-multi-tce"); + } + _FDT(fdt_setprop(fdt, rtas, "ibm,hypertas-functions", + hypertas->str, hypertas->len)); + g_string_free(hypertas, TRUE); + _FDT(fdt_setprop(fdt, rtas, "qemu,hypertas-functions", + qemu_hypertas->str, qemu_hypertas->len)); + g_string_free(qemu_hypertas, TRUE); + + _FDT(fdt_setprop(fdt, rtas, "ibm,associativity-reference-points", + refpoints, sizeof(refpoints))); + + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-error-log-max", + RTAS_ERROR_LOG_MAX)); + _FDT(fdt_setprop_cell(fdt, rtas, "rtas-event-scan-rate", + RTAS_EVENT_SCAN_RATE)); + + if (msi_nonbroken) { + _FDT(fdt_setprop(fdt, rtas, "ibm,change-msix-capable", NULL, 0)); + } + + /* + * According to PAPR, rtas ibm,os-term does not guarantee a return + * back to the guest cpu. + * + * While an additional ibm,extended-os-term property indicates + * that rtas call return will always occur. Set this property. + */ + _FDT(fdt_setprop(fdt, rtas, "ibm,extended-os-term", NULL, 0)); + + _FDT(fdt_setprop(fdt, rtas, "ibm,lrdr-capacity", + lrdr_capacity, sizeof(lrdr_capacity))); + + spapr_dt_rtas_tokens(fdt, rtas); +} + +static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) +{ + MachineState *machine = MACHINE(spapr); + int chosen; + const char *boot_device = machine->boot_order; + char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); + size_t cb = 0; + char *bootlist = get_boot_devices_list(&cb, true); + + _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); + + _FDT(fdt_setprop_string(fdt, chosen, "bootargs", machine->kernel_cmdline)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-start", + spapr->initrd_base)); + _FDT(fdt_setprop_cell(fdt, chosen, "linux,initrd-end", + spapr->initrd_base + spapr->initrd_size)); + + if (spapr->kernel_size) { + uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR), + cpu_to_be64(spapr->kernel_size) }; + + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel", + &kprop, sizeof(kprop))); + if (spapr->kernel_le) { + _FDT(fdt_setprop(fdt, chosen, "qemu,boot-kernel-le", NULL, 0)); + } + } + if (boot_menu) { + _FDT((fdt_setprop_cell(fdt, chosen, "qemu,boot-menu", boot_menu))); + } + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-width", graphic_width)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-height", graphic_height)); + _FDT(fdt_setprop_cell(fdt, chosen, "qemu,graphic-depth", graphic_depth)); + + if (cb && bootlist) { + int i; + + for (i = 0; i < cb; i++) { + if (bootlist[i] == '\n') { + bootlist[i] = ' '; + } + } + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-list", bootlist)); + } + + if (boot_device && strlen(boot_device)) { + _FDT(fdt_setprop_string(fdt, chosen, "qemu,boot-device", boot_device)); + } + + if (!spapr->has_graphics && stdout_path) { + _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); + } + + g_free(stdout_path); + g_free(bootlist); +} + +static void spapr_dt_hypervisor(sPAPRMachineState *spapr, void *fdt) +{ + /* The /hypervisor node isn't in PAPR - this is a hack to allow PR + * KVM to work under pHyp with some guest co-operation */ + int hypervisor; + uint8_t hypercall[16]; + + _FDT(hypervisor = fdt_add_subnode(fdt, 0, "hypervisor")); + /* indicate KVM hypercall interface */ + _FDT(fdt_setprop_string(fdt, hypervisor, "compatible", "linux,kvm")); + if (kvmppc_has_cap_fixup_hcalls()) { + /* + * Older KVM versions with older guest kernels were broken + * with the magic page, don't allow the guest to map it. + */ + if (!kvmppc_get_hypercall(first_cpu->env_ptr, hypercall, + sizeof(hypercall))) { + _FDT(fdt_setprop(fdt, hypervisor, "hcall-instructions", + hypercall, sizeof(hypercall))); + } + } +} + +static void *spapr_build_fdt(sPAPRMachineState *spapr, + hwaddr rtas_addr, + hwaddr rtas_size) { MachineState *machine = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); - const char *boot_device = machine->boot_order; - int ret, i; - size_t cb = 0; - char *bootlist; + int ret; void *fdt; sPAPRPHBState *phb; + char *buf; + + fdt = g_malloc0(FDT_MAX_SIZE); + _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); + + /* Root node */ + _FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp")); + _FDT(fdt_setprop_string(fdt, 0, "model", "IBM pSeries (emulated by qemu)")); + _FDT(fdt_setprop_string(fdt, 0, "compatible", "qemu,pseries")); + + /* + * Add info to guest to indentify which host is it being run on + * and what is the uuid of the guest + */ + if (kvmppc_get_host_model(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-model", buf)); + g_free(buf); + } + if (kvmppc_get_host_serial(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); + g_free(buf); + } + + buf = qemu_uuid_unparse_strdup(&qemu_uuid); + + _FDT(fdt_setprop_string(fdt, 0, "vm,uuid", buf)); + if (qemu_uuid_set) { + _FDT(fdt_setprop_string(fdt, 0, "system-id", buf)); + } + g_free(buf); + + if (qemu_get_vm_name()) { + _FDT(fdt_setprop_string(fdt, 0, "ibm,partition-name", + qemu_get_vm_name())); + } - fdt = g_malloc(FDT_MAX_SIZE); + _FDT(fdt_setprop_cell(fdt, 0, "#address-cells", 2)); + _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); - /* open out the base tree into a temp buffer for the final tweaks */ - _FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE))); + /* /interrupt controller */ + spapr_dt_xics(spapr->xics, fdt, PHANDLE_XICP); ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { @@ -926,11 +936,8 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, exit(1); } - ret = spapr_populate_vdevice(spapr->vio_bus, fdt); - if (ret < 0) { - error_report("couldn't setup vio devices in fdt"); - exit(1); - } + /* /vdevice */ + spapr_dt_vdevice(spapr->vio_bus, fdt); if (object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)) { ret = spapr_rng_populate_dt(fdt); @@ -948,43 +955,9 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, } } - /* RTAS */ - ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size); - if (ret < 0) { - error_report("Couldn't set up RTAS device tree properties"); - } - /* cpus */ spapr_populate_cpus_dt_node(fdt, spapr); - bootlist = get_boot_devices_list(&cb, true); - if (cb && bootlist) { - int offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - exit(1); - } - for (i = 0; i < cb; i++) { - if (bootlist[i] == '\n') { - bootlist[i] = ' '; - } - - } - ret = fdt_setprop_string(fdt, offset, "qemu,boot-list", bootlist); - } - - if (boot_device && strlen(boot_device)) { - int offset = fdt_path_offset(fdt, "/chosen"); - - if (offset < 0) { - exit(1); - } - fdt_setprop_string(fdt, offset, "qemu,boot-device", boot_device); - } - - if (!spapr->has_graphics) { - spapr_populate_chosen_stdout(fdt, spapr->vio_bus); - } - if (smc->dr_lmb_enabled) { _FDT(spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_LMB)); } @@ -999,19 +972,36 @@ static void spapr_finalize_fdt(sPAPRMachineState *spapr, } } - _FDT((fdt_pack(fdt))); + /* /event-sources */ + spapr_dt_events(spapr, fdt); - 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); + /* /rtas */ + spapr_dt_rtas(spapr, fdt); + + /* /chosen */ + spapr_dt_chosen(spapr, fdt); + + /* /hypervisor */ + if (kvm_enabled()) { + spapr_dt_hypervisor(spapr, fdt); } - qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); - cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); + /* Build memory reserve map */ + if (spapr->kernel_size) { + _FDT((fdt_add_mem_rsv(fdt, KERNEL_LOAD_ADDR, spapr->kernel_size))); + } + if (spapr->initrd_size) { + _FDT((fdt_add_mem_rsv(fdt, spapr->initrd_base, spapr->initrd_size))); + } - g_free(bootlist); - g_free(fdt); + /* ibm,client-architecture-support updates */ + ret = spapr_dt_cas_updates(spapr, fdt, spapr->ov5_cas); + if (ret < 0) { + error_report("couldn't setup CAS properties fdt"); + exit(1); + } + + return fdt; } static uint64_t translate_kernel_address(void *opaque, uint64_t addr) @@ -1147,6 +1137,9 @@ static void ppc_spapr_reset(void) sPAPRMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; + hwaddr rtas_addr, fdt_addr; + void *fdt; + int rc; /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); @@ -1170,24 +1163,44 @@ static void ppc_spapr_reset(void) * processed with 32-bit real mode code if necessary */ rtas_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR); - spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE; - spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE; + rtas_addr = rtas_limit - RTAS_MAX_SIZE; + fdt_addr = rtas_addr - FDT_MAX_SIZE; - /* Load the fdt */ - spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr, - spapr->rtas_size); + /* if this reset wasn't generated by CAS, we should reset our + * negotiated options and start from scratch */ + if (!spapr->cas_reboot) { + spapr_ovec_cleanup(spapr->ov5_cas); + spapr->ov5_cas = spapr_ovec_new(); + } + + fdt = spapr_build_fdt(spapr, rtas_addr, spapr->rtas_size); + + spapr_load_rtas(spapr, fdt, rtas_addr); - /* Copy RTAS over */ - cpu_physical_memory_write(spapr->rtas_addr, spapr->rtas_blob, - spapr->rtas_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)); + g_free(fdt); /* Set up the entry state */ first_ppc_cpu = POWERPC_CPU(first_cpu); - first_ppc_cpu->env.gpr[3] = spapr->fdt_addr; + first_ppc_cpu->env.gpr[3] = fdt_addr; first_ppc_cpu->env.gpr[5] = 0; first_cpu->halted = 0; first_ppc_cpu->env.nip = SPAPR_ENTRY_POINT; + spapr->cas_reboot = false; } static void spapr_create_nvram(sPAPRMachineState *spapr) @@ -1682,7 +1695,6 @@ static void ppc_spapr_init(MachineState *machine) MachineClass *mc = MACHINE_GET_CLASS(machine); sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; PCIHostState *phb; int i; @@ -1692,10 +1704,7 @@ static void ppc_spapr_init(MachineState *machine) void *rma = NULL; hwaddr rma_alloc_size; hwaddr node0_size = spapr_node0_size(); - uint32_t initrd_base = 0; - long kernel_size = 0, initrd_size = 0; long load_limit, fw_size; - bool kernel_le = false; char *filename; int smt = kvmppc_smt_threads(); int spapr_cores = smp_cpus / smp_threads; @@ -1769,10 +1778,22 @@ static void ppc_spapr_init(MachineState *machine) DIV_ROUND_UP(max_cpus * smt, smp_threads), XICS_IRQS_SPAPR, &error_fatal); + /* Set up containers for ibm,client-set-architecture negotiated options */ + spapr->ov5 = spapr_ovec_new(); + spapr->ov5_cas = spapr_ovec_new(); + if (smc->dr_lmb_enabled) { + spapr_ovec_set(spapr->ov5, OV5_DRCONF_MEMORY); spapr_validate_node_memory(machine, &error_fatal); } + spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); + + /* advertise support for dedicated HP event source to guests */ + if (spapr->use_hotplug_event_source) { + spapr_ovec_set(spapr->ov5, OV5_HP_EVT); + } + /* init CPUs */ if (machine->cpu_model == NULL) { machine->cpu_model = kvm_enabled() ? "host" : smc->tcg_default_cpu; @@ -1896,7 +1917,7 @@ static void ppc_spapr_init(MachineState *machine) } g_free(filename); - /* Set up EPOW events infrastructure */ + /* Set up RTAS event infrastructure */ spapr_events_init(spapr); /* Set up the RTC RTAS interfaces */ @@ -1968,19 +1989,19 @@ static void ppc_spapr_init(MachineState *machine) if (kernel_filename) { uint64_t lowaddr = 0; - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, - 0, 0); - if (kernel_size == ELF_LOAD_WRONG_ENDIAN) { - kernel_size = load_elf(kernel_filename, - translate_kernel_address, NULL, - NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, - 0, 0); - kernel_le = kernel_size > 0; - } - if (kernel_size < 0) { - error_report("error loading %s: %s", - kernel_filename, load_elf_strerror(kernel_size)); + spapr->kernel_size = load_elf(kernel_filename, translate_kernel_address, + NULL, NULL, &lowaddr, NULL, 1, + PPC_ELF_MACHINE, 0, 0); + if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) { + spapr->kernel_size = load_elf(kernel_filename, + translate_kernel_address, NULL, NULL, + &lowaddr, NULL, 0, PPC_ELF_MACHINE, + 0, 0); + spapr->kernel_le = spapr->kernel_size > 0; + } + if (spapr->kernel_size < 0) { + error_report("error loading %s: %s", kernel_filename, + load_elf_strerror(spapr->kernel_size)); exit(1); } @@ -1989,17 +2010,17 @@ static void ppc_spapr_init(MachineState *machine) /* Try to locate the initrd in the gap between the kernel * and the firmware. Add a bit of space just in case */ - initrd_base = (KERNEL_LOAD_ADDR + kernel_size + 0x1ffff) & ~0xffff; - initrd_size = load_image_targphys(initrd_filename, initrd_base, - load_limit - initrd_base); - if (initrd_size < 0) { + spapr->initrd_base = (KERNEL_LOAD_ADDR + spapr->kernel_size + + 0x1ffff) & ~0xffff; + spapr->initrd_size = load_image_targphys(initrd_filename, + spapr->initrd_base, + load_limit + - spapr->initrd_base); + if (spapr->initrd_size < 0) { error_report("could not load initial ram disk '%s'", initrd_filename); exit(1); } - } else { - initrd_base = 0; - initrd_size = 0; } } @@ -2025,13 +2046,6 @@ static void ppc_spapr_init(MachineState *machine) register_savevm_live(NULL, "spapr/htab", -1, 1, &savevm_htab_handlers, spapr); - /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, - kernel_size, kernel_le, - kernel_cmdline, - spapr->check_exception_irq); - assert(spapr->fdt_skel != NULL); - /* used by RTAS */ QTAILQ_INIT(&spapr->ccs_list); qemu_register_reset(spapr_ccs_reset_hook, spapr); @@ -2129,16 +2143,41 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) spapr->kvm_type = g_strdup(value); } +static bool spapr_get_modern_hotplug_events(Object *obj, Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + return spapr->use_hotplug_event_source; +} + +static void spapr_set_modern_hotplug_events(Object *obj, bool value, + Error **errp) +{ + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + spapr->use_hotplug_event_source = value; +} + static void spapr_machine_initfn(Object *obj) { sPAPRMachineState *spapr = SPAPR_MACHINE(obj); spapr->htab_fd = -1; + spapr->use_hotplug_event_source = true; object_property_add_str(obj, "kvm-type", spapr_get_kvm_type, spapr_set_kvm_type, NULL); object_property_set_description(obj, "kvm-type", "Specifies the KVM virtualization mode (HV, PR)", NULL); + object_property_add_bool(obj, "modern-hotplug-events", + spapr_get_modern_hotplug_events, + spapr_set_modern_hotplug_events, + NULL); + object_property_set_description(obj, "modern-hotplug-events", + "Use dedicated hotplug event mechanism in" + " place of standard EPOW events when possible" + " (required for memory hot-unplug support)", + NULL); } static void spapr_machine_finalizefn(Object *obj) @@ -2163,14 +2202,16 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, - uint32_t node, Error **errp) +static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, + uint32_t node, bool dedicated_hp_event_source, + Error **errp) { sPAPRDRConnector *drc; sPAPRDRConnectorClass *drck; uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; int i, fdt_offset, fdt_size; void *fdt; + uint64_t addr = addr_start; for (i = 0; i < nr_lmbs; i++) { drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, @@ -2189,7 +2230,17 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr, uint64_t size, * guest only in case of hotplugged memory */ if (dev->hotplugged) { - spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, nr_lmbs); + if (dedicated_hp_event_source) { + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr_start / SPAPR_MEMORY_BLOCK_SIZE); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + spapr_hotplug_req_add_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs, + drck->get_index(drc)); + } else { + spapr_hotplug_req_add_by_count(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs); + } } } @@ -2222,8 +2273,94 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out; } - spapr_add_lmbs(dev, addr, size, node, &error_abort); + spapr_add_lmbs(dev, addr, size, node, + spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), + &error_abort); + +out: + error_propagate(errp, local_err); +} + +typedef struct sPAPRDIMMState { + uint32_t nr_lmbs; +} sPAPRDIMMState; + +static void spapr_lmb_release(DeviceState *dev, void *opaque) +{ + sPAPRDIMMState *ds = (sPAPRDIMMState *)opaque; + HotplugHandler *hotplug_ctrl; + + if (--ds->nr_lmbs) { + return; + } + + g_free(ds); + + /* + * Now that all the LMBs have been removed by the guest, call the + * pc-dimm unplug handler to cleanup up the pc-dimm device. + */ + hotplug_ctrl = qdev_get_hotplug_handler(dev); + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); +} + +static void spapr_del_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, + Error **errp) +{ + sPAPRDRConnector *drc; + sPAPRDRConnectorClass *drck; + uint32_t nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; + int i; + sPAPRDIMMState *ds = g_malloc0(sizeof(sPAPRDIMMState)); + uint64_t addr = addr_start; + + ds->nr_lmbs = nr_lmbs; + for (i = 0; i < nr_lmbs; i++) { + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr / SPAPR_MEMORY_BLOCK_SIZE); + g_assert(drc); + + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + drck->detach(drc, dev, spapr_lmb_release, ds, errp); + addr += SPAPR_MEMORY_BLOCK_SIZE; + } + + drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_LMB, + addr_start / SPAPR_MEMORY_BLOCK_SIZE); + drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + spapr_hotplug_req_remove_by_count_indexed(SPAPR_DR_CONNECTOR_TYPE_LMB, + nr_lmbs, + drck->get_index(drc)); +} + +static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + + pc_dimm_memory_unplug(dev, &ms->hotplug_memory, mr); + object_unparent(OBJECT(dev)); +} + +static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + Error *local_err = NULL; + PCDIMMDevice *dimm = PC_DIMM(dev); + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *mr = ddc->get_memory_region(dimm); + uint64_t size = memory_region_size(mr); + uint64_t addr; + addr = object_property_get_int(OBJECT(dimm), PC_DIMM_ADDR_PROP, &local_err); + if (local_err) { + goto out; + } + + spapr_del_lmbs(dev, addr, size, &error_abort); out: error_propagate(errp, local_err); } @@ -2301,10 +2438,42 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { + sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { - error_setg(errp, "Memory hot unplug not supported by sPAPR"); + if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { + spapr_memory_unplug(hotplug_dev, dev, errp); + } else { + error_setg(errp, "Memory hot unplug not supported for this guest"); + } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + if (!mc->query_hotpluggable_cpus) { + error_setg(errp, "CPU hot unplug not supported on this machine"); + return; + } + spapr_core_unplug(hotplug_dev, dev, errp); + } +} + +static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + sPAPRMachineState *sms = SPAPR_MACHINE(qdev_get_machine()); + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { + spapr_memory_unplug_request(hotplug_dev, dev, errp); + } else { + /* NOTE: this means there is a window after guest reset, prior to + * CAS negotiation, where unplug requests will fail due to the + * capability not being detected yet. This is a bit different than + * the case with PCI unplug, where the events will be queued and + * eventually handled by the guest after boot + */ + error_setg(errp, "Memory hot unplug not supported for this guest"); + } } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { if (!mc->query_hotpluggable_cpus) { error_setg(errp, "CPU hot unplug not supported on this machine"); @@ -2450,6 +2619,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) hc->plug = spapr_machine_device_plug; hc->unplug = spapr_machine_device_unplug; mc->cpu_index_to_socket_id = spapr_cpu_index_to_socket_id; + hc->unplug_request = spapr_machine_device_unplug_request; smc->dr_lmb_enabled = true; smc->tcg_default_cpu = "POWER8"; @@ -2585,7 +2755,10 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, static void spapr_machine_2_7_instance_options(MachineState *machine) { + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + spapr_machine_2_8_instance_options(machine); + spapr->use_hotplug_event_source = false; } static void spapr_machine_2_7_class_options(MachineClass *mc) diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index bc922bc86f..e0c14f6b77 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -184,7 +184,7 @@ void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, /* * Setup CPU DT entries only for hotplugged CPUs. For boot time or - * coldplugged CPUs DT entries are setup in spapr_finalize_fdt(). + * coldplugged CPUs DT entries are setup in spapr_build_fdt(). */ if (dev->hotplugged) { fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 6e54fd4743..a0c44ee593 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -68,6 +68,23 @@ static uint32_t set_isolation_state(sPAPRDRConnector *drc, } } + /* + * Fail any requests to ISOLATE the LMB DRC if this LMB doesn't + * belong to a DIMM device that is marked for removal. + * + * Currently the guest userspace tool drmgr that drives the memory + * hotplug/unplug will just try to remove a set of 'removable' LMBs + * in response to a hot unplug request that is based on drc-count. + * If the LMB being removed doesn't belong to a DIMM device that is + * actually being unplugged, fail the isolation request here. + */ + if (drc->type == SPAPR_DR_CONNECTOR_TYPE_LMB) { + if ((state == SPAPR_DR_ISOLATION_STATE_ISOLATED) && + !drc->awaiting_release) { + return RTAS_OUT_HW_ERROR; + } + } + drc->isolation_state = state; if (drc->isolation_state == SPAPR_DR_ISOLATION_STATE_ISOLATED) { diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index 6d3534541c..f85a9c32a7 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -40,6 +40,7 @@ #include "hw/ppc/spapr_drc.h" #include "qemu/help_option.h" #include "qemu/bcd.h" +#include "hw/ppc/spapr_ovec.h" #include <libfdt.h> struct rtas_error_log { @@ -174,6 +175,16 @@ struct epow_log_full { struct rtas_event_log_v6_epow epow; } QEMU_PACKED; +union drc_identifier { + uint32_t index; + uint32_t count; + struct { + uint32_t count; + uint32_t index; + } count_indexed; + char name[1]; +} QEMU_PACKED; + struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_SECTION_ID_HOTPLUG 0x4850 /* HP */ struct rtas_event_log_v6_section_header hdr; @@ -190,12 +201,9 @@ struct rtas_event_log_v6_hp { #define RTAS_LOG_V6_HP_ID_DRC_NAME 1 #define RTAS_LOG_V6_HP_ID_DRC_INDEX 2 #define RTAS_LOG_V6_HP_ID_DRC_COUNT 3 +#define RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED 4 uint8_t reserved; - union { - uint32_t index; - uint32_t count; - char name[1]; - } drc; + union drc_identifier drc_id; } QEMU_PACKED; struct hp_log_full { @@ -206,28 +214,132 @@ struct hp_log_full { struct rtas_event_log_v6_hp hp; } QEMU_PACKED; -#define EVENT_MASK_INTERNAL_ERRORS 0x80000000 -#define EVENT_MASK_EPOW 0x40000000 -#define EVENT_MASK_HOTPLUG 0x10000000 -#define EVENT_MASK_IO 0x08000000 +typedef enum EventClass { + EVENT_CLASS_INTERNAL_ERRORS = 0, + EVENT_CLASS_EPOW = 1, + EVENT_CLASS_RESERVED = 2, + EVENT_CLASS_HOT_PLUG = 3, + EVENT_CLASS_IO = 4, + EVENT_CLASS_MAX +} EventClassIndex; +#define EVENT_CLASS_MASK(index) (1 << (31 - index)) + +static const char * const event_names[EVENT_CLASS_MAX] = { + [EVENT_CLASS_INTERNAL_ERRORS] = "internal-errors", + [EVENT_CLASS_EPOW] = "epow-events", + [EVENT_CLASS_HOT_PLUG] = "hot-plug-events", + [EVENT_CLASS_IO] = "ibm,io-events", +}; + +struct sPAPREventSource { + int irq; + uint32_t mask; + bool enabled; +}; + +static sPAPREventSource *spapr_event_sources_new(void) +{ + return g_new0(sPAPREventSource, EVENT_CLASS_MAX); +} + +static void spapr_event_sources_register(sPAPREventSource *event_sources, + EventClassIndex index, int irq) +{ + /* we only support 1 irq per event class at the moment */ + g_assert(event_sources); + g_assert(!event_sources[index].enabled); + event_sources[index].irq = irq; + event_sources[index].mask = EVENT_CLASS_MASK(index); + event_sources[index].enabled = true; +} + +static const sPAPREventSource * +spapr_event_sources_get_source(sPAPREventSource *event_sources, + EventClassIndex index) +{ + g_assert(index < EVENT_CLASS_MAX); + g_assert(event_sources); + + return &event_sources[index]; +} + +void spapr_dt_events(sPAPRMachineState *spapr, void *fdt) +{ + uint32_t irq_ranges[EVENT_CLASS_MAX * 2]; + int i, count = 0, event_sources; + sPAPREventSource *events = spapr->event_sources; + + g_assert(events); + + _FDT(event_sources = fdt_add_subnode(fdt, 0, "event-sources")); + + for (i = 0, count = 0; i < EVENT_CLASS_MAX; i++) { + int node_offset; + uint32_t interrupts[2]; + const sPAPREventSource *source = + spapr_event_sources_get_source(events, i); + const char *source_name = event_names[i]; + + if (!source->enabled) { + continue; + } + + interrupts[0] = cpu_to_be32(source->irq); + interrupts[1] = 0; -void spapr_events_fdt_skel(void *fdt, uint32_t check_exception_irq) + _FDT(node_offset = fdt_add_subnode(fdt, event_sources, source_name)); + _FDT(fdt_setprop(fdt, node_offset, "interrupts", interrupts, + sizeof(interrupts))); + + irq_ranges[count++] = interrupts[0]; + irq_ranges[count++] = cpu_to_be32(1); + } + + irq_ranges[count] = cpu_to_be32(count); + count++; + + _FDT((fdt_setprop(fdt, event_sources, "interrupt-controller", NULL, 0))); + _FDT((fdt_setprop_cell(fdt, event_sources, "#interrupt-cells", 2))); + _FDT((fdt_setprop(fdt, event_sources, "interrupt-ranges", + irq_ranges, count * sizeof(uint32_t)))); +} + +static const sPAPREventSource * +rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type) { - uint32_t irq_ranges[] = {cpu_to_be32(check_exception_irq), cpu_to_be32(1)}; - uint32_t interrupts[] = {cpu_to_be32(check_exception_irq), 0}; + const sPAPREventSource *source; + + g_assert(spapr->event_sources); + + switch (log_type) { + case RTAS_LOG_TYPE_HOTPLUG: + source = spapr_event_sources_get_source(spapr->event_sources, + EVENT_CLASS_HOT_PLUG); + if (spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)) { + g_assert(source->enabled); + break; + } + /* fall back to epow for legacy hotplug interrupt source */ + case RTAS_LOG_TYPE_EPOW: + source = spapr_event_sources_get_source(spapr->event_sources, + EVENT_CLASS_EPOW); + break; + default: + source = NULL; + } - _FDT((fdt_begin_node(fdt, "event-sources"))); + return source; +} - _FDT((fdt_property(fdt, "interrupt-controller", NULL, 0))); - _FDT((fdt_property_cell(fdt, "#interrupt-cells", 2))); - _FDT((fdt_property(fdt, "interrupt-ranges", - irq_ranges, sizeof(irq_ranges)))); +static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type) +{ + const sPAPREventSource *source; - _FDT((fdt_begin_node(fdt, "epow-events"))); - _FDT((fdt_property(fdt, "interrupts", interrupts, sizeof(interrupts)))); - _FDT((fdt_end_node(fdt))); + source = rtas_event_log_to_source(spapr, log_type); + g_assert(source); + g_assert(source->enabled); - _FDT((fdt_end_node(fdt))); + return source->irq; } static void rtas_event_log_queue(int log_type, void *data, bool exception) @@ -248,19 +360,15 @@ static sPAPREventLogEntry *rtas_event_log_dequeue(uint32_t event_mask, sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); sPAPREventLogEntry *entry = NULL; - /* we only queue EPOW events atm. */ - if ((event_mask & EVENT_MASK_EPOW) == 0) { - return NULL; - } - QTAILQ_FOREACH(entry, &spapr->pending_events, next) { + const sPAPREventSource *source = + rtas_event_log_to_source(spapr, entry->log_type); + if (entry->exception != exception) { continue; } - /* EPOW and hotplug events are surfaced in the same manner */ - if (entry->log_type == RTAS_LOG_TYPE_EPOW || - entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { + if (source->mask & event_mask) { break; } } @@ -277,19 +385,15 @@ static bool rtas_event_log_contains(uint32_t event_mask, bool exception) sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); sPAPREventLogEntry *entry = NULL; - /* we only queue EPOW events atm. */ - if ((event_mask & EVENT_MASK_EPOW) == 0) { - return false; - } - QTAILQ_FOREACH(entry, &spapr->pending_events, next) { + const sPAPREventSource *source = + rtas_event_log_to_source(spapr, entry->log_type); + if (entry->exception != exception) { continue; } - /* EPOW and hotplug events are surfaced in the same manner */ - if (entry->log_type == RTAS_LOG_TYPE_EPOW || - entry->log_type == RTAS_LOG_TYPE_HOTPLUG) { + if (source->mask & event_mask) { return true; } } @@ -377,7 +481,9 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) rtas_event_log_queue(RTAS_LOG_TYPE_EPOW, new_epow, true); - qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq)); + qemu_irq_pulse(xics_get_qirq(spapr->xics, + rtas_event_log_to_irq(spapr, + RTAS_LOG_TYPE_EPOW))); } static void spapr_hotplug_set_signalled(uint32_t drc_index) @@ -389,7 +495,7 @@ static void spapr_hotplug_set_signalled(uint32_t drc_index) static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, sPAPRDRConnectorType drc_type, - uint32_t drc) + union drc_identifier *drc_id) { sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); struct hp_log_full *new_hp; @@ -434,7 +540,7 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, case SPAPR_DR_CONNECTOR_TYPE_PCI: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PCI; if (hp->hotplug_action == RTAS_LOG_V6_HP_ACTION_ADD) { - spapr_hotplug_set_signalled(drc); + spapr_hotplug_set_signalled(drc_id->index); } break; case SPAPR_DR_CONNECTOR_TYPE_LMB: @@ -452,48 +558,89 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, } if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT) { - hp->drc.count = cpu_to_be32(drc); + hp->drc_id.count = cpu_to_be32(drc_id->count); } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_INDEX) { - hp->drc.index = cpu_to_be32(drc); + hp->drc_id.index = cpu_to_be32(drc_id->index); + } else if (hp_id == RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED) { + /* we should not be using count_indexed value unless the guest + * supports dedicated hotplug event source + */ + g_assert(spapr_ovec_test(spapr->ov5_cas, OV5_HP_EVT)); + hp->drc_id.count_indexed.count = + cpu_to_be32(drc_id->count_indexed.count); + hp->drc_id.count_indexed.index = + cpu_to_be32(drc_id->count_indexed.index); } rtas_event_log_queue(RTAS_LOG_TYPE_HOTPLUG, new_hp, true); - qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq)); + qemu_irq_pulse(xics_get_qirq(spapr->xics, + rtas_event_log_to_irq(spapr, + RTAS_LOG_TYPE_HOTPLUG))); } void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); sPAPRDRConnectorType drc_type = drck->get_type(drc); - uint32_t index = drck->get_index(drc); + union drc_identifier drc_id; + drc_id.index = drck->get_index(drc); spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, - RTAS_LOG_V6_HP_ACTION_ADD, drc_type, index); + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) { sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); sPAPRDRConnectorType drc_type = drck->get_type(drc); - uint32_t index = drck->get_index(drc); + union drc_identifier drc_id; + drc_id.index = drck->get_index(drc); spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_INDEX, - RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, index); + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, uint32_t count) { + union drc_identifier drc_id; + + drc_id.count = count; spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, - RTAS_LOG_V6_HP_ACTION_ADD, drc_type, count); + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, uint32_t count) { + union drc_identifier drc_id; + + drc_id.count = count; spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT, - RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, count); + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); +} + +void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index) +{ + union drc_identifier drc_id; + + drc_id.count_indexed.count = count; + drc_id.count_indexed.index = index; + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED, + RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); +} + +void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index) +{ + union drc_identifier drc_id; + + drc_id.count_indexed.count = count; + drc_id.count_indexed.index = index; + spapr_hotplug_req_event(RTAS_LOG_V6_HP_ID_DRC_COUNT_INDEXED, + RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, @@ -505,6 +652,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, uint64_t xinfo; sPAPREventLogEntry *event; struct rtas_error_log *hdr; + int i; if ((nargs < 6) || (nargs > 7) || nret != 1) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -541,8 +689,14 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, * do the latter here, since our code relies on edge-triggered * interrupts. */ - if (rtas_event_log_contains(mask, true)) { - qemu_irq_pulse(xics_get_qirq(spapr->xics, spapr->check_exception_irq)); + for (i = 0; i < EVENT_CLASS_MAX; i++) { + if (rtas_event_log_contains(EVENT_CLASS_MASK(i), true)) { + const sPAPREventSource *source = + spapr_event_sources_get_source(spapr->event_sources, i); + + g_assert(source->enabled); + qemu_irq_pulse(xics_get_qirq(spapr->xics, source->irq)); + } } return; @@ -594,8 +748,27 @@ out_no_events: void spapr_events_init(sPAPRMachineState *spapr) { QTAILQ_INIT(&spapr->pending_events); - spapr->check_exception_irq = xics_spapr_alloc(spapr->xics, 0, false, - &error_fatal); + + spapr->event_sources = spapr_event_sources_new(); + + spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_EPOW, + xics_spapr_alloc(spapr->xics, 0, false, + &error_fatal)); + + /* NOTE: if machine supports modern/dedicated hotplug event source, + * we add it to the device-tree unconditionally. This means we may + * have cases where the source is enabled in QEMU, but unused by the + * guest because it does not support modern hotplug events, so we + * take care to rely on checking for negotiation of OV5_HP_EVT option + * before attempting to use it to signal events, rather than simply + * checking that it's enabled. + */ + if (spapr->use_hotplug_event_source) { + spapr_event_sources_register(spapr->event_sources, EVENT_CLASS_HOT_PLUG, + xics_spapr_alloc(spapr->xics, 0, false, + &error_fatal)); + } + spapr->epow_notifier.notify = spapr_powerdown_req; qemu_register_powerdown_notifier(&spapr->epow_notifier); spapr_rtas_register(RTAS_CHECK_EXCEPTION, "check-exception", diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index c5e7e8c995..7c46d4625b 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -11,6 +11,7 @@ #include "trace.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" +#include "hw/ppc/spapr_ovec.h" struct SPRSyncState { int spr; @@ -880,32 +881,6 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -/* - * Return the offset to the requested option vector @vector in the - * option vector table @table. - */ -static target_ulong cas_get_option_vector(int vector, target_ulong table) -{ - int i; - char nr_vectors, nr_entries; - - if (!table) { - return 0; - } - - nr_vectors = (ldl_phys(&address_space_memory, table) >> 24) + 1; - if (!vector || vector > nr_vectors) { - return 0; - } - table++; /* skip nr option vectors */ - - for (i = 0; i < vector - 1; i++) { - nr_entries = ldl_phys(&address_space_memory, table) >> 24; - table += nr_entries + 2; - } - return table; -} - typedef struct { uint32_t cpu_version; Error *err; @@ -961,23 +936,21 @@ static void cas_handle_compat_cpu(PowerPCCPUClass *pcc, uint32_t pvr, } } -#define OV5_DRCONF_MEMORY 0x20 - static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong list = ppc64_phys_to_real(args[0]); - target_ulong ov_table, ov5; + target_ulong ov_table; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu_); CPUState *cs; - bool cpu_match = false, cpu_update = true, memory_update = false; + bool cpu_match = false, cpu_update = true; unsigned old_cpu_version = cpu_->cpu_version; unsigned compat_lvl = 0, cpu_version = 0; unsigned max_lvl = get_compat_level(cpu_->max_compat); int counter; - char ov5_byte2; + sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; /* Parse PVR list */ for (counter = 0; counter < 512; ++counter) { @@ -1033,19 +1006,34 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu_, /* For the future use: here @ov_table points to the first option vector */ ov_table = list; - ov5 = cas_get_option_vector(5, ov_table); - if (!ov5) { - return H_SUCCESS; - } + ov5_guest = spapr_ovec_parse_vector(ov_table, 5); + + /* NOTE: there are actually a number of ov5 bits where input from the + * guest is always zero, and the platform/QEMU enables them independently + * of guest input. To model these properly we'd want some sort of mask, + * but since they only currently apply to memory migration as defined + * by LoPAPR 1.1, 14.5.4.8, which QEMU doesn't implement, we don't need + * to worry about this for now. + */ + ov5_cas_old = spapr_ovec_clone(spapr->ov5_cas); + /* full range of negotiated ov5 capabilities */ + spapr_ovec_intersect(spapr->ov5_cas, spapr->ov5, ov5_guest); + spapr_ovec_cleanup(ov5_guest); + /* 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); - /* @list now points to OV 5 */ - ov5_byte2 = ldub_phys(&address_space_memory, ov5 + 2); - if (ov5_byte2 & OV5_DRCONF_MEMORY) { - memory_update = true; + if (!spapr->cas_reboot) { + spapr->cas_reboot = + (spapr_h_cas_compose_response(spapr, args[1], args[2], cpu_update, + ov5_updates) != 0); } + spapr_ovec_cleanup(ov5_updates); - if (spapr_h_cas_compose_response(spapr, args[1], args[2], - cpu_update, memory_update)) { + if (spapr->cas_reboot) { qemu_system_reset_request(); } diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c new file mode 100644 index 0000000000..c2a0d18577 --- /dev/null +++ b/hw/ppc/spapr_ovec.c @@ -0,0 +1,242 @@ +/* + * QEMU SPAPR Architecture Option Vector Helper Functions + * + * Copyright IBM Corp. 2016 + * + * Authors: + * Bharata B Rao <bharata@linux.vnet.ibm.com> + * Michael Roth <mdroth@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "hw/ppc/spapr_ovec.h" +#include "qemu/bitmap.h" +#include "exec/address-spaces.h" +#include "qemu/error-report.h" +#include <libfdt.h> + +/* #define DEBUG_SPAPR_OVEC */ + +#ifdef DEBUG_SPAPR_OVEC +#define DPRINTFN(fmt, ...) \ + do { fprintf(stderr, fmt "\n", ## __VA_ARGS__); } while (0) +#else +#define DPRINTFN(fmt, ...) \ + do { } while (0) +#endif + +#define OV_MAXBYTES 256 /* not including length byte */ +#define OV_MAXBITS (OV_MAXBYTES * BITS_PER_BYTE) + +/* we *could* work with bitmaps directly, but handling the bitmap privately + * allows us to more safely make assumptions about the bitmap size and + * simplify the calling code somewhat + */ +struct sPAPROptionVector { + unsigned long *bitmap; +}; + +sPAPROptionVector *spapr_ovec_new(void) +{ + sPAPROptionVector *ov; + + ov = g_new0(sPAPROptionVector, 1); + ov->bitmap = bitmap_new(OV_MAXBITS); + + return ov; +} + +sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig) +{ + sPAPROptionVector *ov; + + g_assert(ov_orig); + + ov = spapr_ovec_new(); + bitmap_copy(ov->bitmap, ov_orig->bitmap, OV_MAXBITS); + + return ov; +} + +void spapr_ovec_intersect(sPAPROptionVector *ov, + sPAPROptionVector *ov1, + sPAPROptionVector *ov2) +{ + g_assert(ov); + g_assert(ov1); + g_assert(ov2); + + 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) +{ + unsigned long *change_mask = bitmap_new(OV_MAXBITS); + unsigned long *removed_bits = bitmap_new(OV_MAXBITS); + bool bits_were_removed = false; + + 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); + + if (!bitmap_empty(removed_bits, OV_MAXBITS)) { + bits_were_removed = true; + } + + g_free(change_mask); + g_free(removed_bits); + + return bits_were_removed; +} + +void spapr_ovec_cleanup(sPAPROptionVector *ov) +{ + if (ov) { + g_free(ov->bitmap); + g_free(ov); + } +} + +void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) +{ + g_assert(ov); + g_assert_cmpint(bitnr, <, OV_MAXBITS); + + set_bit(bitnr, ov->bitmap); +} + +void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) +{ + g_assert(ov); + g_assert_cmpint(bitnr, <, OV_MAXBITS); + + clear_bit(bitnr, ov->bitmap); +} + +bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr) +{ + g_assert(ov); + g_assert_cmpint(bitnr, <, OV_MAXBITS); + + return test_bit(bitnr, ov->bitmap) ? true : false; +} + +static void guest_byte_to_bitmap(uint8_t entry, unsigned long *bitmap, + long bitmap_offset) +{ + int i; + + for (i = 0; i < BITS_PER_BYTE; i++) { + if (entry & (1 << (BITS_PER_BYTE - 1 - i))) { + bitmap_set(bitmap, bitmap_offset + i, 1); + } + } +} + +static uint8_t guest_byte_from_bitmap(unsigned long *bitmap, long bitmap_offset) +{ + uint8_t entry = 0; + int i; + + for (i = 0; i < BITS_PER_BYTE; i++) { + if (test_bit(bitmap_offset + i, bitmap)) { + entry |= (1 << (BITS_PER_BYTE - 1 - i)); + } + } + + return entry; +} + +static target_ulong vector_addr(target_ulong table_addr, int vector) +{ + uint16_t vector_count, vector_len; + int i; + + vector_count = ldub_phys(&address_space_memory, table_addr) + 1; + if (vector > vector_count) { + return 0; + } + table_addr++; /* skip nr option vectors */ + + for (i = 0; i < vector - 1; i++) { + vector_len = ldub_phys(&address_space_memory, table_addr) + 1; + table_addr += vector_len + 1; /* bit-vector + length byte */ + } + return table_addr; +} + +sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) +{ + sPAPROptionVector *ov; + target_ulong addr; + uint16_t vector_len; + int i; + + g_assert(table_addr); + g_assert_cmpint(vector, >=, 1); /* vector numbering starts at 1 */ + + addr = vector_addr(table_addr, vector); + if (!addr) { + /* specified vector isn't present */ + return NULL; + } + + vector_len = ldub_phys(&address_space_memory, addr++) + 1; + g_assert_cmpint(vector_len, <=, OV_MAXBYTES); + ov = spapr_ovec_new(); + + for (i = 0; i < vector_len; i++) { + uint8_t entry = ldub_phys(&address_space_memory, addr + i); + if (entry) { + DPRINTFN("read guest vector %2d, byte %3d / %3d: 0x%.2x", + vector, i + 1, vector_len, entry); + guest_byte_to_bitmap(entry, ov->bitmap, i * BITS_PER_BYTE); + } + } + + return ov; +} + +int spapr_ovec_populate_dt(void *fdt, int fdt_offset, + sPAPROptionVector *ov, const char *name) +{ + uint8_t vec[OV_MAXBYTES + 1]; + uint16_t vec_len; + unsigned long lastbit; + int i; + + g_assert(ov); + + lastbit = find_last_bit(ov->bitmap, OV_MAXBITS); + /* if no bits are set, include at least 1 byte of the vector so we can + * still encoded this in the device tree while abiding by the same + * encoding/sizing expected in ibm,client-architecture-support + */ + vec_len = (lastbit == OV_MAXBITS) ? 1 : lastbit / BITS_PER_BYTE + 1; + g_assert_cmpint(vec_len, <=, OV_MAXBYTES); + /* guest expects vector len encoded as vec_len - 1, since the length byte + * is assumed and not included, and the first byte of the vector + * is assumed as well + */ + vec[0] = vec_len - 1; + + for (i = 1; i < vec_len + 1; i++) { + vec[i] = guest_byte_from_bitmap(ov->bitmap, (i - 1) * BITS_PER_BYTE); + if (vec[i]) { + DPRINTFN("encoding guest vector byte %3d / %3d: 0x%.2x", + i, vec_len, vec[i]); + } + } + + return fdt_setprop(fdt, fdt_offset, name, vec, vec_len); +} diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 2a1ccf59ea..7cde30ee09 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -1392,6 +1392,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) return; } + if (sphb->numa_node != -1 && + (sphb->numa_node >= MAX_NODES || !numa_info[sphb->numa_node].present)) { + error_setg(errp, "Invalid NUMA node ID for PCI host bridge"); + return; + } + sphb->dtbusname = g_strdup_printf("pci@%" PRIx64, sphb->buid); namebuf = alloca(strlen(sphb->dtbusname) + 32); @@ -1880,7 +1886,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, } /* Advertise NUMA via ibm,associativity */ - if (nb_numa_nodes > 1) { + if (phb->numa_node != -1) { _FDT(fdt_setprop(fdt, bus_off, "ibm,associativity", associativity, sizeof(associativity))); } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 0db84c816d..bb19944686 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -46,6 +46,7 @@ #include "hw/ppc/spapr_drc.h" #include "qemu/cutils.h" #include "trace.h" +#include "hw/ppc/fdt.h" static sPAPRConfigureConnectorState *spapr_ccs_find(sPAPRMachineState *spapr, uint32_t drc_index) @@ -710,78 +711,60 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn) rtas_table[token].fn = fn; } -int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, - hwaddr rtas_size) +void spapr_dt_rtas_tokens(void *fdt, int rtas) { - int ret; int i; - uint32_t lrdr_capacity[5]; - MachineState *machine = MACHINE(qdev_get_machine()); - sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - uint64_t max_hotplug_addr = spapr->hotplug_memory.base + - memory_region_size(&spapr->hotplug_memory.mr); - ret = fdt_add_mem_rsv(fdt, rtas_addr, rtas_size); + for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { + struct rtas_call *call = &rtas_table[i]; + + if (!call->name) { + continue; + } + + _FDT(fdt_setprop_cell(fdt, rtas, call->name, i + RTAS_TOKEN_BASE)); + } +} + +void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr) +{ + int rtas_node; + int ret; + + /* Copy RTAS blob into guest RAM */ + cpu_physical_memory_write(addr, spapr->rtas_blob, spapr->rtas_size); + + ret = fdt_add_mem_rsv(fdt, addr, spapr->rtas_size); if (ret < 0) { error_report("Couldn't add RTAS reserve entry: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-base", - rtas_addr); + /* Update the device tree with the blob's location */ + rtas_node = fdt_path_offset(fdt, "/rtas"); + assert(rtas_node >= 0); + + ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-base", addr); if (ret < 0) { error_report("Couldn't add linux,rtas-base property: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "linux,rtas-entry", - rtas_addr); + ret = fdt_setprop_cell(fdt, rtas_node, "linux,rtas-entry", addr); if (ret < 0) { error_report("Couldn't add linux,rtas-entry property: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - ret = qemu_fdt_setprop_cell(fdt, "/rtas", "rtas-size", - rtas_size); + ret = fdt_setprop_cell(fdt, rtas_node, "rtas-size", spapr->rtas_size); if (ret < 0) { error_report("Couldn't add rtas-size property: %s", - fdt_strerror(ret)); - return ret; + fdt_strerror(ret)); + exit(1); } - - for (i = 0; i < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; i++) { - struct rtas_call *call = &rtas_table[i]; - - if (!call->name) { - continue; - } - - ret = qemu_fdt_setprop_cell(fdt, "/rtas", call->name, - i + RTAS_TOKEN_BASE); - if (ret < 0) { - error_report("Couldn't add rtas token for %s: %s", - call->name, fdt_strerror(ret)); - return ret; - } - - } - - lrdr_capacity[0] = cpu_to_be32(max_hotplug_addr >> 32); - lrdr_capacity[1] = cpu_to_be32(max_hotplug_addr & 0xffffffff); - lrdr_capacity[2] = 0; - lrdr_capacity[3] = cpu_to_be32(SPAPR_MEMORY_BLOCK_SIZE); - lrdr_capacity[4] = cpu_to_be32(max_cpus/smp_threads); - ret = qemu_fdt_setprop(fdt, "/rtas", "ibm,lrdr-capacity", lrdr_capacity, - sizeof(lrdr_capacity)); - if (ret < 0) { - error_report("Couldn't add ibm,lrdr-capacity rtas property"); - return ret; - } - - return 0; } static void core_rtas_register_types(void) diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 3648aa5960..cc1e09c568 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -36,6 +36,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/ppc/xics.h" +#include "hw/ppc/fdt.h" #include "trace.h" #include <libfdt.h> @@ -624,11 +625,21 @@ static int compare_reg(const void *p1, const void *p2) return 1; } -int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) +void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt) { DeviceState *qdev, **qdevs; BusChild *kid; int i, num, ret = 0; + int node; + + _FDT(node = fdt_add_subnode(fdt, 0, "vdevice")); + + _FDT(fdt_setprop_string(fdt, node, "device_type", "vdevice")); + _FDT(fdt_setprop_string(fdt, node, "compatible", "IBM,vdevice")); + _FDT(fdt_setprop_cell(fdt, node, "#address-cells", 1)); + _FDT(fdt_setprop_cell(fdt, node, "#size-cells", 0)); + _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); + _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); /* Count qdevs on the bus list */ num = 0; @@ -650,43 +661,32 @@ int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt) * to know that will mean they are in forward order in the tree. */ for (i = num - 1; i >= 0; i--) { VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]); + VIOsPAPRDeviceClass *vdc = VIO_SPAPR_DEVICE_GET_CLASS(dev); ret = vio_make_devnode(dev, fdt); - if (ret < 0) { - goto out; + error_report("Couldn't create device node /vdevice/%s@%"PRIx32, + vdc->dt_name, dev->reg); + exit(1); } } - ret = 0; -out: g_free(qdevs); - - return ret; } -int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus) +gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus) { VIOsPAPRDevice *dev; char *name, *path; - int ret, offset; dev = spapr_vty_get_default(bus); - if (!dev) - return 0; - - offset = fdt_path_offset(fdt, "/chosen"); - if (offset < 0) { - return offset; + if (!dev) { + return NULL; } name = spapr_vio_get_dev_name(DEVICE(dev)); path = g_strdup_printf("/vdevice/%s", name); - ret = fdt_setprop_string(fdt, offset, "linux,stdout-path", path); - g_free(name); - g_free(path); - - return ret; + return path; } diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index b3915e4fd6..6224288ac3 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -35,10 +35,11 @@ #include "sysemu/sysemu.h" #include "net/net.h" #include "hw/boards.h" -#include "hw/nvram/openbios_firmware_abi.h" #include "hw/scsi/esp.h" #include "hw/i386/pc.h" #include "hw/isa/isa.h" +#include "hw/nvram/sun_nvram.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/nvram/fw_cfg.h" #include "hw/char/escc.h" #include "hw/empty_slot.h" @@ -117,39 +118,17 @@ static void nvram_init(Nvram *nvram, uint8_t *macaddr, int nvram_machine_id, const char *arch) { unsigned int i; - uint32_t start, end; + int sysp_end; uint8_t image[0x1ff0]; - struct OpenBIOS_nvpart_v1 *part_header; NvramClass *k = NVRAM_GET_CLASS(nvram); memset(image, '\0', sizeof(image)); - start = 0; + /* OpenBIOS nvram variables partition */ + sysp_end = chrp_nvram_create_system_partition(image, 0); - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(image, end, prom_envs[i]); - - // End marker - image[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = 0x1fd0; - OpenBIOS_finish_partition(part_header, end - start); + /* Free space partition */ + chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end); Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, nvram_machine_id); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 7b8134ef51..271d8bc592 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -36,7 +36,8 @@ #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "hw/boards.h" -#include "hw/nvram/openbios_firmware_abi.h" +#include "hw/nvram/sun_nvram.h" +#include "hw/nvram/chrp_nvram.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" #include "hw/ide.h" @@ -124,39 +125,17 @@ static int sun4u_NVRAM_set_params(Nvram *nvram, uint16_t NVRAM_size, const uint8_t *macaddr) { unsigned int i; - uint32_t start, end; + int sysp_end; uint8_t image[0x1ff0]; - struct OpenBIOS_nvpart_v1 *part_header; NvramClass *k = NVRAM_GET_CLASS(nvram); memset(image, '\0', sizeof(image)); - start = 0; + /* OpenBIOS nvram variables partition */ + sysp_end = chrp_nvram_create_system_partition(image, 0); - // OpenBIOS nvram variables - // Variable partition - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_SYSTEM; - pstrcpy(part_header->name, sizeof(part_header->name), "system"); - - end = start + sizeof(struct OpenBIOS_nvpart_v1); - for (i = 0; i < nb_prom_envs; i++) - end = OpenBIOS_set_var(image, end, prom_envs[i]); - - // End marker - image[end++] = '\0'; - - end = start + ((end - start + 15) & ~15); - OpenBIOS_finish_partition(part_header, end - start); - - // free partition - start = end; - part_header = (struct OpenBIOS_nvpart_v1 *)&image[start]; - part_header->signature = OPENBIOS_PART_FREE; - pstrcpy(part_header->name, sizeof(part_header->name), "free"); - - end = 0x1fd0; - OpenBIOS_finish_partition(part_header, end - start); + /* Free space partition */ + chrp_nvram_create_free_partition(&image[sysp_end], 0x1fd0 - sysp_end); Sun_init_header((struct Sun_nvram *)&image[0x1fd8], macaddr, 0x80); diff --git a/include/hw/nvram/chrp_nvram.h b/include/hw/nvram/chrp_nvram.h new file mode 100644 index 0000000000..b4f5b2b104 --- /dev/null +++ b/include/hw/nvram/chrp_nvram.h @@ -0,0 +1,54 @@ +/* + * Common Hardware Reference Platform NVRAM functions. + * + * This code is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2 of the License, + * or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef CHRP_NVRAM_H +#define CHRP_NVRAM_H + +/* OpenBIOS NVRAM partition */ +typedef struct { + uint8_t signature; + uint8_t checksum; + uint16_t len; /* Big endian, length divided by 16 */ + char name[12]; +} ChrpNvramPartHdr; + +#define CHRP_NVPART_SYSTEM 0x70 +#define CHRP_NVPART_FREE 0x7f + +static inline void +chrp_nvram_finish_partition(ChrpNvramPartHdr *header, uint32_t size) +{ + unsigned int i, sum; + uint8_t *tmpptr; + + /* Length divided by 16 */ + header->len = cpu_to_be16(size >> 4); + + /* Checksum */ + tmpptr = (uint8_t *)header; + sum = *tmpptr; + for (i = 0; i < 14; i++) { + sum += tmpptr[2 + i]; + sum = (sum + ((sum & 0xff00) >> 8)) & 0xff; + } + header->checksum = sum & 0xff; +} + +int chrp_nvram_create_system_partition(uint8_t *data, int min_len); +int chrp_nvram_create_free_partition(uint8_t *data, int len); + +#endif diff --git a/include/hw/nvram/openbios_firmware_abi.h b/include/hw/nvram/sun_nvram.h index 74cfd56180..68eaa60308 100644 --- a/include/hw/nvram/openbios_firmware_abi.h +++ b/include/hw/nvram/sun_nvram.h @@ -1,46 +1,5 @@ -#ifndef OPENBIOS_FIRMWARE_ABI_H -#define OPENBIOS_FIRMWARE_ABI_H - -/* OpenBIOS NVRAM partition */ -struct OpenBIOS_nvpart_v1 { - uint8_t signature; - uint8_t checksum; - uint16_t len; // BE, length divided by 16 - char name[12]; -}; - -#define OPENBIOS_PART_SYSTEM 0x70 -#define OPENBIOS_PART_FREE 0x7f - -static inline void -OpenBIOS_finish_partition(struct OpenBIOS_nvpart_v1 *header, uint32_t size) -{ - unsigned int i, sum; - uint8_t *tmpptr; - - // Length divided by 16 - header->len = cpu_to_be16(size >> 4); - - // Checksum - tmpptr = (uint8_t *)header; - sum = *tmpptr; - for (i = 0; i < 14; i++) { - sum += tmpptr[2 + i]; - sum = (sum + ((sum & 0xff00) >> 8)) & 0xff; - } - header->checksum = sum & 0xff; -} - -static inline uint32_t -OpenBIOS_set_var(uint8_t *nvram, uint32_t addr, const char *str) -{ - uint32_t len; - - len = strlen(str) + 1; - memcpy(&nvram[addr], str, len); - - return addr + len; -} +#ifndef SUN_NVRAM_H +#define SUN_NVRAM_H /* Sun IDPROM structure at the end of NVRAM */ /* from http://www.squirrel.com/squirrel/sun-nvram-hostid.faq.html */ @@ -72,4 +31,4 @@ Sun_init_header(struct Sun_nvram *header, const uint8_t *macaddr, int machine_id header->checksum = tmp; } -#endif /* OPENBIOS_FIRMWARE_ABI_H */ +#endif /* SUN_NVRAM_H */ diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h new file mode 100644 index 0000000000..02ac1c5f42 --- /dev/null +++ b/include/hw/ppc/pnv.h @@ -0,0 +1,129 @@ +/* + * QEMU PowerPC PowerNV various definitions + * + * Copyright (c) 2014-2016 BenH, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_H +#define _PPC_PNV_H + +#include "hw/boards.h" +#include "hw/sysbus.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_lpc.h" + +#define TYPE_PNV_CHIP "powernv-chip" +#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) +#define PNV_CHIP_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvChipClass, (klass), TYPE_PNV_CHIP) +#define PNV_CHIP_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvChipClass, (obj), TYPE_PNV_CHIP) + +typedef enum PnvChipType { + PNV_CHIP_POWER8E, /* AKA Murano (default) */ + PNV_CHIP_POWER8, /* AKA Venice */ + PNV_CHIP_POWER8NVL, /* AKA Naples */ + PNV_CHIP_POWER9, /* AKA Nimbus */ +} PnvChipType; + +typedef struct PnvChip { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + uint32_t chip_id; + uint64_t ram_start; + uint64_t ram_size; + + uint32_t nr_cores; + uint64_t cores_mask; + void *cores; + + hwaddr xscom_base; + MemoryRegion xscom_mmio; + MemoryRegion xscom; + AddressSpace xscom_as; + + PnvLpcController lpc; +} PnvChip; + +typedef struct PnvChipClass { + /*< private >*/ + SysBusDeviceClass parent_class; + + /*< public >*/ + const char *cpu_model; + PnvChipType chip_type; + uint64_t chip_cfam_id; + uint64_t cores_mask; + + hwaddr xscom_base; + + uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); +} PnvChipClass; + +#define TYPE_PNV_CHIP_POWER8E TYPE_PNV_CHIP "-POWER8E" +#define PNV_CHIP_POWER8E(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8E) + +#define TYPE_PNV_CHIP_POWER8 TYPE_PNV_CHIP "-POWER8" +#define PNV_CHIP_POWER8(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8) + +#define TYPE_PNV_CHIP_POWER8NVL TYPE_PNV_CHIP "-POWER8NVL" +#define PNV_CHIP_POWER8NVL(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER8NVL) + +#define TYPE_PNV_CHIP_POWER9 TYPE_PNV_CHIP "-POWER9" +#define PNV_CHIP_POWER9(obj) \ + OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) + +/* + * This generates a HW chip id depending on an index: + * + * 0x0, 0x1, 0x10, 0x11 + * + * 4 chips should be the maximum + */ +#define PNV_CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1)) + +#define TYPE_POWERNV_MACHINE MACHINE_TYPE_NAME("powernv") +#define POWERNV_MACHINE(obj) \ + OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) + +typedef struct PnvMachineState { + /*< private >*/ + MachineState parent_obj; + + uint32_t initrd_base; + long initrd_size; + + uint32_t num_chips; + PnvChip **chips; + + ISABus *isa_bus; +} PnvMachineState; + +#define PNV_FDT_ADDR 0x01000000 +#define PNV_TIMEBASE_FREQ 512000000ULL + +/* + * POWER8 MMIO base addresses + */ +#define PNV_XSCOM_SIZE 0x800000000ull +#define PNV_XSCOM_BASE(chip) \ + (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) + +#endif /* _PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h new file mode 100644 index 0000000000..2955a41c90 --- /dev/null +++ b/include/hw/ppc/pnv_core.h @@ -0,0 +1,50 @@ +/* + * QEMU PowerPC PowerNV CPU Core model + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_CORE_H +#define _PPC_PNV_CORE_H + +#include "hw/cpu/core.h" + +#define TYPE_PNV_CORE "powernv-cpu-core" +#define PNV_CORE(obj) \ + OBJECT_CHECK(PnvCore, (obj), TYPE_PNV_CORE) +#define PNV_CORE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvCoreClass, (klass), TYPE_PNV_CORE) +#define PNV_CORE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvCoreClass, (obj), TYPE_PNV_CORE) + +typedef struct PnvCore { + /*< private >*/ + CPUCore parent_obj; + + /*< public >*/ + void *threads; + uint32_t pir; + + MemoryRegion xscom_regs; +} PnvCore; + +typedef struct PnvCoreClass { + DeviceClass parent_class; + ObjectClass *cpu_oc; +} PnvCoreClass; + +extern char *pnv_core_typename(const char *model); + +#endif /* _PPC_PNV_CORE_H */ diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h new file mode 100644 index 0000000000..38e5506975 --- /dev/null +++ b/include/hw/ppc/pnv_lpc.h @@ -0,0 +1,67 @@ +/* + * QEMU PowerPC PowerNV LPC controller + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_LPC_H +#define _PPC_PNV_LPC_H + +#define TYPE_PNV_LPC "pnv-lpc" +#define PNV_LPC(obj) \ + OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) + +typedef struct PnvLpcController { + DeviceState parent; + + uint64_t eccb_stat_reg; + uint32_t eccb_data_reg; + + /* OPB bus */ + MemoryRegion opb_mr; + AddressSpace opb_as; + + /* ISA IO and Memory space */ + MemoryRegion isa_io; + MemoryRegion isa_mem; + + /* Windows from OPB to ISA (aliases) */ + MemoryRegion opb_isa_io; + MemoryRegion opb_isa_mem; + MemoryRegion opb_isa_fw; + + /* Registers */ + MemoryRegion lpc_hc_regs; + MemoryRegion opb_master_regs; + + /* OPB Master LS registers */ + uint32_t opb_irq_stat; + uint32_t opb_irq_mask; + uint32_t opb_irq_pol; + uint32_t opb_irq_input; + + /* LPC HC registers */ + uint32_t lpc_hc_fw_seg_idsel; + uint32_t lpc_hc_fw_rd_acc_size; + uint32_t lpc_hc_irqser_ctrl; + uint32_t lpc_hc_irqmask; + uint32_t lpc_hc_irqstat; + uint32_t lpc_hc_error_addr; + + /* XSCOM registers */ + MemoryRegion xscom_regs; +} PnvLpcController; + +#endif /* _PPC_PNV_LPC_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h new file mode 100644 index 0000000000..c0a2fbb9f6 --- /dev/null +++ b/include/hw/ppc/pnv_xscom.h @@ -0,0 +1,78 @@ +/* + * QEMU PowerPC PowerNV XSCOM bus definitions + * + * Copyright (c) 2016, IBM Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef _PPC_PNV_XSCOM_H +#define _PPC_PNV_XSCOM_H + +#include "qom/object.h" + +typedef struct PnvChip PnvChip; + +typedef struct PnvXScomInterface { + Object parent; +} PnvXScomInterface; + +#define TYPE_PNV_XSCOM_INTERFACE "pnv-xscom-interface" +#define PNV_XSCOM_INTERFACE(obj) \ + OBJECT_CHECK(PnvXScomInterface, (obj), TYPE_PNV_XSCOM_INTERFACE) +#define PNV_XSCOM_INTERFACE_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvXScomInterfaceClass, (klass), \ + TYPE_PNV_XSCOM_INTERFACE) +#define PNV_XSCOM_INTERFACE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvXScomInterfaceClass, (obj), TYPE_PNV_XSCOM_INTERFACE) + +typedef struct PnvXScomInterfaceClass { + InterfaceClass parent; + int (*populate)(PnvXScomInterface *dev, void *fdt, int offset); +} PnvXScomInterfaceClass; + +/* + * Layout of the XSCOM PCB addresses of EX core 1 + * + * GPIO 0x1100xxxx + * SCOM 0x1101xxxx + * OHA 0x1102xxxx + * CLOCK CTL 0x1103xxxx + * FIR 0x1104xxxx + * THERM 0x1105xxxx + * <reserved> 0x1106xxxx + * .. + * 0x110Exxxx + * PCB SLAVE 0x110Fxxxx + */ + +#define PNV_XSCOM_EX_BASE 0x10000000 +#define PNV_XSCOM_EX_CORE_BASE(i) (PNV_XSCOM_EX_BASE | (((uint64_t)i) << 24)) +#define PNV_XSCOM_EX_CORE_SIZE 0x100000 + +#define PNV_XSCOM_LPC_BASE 0xb0020 +#define PNV_XSCOM_LPC_SIZE 0x4 + +extern void pnv_xscom_realize(PnvChip *chip, Error **errp); +extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); + +extern void pnv_xscom_add_subregion(PnvChip *chip, hwaddr offset, + MemoryRegion *mr); +extern void pnv_xscom_region_init(MemoryRegion *mr, + struct Object *owner, + const MemoryRegionOps *ops, + void *opaque, + const char *name, + uint64_t size); + +#endif /* _PPC_PNV_XSCOM_H */ diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index aeaba3edf9..bd5bcf70de 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -6,12 +6,14 @@ #include "hw/ppc/xics.h" #include "hw/ppc/spapr_drc.h" #include "hw/mem/pc-dimm.h" +#include "hw/ppc/spapr_ovec.h" struct VIOsPAPRBus; struct sPAPRPHBState; struct sPAPRNVRAM; typedef struct sPAPRConfigureConnectorState sPAPRConfigureConnectorState; typedef struct sPAPREventLogEntry sPAPREventLogEntry; +typedef struct sPAPREventSource sPAPREventSource; #define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL #define SPAPR_ENTRY_POINT 0x100 @@ -63,17 +65,23 @@ struct sPAPRMachineState { uint32_t htab_shift; hwaddr rma_size; int vrma_adjust; - hwaddr fdt_addr, rtas_addr; ssize_t rtas_size; void *rtas_blob; - void *fdt_skel; + long kernel_size; + bool kernel_le; + uint32_t initrd_base; + long initrd_size; uint64_t rtc_offset; /* Now used only during incoming migration */ struct PPCTimebase tb; bool has_graphics; + sPAPROptionVector *ov5; /* QEMU-supported option vectors */ + sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ + bool cas_reboot; - uint32_t check_exception_irq; Notifier epow_notifier; QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; + bool use_hotplug_event_source; + sPAPREventSource *event_sources; /* Migration state */ int htab_save_index; @@ -527,8 +535,8 @@ void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn); target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *sm, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets); -int spapr_rtas_device_tree_setup(void *fdt, hwaddr rtas_addr, - hwaddr rtas_size); +void spapr_dt_rtas_tokens(void *fdt, int rtas); +void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_TCE_PAGE_SHIFT 12 #define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT) @@ -578,10 +586,11 @@ struct sPAPREventLogEntry { }; void spapr_events_init(sPAPRMachineState *sm); -void spapr_events_fdt_skel(void *fdt, uint32_t epow_irq); +void spapr_dt_events(sPAPRMachineState *sm, void *fdt); int spapr_h_cas_compose_response(sPAPRMachineState *sm, target_ulong addr, target_ulong size, - bool cpu_update, bool memory_update); + bool cpu_update, + sPAPROptionVector *ov5_updates); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); void spapr_tce_table_enable(sPAPRTCETable *tcet, uint32_t page_shift, uint64_t bus_offset, @@ -601,6 +610,10 @@ void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, uint32_t count); void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, uint32_t count); +void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index); +void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, + uint32_t count, uint32_t index); void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, Error **errp); void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, sPAPRMachineState *spapr); diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h new file mode 100644 index 0000000000..6a06da32e6 --- /dev/null +++ b/include/hw/ppc/spapr_ovec.h @@ -0,0 +1,67 @@ +/* + * QEMU SPAPR Option/Architecture Vector Definitions + * + * Each architecture option is organized/documented by the following + * in LoPAPR 1.1, Table 244: + * + * <vector number>: the bit-vector in which the option is located + * <vector byte>: the byte offset of the vector entry + * <vector bit>: the bit offset within the vector entry + * + * where each vector entry can be one or more bytes. + * + * Firmware expects a somewhat literal encoding of this bit-vector + * structure, where each entry is stored in little-endian so that the + * byte ordering reflects that of the documentation, but where each bit + * offset is from "left-to-right" in the traditional representation of + * a byte value where the MSB is the left-most bit. Thus, each + * individual byte encodes the option bits in reverse order of the + * documented bit. + * + * These definitions/helpers attempt to abstract away this internal + * representation so that we can define/set/test for individual option + * bits using only the documented values. This is done mainly by relying + * on a bitmap to approximate the documented "bit-vector" structure and + * handling conversations to-from the internal representation under the + * covers. + * + * Copyright IBM Corp. 2016 + * + * Authors: + * Michael Roth <mdroth@linux.vnet.ibm.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef _SPAPR_OVEC_H +#define _SPAPR_OVEC_H + +#include "cpu.h" + +typedef struct sPAPROptionVector sPAPROptionVector; + +#define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) + +/* option vector 5 */ +#define OV5_DRCONF_MEMORY OV_BIT(2, 2) +#define OV5_FORM1_AFFINITY OV_BIT(5, 0) +#define OV5_HP_EVT OV_BIT(6, 5) + +/* interfaces */ +sPAPROptionVector *spapr_ovec_new(void); +sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); +void spapr_ovec_intersect(sPAPROptionVector *ov, + sPAPROptionVector *ov1, + sPAPROptionVector *ov2); +bool spapr_ovec_diff(sPAPROptionVector *ov, + sPAPROptionVector *ov_old, + sPAPROptionVector *ov_new); +void spapr_ovec_cleanup(sPAPROptionVector *ov); +void spapr_ovec_set(sPAPROptionVector *ov, long bitnr); +void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr); +bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr); +sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); +int spapr_ovec_populate_dt(void *fdt, int fdt_offset, + sPAPROptionVector *ov, const char *name); + +#endif /* !defined (_SPAPR_OVEC_H) */ diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index 40d0e5f6a3..14f502240e 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -76,14 +76,12 @@ struct VIOsPAPRDevice { struct VIOsPAPRBus { BusState bus; uint32_t next_reg; - int (*init)(VIOsPAPRDevice *dev); - int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); }; extern VIOsPAPRBus *spapr_vio_bus_init(void); extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg); -extern int spapr_populate_vdevice(VIOsPAPRBus *bus, void *fdt); -extern int spapr_populate_chosen_stdout(void *fdt, VIOsPAPRBus *bus); +void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt); +extern gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus); static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev) { diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 66ae55ded3..3f0c31610a 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -117,6 +117,8 @@ struct ICPState { uint8_t mfrr; qemu_irq output; bool cap_irq_xics_enabled; + + XICSState *xics; }; #define TYPE_ICS_BASE "ics-base" @@ -185,18 +187,21 @@ int xics_spapr_alloc(XICSState *icp, int irq_hint, bool lsi, Error **errp); int xics_spapr_alloc_block(XICSState *icp, int num, bool lsi, bool align, Error **errp); void xics_spapr_free(XICSState *icp, int irq, int num); +void spapr_dt_xics(XICSState *xics, void *fdt, uint32_t phandle); void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu); void xics_cpu_destroy(XICSState *icp, PowerPCCPU *cpu); +void xics_set_nr_servers(XICSState *xics, uint32_t nr_servers, + const char *typename, Error **errp); /* Internal XICS interfaces */ int xics_get_cpu_index_by_dt_id(int cpu_dt_id); -void icp_set_cppr(XICSState *icp, int server, uint8_t cppr); -void icp_set_mfrr(XICSState *icp, int server, uint8_t mfrr); +void icp_set_cppr(ICPState *icp, uint8_t cppr); +void icp_set_mfrr(ICPState *icp, uint8_t mfrr); uint32_t icp_accept(ICPState *ss); uint32_t icp_ipoll(ICPState *ss, uint32_t *mfrr); -void icp_eoi(XICSState *icp, int server, uint32_t xirr); +void icp_eoi(ICPState *icp, uint32_t xirr); void ics_simple_write_xive(ICSState *ics, int nr, int server, uint8_t priority, uint8_t saved_priority); diff --git a/pc-bios/README b/pc-bios/README index 5a8a93193e..47a913f9c7 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -17,7 +17,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20160223. + built from git tag qemu-slof-20161019. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as @@ -42,3 +42,8 @@ it was compiled using the qemu-ppce500 target. A git mirror is available at: git://git.qemu-project.org/u-boot.git The hash used to compile the current version is: 2072e72 + +- Skiboot (https://github.com/open-power/skiboot/) is an OPAL + (OpenPower Abstraction Layer) firmware for OpenPOWER systems. It can + run an hypervisor OS or simply a host OS on the "baremetal" + platform, also known as the PowerNV (Non-Virtualized) platform. diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid Binary files differnew file mode 100644 index 0000000000..0e59a8280d --- /dev/null +++ b/pc-bios/skiboot.lid diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin Binary files differindex f2384939ef..30ce7ac384 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin diff --git a/roms/Makefile b/roms/Makefile index 88b3709d4d..b5e5a69e91 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -63,6 +63,7 @@ default: @echo " efirom -- update nic roms (bios+efi, this needs" @echo " the EfiRom utility from edk2 / tianocore)" @echo " slof -- update slof.bin" + @echo " skiboot -- update skiboot.lid" @echo " u-boot.e500 -- update u-boot.e500" bios: build-seabios-config-seabios-128k build-seabios-config-seabios-256k @@ -103,7 +104,7 @@ build-lgplvgabios: $(MAKE) -C vgabios $(vgabios_targets) -.PHONY: sgabios +.PHONY: sgabios skiboot sgabios: $(MAKE) -C sgabios cp sgabios/sgabios.bin ../pc-bios @@ -146,6 +147,10 @@ u-boot.e500: $(powerpc_cross_prefix)strip u-boot/build.e500/u-boot -o \ ../pc-bios/u-boot.e500 +skiboot: + $(MAKE) -C skiboot CROSS=$(powerpc64_cross_prefix) + cp skiboot/skiboot.lid ../pc-bios/skiboot.lid + clean: rm -rf seabios/.config seabios/out seabios/builds $(MAKE) -C vgabios clean @@ -155,3 +160,4 @@ clean: $(MAKE) -C ipxe/src veryclean $(MAKE) -C SLOF clean rm -rf u-boot/build.e500 + $(MAKE) -C skiboot clean diff --git a/roms/SLOF b/roms/SLOF -Subproject e3d05727a074619fc12d0a67f05cf2c42c875cc +Subproject efd65f49929d7db775b26066d538c8120ae3db9 diff --git a/roms/skiboot b/roms/skiboot new file mode 160000 +Subproject 762d0082f18e4fb921a2d44a1051b02d8b0f638 diff --git a/target-ppc/excp_helper.c b/target-ppc/excp_helper.c index 921c39d33f..808760bf53 100644 --- a/target-ppc/excp_helper.c +++ b/target-ppc/excp_helper.c @@ -213,7 +213,12 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) cs->halted = 1; cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } - new_msr |= (target_ulong)MSR_HVB; + if (env->msr_mask & MSR_HVB) { + /* ISA specifies HV, but can be delivered to guest with HV clear + * (e.g., see FWNMI in PAPR). + */ + new_msr |= (target_ulong)MSR_HVB; + } ail = 0; /* machine check exceptions don't have ME set */ @@ -385,14 +390,23 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) srr1 = SPR_BOOKE_CSRR1; break; case POWERPC_EXCP_RESET: /* System reset exception */ + /* A power-saving exception sets ME, otherwise it is unchanged */ if (msr_pow) { /* indicate that we resumed from power save mode */ msr |= 0x10000; + new_msr |= ((target_ulong)1 << MSR_ME); + } + if (env->msr_mask & MSR_HVB) { + /* ISA specifies HV, but can be delivered to guest with HV clear + * (e.g., see FWNMI in PAPR, NMI injection in QEMU). + */ + new_msr |= (target_ulong)MSR_HVB; } else { - new_msr &= ~((target_ulong)1 << MSR_ME); + if (msr_pow) { + cpu_abort(cs, "Trying to deliver power-saving system reset " + "exception %d with no HV support\n", excp); + } } - - new_msr |= (target_ulong)MSR_HVB; ail = 0; break; case POWERPC_EXCP_DSEG: /* Data segment exception */ @@ -609,9 +623,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) env->spr[srr1] = msr; /* Sanity check */ - if (!(env->msr_mask & MSR_HVB) && (srr0 == SPR_HSRR0)) { - cpu_abort(cs, "Trying to deliver HV exception %d with " - "no HV support\n", excp); + if (!(env->msr_mask & MSR_HVB)) { + if (new_msr & MSR_HVB) { + cpu_abort(cs, "Trying to deliver HV exception (MSR) %d with " + "no HV support\n", excp); + } + if (srr0 == SPR_HSRR0) { + cpu_abort(cs, "Trying to deliver HV exception (HSRR) %d with " + "no HV support\n", excp); + } } /* If any alternate SRR register are defined, duplicate saved values */ diff --git a/target-ppc/fpu_helper.c b/target-ppc/fpu_helper.c index b0760f041d..8a389e19af 100644 --- a/target-ppc/fpu_helper.c +++ b/target-ppc/fpu_helper.c @@ -2362,6 +2362,58 @@ VSX_MADD(xvnmaddmsp, 4, float32, VsrW(i), NMADD_FLGS, 0, 0, 0) VSX_MADD(xvnmsubasp, 4, float32, VsrW(i), NMSUB_FLGS, 1, 0, 0) VSX_MADD(xvnmsubmsp, 4, float32, VsrW(i), NMSUB_FLGS, 0, 0, 0) +/* VSX_SCALAR_CMP_DP - VSX scalar floating point compare double precision + * op - instruction mnemonic + * cmp - comparison operation + * exp - expected result of comparison + * svxvc - set VXVC bit + */ +#define VSX_SCALAR_CMP_DP(op, cmp, exp, svxvc) \ +void helper_##op(CPUPPCState *env, uint32_t opcode) \ +{ \ + ppc_vsr_t xt, xa, xb; \ + bool vxsnan_flag = false, vxvc_flag = false, vex_flag = false; \ + \ + getVSR(xA(opcode), &xa, env); \ + getVSR(xB(opcode), &xb, env); \ + getVSR(xT(opcode), &xt, env); \ + \ + if (float64_is_signaling_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_signaling_nan(xb.VsrD(0), &env->fp_status)) { \ + vxsnan_flag = true; \ + if (fpscr_ve == 0 && svxvc) { \ + vxvc_flag = true; \ + } \ + } else if (svxvc) { \ + vxvc_flag = float64_is_quiet_nan(xa.VsrD(0), &env->fp_status) || \ + float64_is_quiet_nan(xb.VsrD(0), &env->fp_status); \ + } \ + if (vxsnan_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + } \ + if (vxvc_flag) { \ + float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + } \ + vex_flag = fpscr_ve && (vxvc_flag || vxsnan_flag); \ + \ + if (!vex_flag) { \ + if (float64_##cmp(xb.VsrD(0), xa.VsrD(0), &env->fp_status) == exp) { \ + xt.VsrD(0) = -1; \ + xt.VsrD(1) = 0; \ + } else { \ + xt.VsrD(0) = 0; \ + xt.VsrD(1) = 0; \ + } \ + } \ + putVSR(xT(opcode), &xt, env); \ + helper_float_check_status(env); \ +} + +VSX_SCALAR_CMP_DP(xscmpeqdp, eq, 1, 0) +VSX_SCALAR_CMP_DP(xscmpgedp, le, 1, 1) +VSX_SCALAR_CMP_DP(xscmpgtdp, lt, 1, 1) +VSX_SCALAR_CMP_DP(xscmpnedp, eq, 0, 0) + #define VSX_SCALAR_CMP(op, ordered) \ void helper_##op(CPUPPCState *env, uint32_t opcode) \ { \ @@ -2445,8 +2497,9 @@ VSX_MAX_MIN(xvminsp, minnum, 4, float32, VsrW(i)) * fld - vsr_t field (VsrD(*) or VsrW(*)) * cmp - comparison operation * svxvc - set VXVC bit + * exp - expected result of comparison */ -#define VSX_CMP(op, nels, tp, fld, cmp, svxvc) \ +#define VSX_CMP(op, nels, tp, fld, cmp, svxvc, exp) \ void helper_##op(CPUPPCState *env, uint32_t opcode) \ { \ ppc_vsr_t xt, xa, xb; \ @@ -2471,7 +2524,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.fld = 0; \ all_true = 0; \ } else { \ - if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == 1) { \ + if (tp##_##cmp(xb.fld, xa.fld, &env->fp_status) == exp) { \ xt.fld = -1; \ all_false = 0; \ } else { \ @@ -2488,12 +2541,14 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ float_check_status(env); \ } -VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0) -VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1) -VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1) -VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0) -VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1) -VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1) +VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) +VSX_CMP(xvcmpgedp, 2, float64, VsrD(i), le, 1, 1) +VSX_CMP(xvcmpgtdp, 2, float64, VsrD(i), lt, 1, 1) +VSX_CMP(xvcmpnedp, 2, float64, VsrD(i), eq, 0, 0) +VSX_CMP(xvcmpeqsp, 4, float32, VsrW(i), eq, 0, 1) +VSX_CMP(xvcmpgesp, 4, float32, VsrW(i), le, 1, 1) +VSX_CMP(xvcmpgtsp, 4, float32, VsrW(i), lt, 1, 1) +VSX_CMP(xvcmpnesp, 4, float32, VsrW(i), eq, 0, 0) /* VSX_CVT_FP_TO_FP - VSX floating point/floating point conversion * op - instruction mnemonic diff --git a/target-ppc/helper.h b/target-ppc/helper.h index 04c64217b7..3916b2eddc 100644 --- a/target-ppc/helper.h +++ b/target-ppc/helper.h @@ -272,6 +272,8 @@ DEF_HELPER_2(vextsh2w, void, avr, avr) DEF_HELPER_2(vextsb2d, void, avr, avr) DEF_HELPER_2(vextsh2d, void, avr, avr) DEF_HELPER_2(vextsw2d, void, avr, avr) +DEF_HELPER_2(vnegw, void, avr, avr) +DEF_HELPER_2(vnegd, void, avr, avr) DEF_HELPER_2(vupkhpx, void, avr, avr) DEF_HELPER_2(vupklpx, void, avr, avr) DEF_HELPER_2(vupkhsb, void, avr, avr) @@ -387,6 +389,10 @@ DEF_HELPER_2(xsnmaddadp, void, env, i32) DEF_HELPER_2(xsnmaddmdp, void, env, i32) DEF_HELPER_2(xsnmsubadp, void, env, i32) DEF_HELPER_2(xsnmsubmdp, void, env, i32) +DEF_HELPER_2(xscmpeqdp, void, env, i32) +DEF_HELPER_2(xscmpgtdp, void, env, i32) +DEF_HELPER_2(xscmpgedp, void, env, i32) +DEF_HELPER_2(xscmpnedp, void, env, i32) DEF_HELPER_2(xscmpodp, void, env, i32) DEF_HELPER_2(xscmpudp, void, env, i32) DEF_HELPER_2(xsmaxdp, void, env, i32) @@ -448,6 +454,7 @@ DEF_HELPER_2(xvmindp, void, env, i32) DEF_HELPER_2(xvcmpeqdp, void, env, i32) DEF_HELPER_2(xvcmpgedp, void, env, i32) DEF_HELPER_2(xvcmpgtdp, void, env, i32) +DEF_HELPER_2(xvcmpnedp, void, env, i32) DEF_HELPER_2(xvcvdpsp, void, env, i32) DEF_HELPER_2(xvcvdpsxds, void, env, i32) DEF_HELPER_2(xvcvdpsxws, void, env, i32) @@ -485,6 +492,7 @@ DEF_HELPER_2(xvminsp, void, env, i32) DEF_HELPER_2(xvcmpeqsp, void, env, i32) DEF_HELPER_2(xvcmpgesp, void, env, i32) DEF_HELPER_2(xvcmpgtsp, void, env, i32) +DEF_HELPER_2(xvcmpnesp, void, env, i32) DEF_HELPER_2(xvcvspdp, void, env, i32) DEF_HELPER_2(xvcvspsxds, void, env, i32) DEF_HELPER_2(xvcvspsxws, void, env, i32) diff --git a/target-ppc/int_helper.c b/target-ppc/int_helper.c index 5aee0a81c7..dca479838d 100644 --- a/target-ppc/int_helper.c +++ b/target-ppc/int_helper.c @@ -1949,6 +1949,18 @@ VEXT_SIGNED(vextsh2d, s64, UINT16_MAX, int16_t, int64_t) VEXT_SIGNED(vextsw2d, s64, UINT32_MAX, int32_t, int64_t) #undef VEXT_SIGNED +#define VNEG(name, element) \ +void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ +{ \ + int i; \ + VECTOR_FOR_INORDER_I(i, element) { \ + r->element[i] = -b->element[i]; \ + } \ +} +VNEG(vnegw, s32) +VNEG(vnegd, s64) +#undef VNEG + #define VSPLTI(suffix, element, splat_type) \ void helper_vspltis##suffix(ppc_avr_t *r, uint32_t splat) \ { \ diff --git a/target-ppc/translate.c b/target-ppc/translate.c index dab8f19a91..43505a936c 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -322,7 +322,7 @@ static void gen_debug_exception(DisasContext *ctx) */ if ((ctx->exception != POWERPC_EXCP_BRANCH) && (ctx->exception != POWERPC_EXCP_SYNC)) { - gen_update_nip(ctx, ctx->nip - 4); + gen_update_nip(ctx, ctx->nip); } t0 = tcg_const_i32(EXCP_DEBUG); gen_helper_raise_exception(cpu_env, t0); @@ -376,6 +376,9 @@ GEN_OPCODE2(name, onam, opc1, opc2, opc3, inval, type, type2) #define GEN_HANDLER_E_2(name, opc1, opc2, opc3, opc4, inval, type, type2) \ GEN_OPCODE3(name, opc1, opc2, opc3, opc4, inval, type, type2) +#define GEN_HANDLER2_E_2(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) \ +GEN_OPCODE4(name, onam, opc1, opc2, opc3, opc4, inval, typ, typ2) + typedef struct opcode_t { unsigned char opc1, opc2, opc3, opc4; #if HOST_LONG_BITS == 64 /* Explicitly align to 64 bits */ @@ -662,6 +665,21 @@ EXTRACT_HELPER(IMM8, 11, 8); }, \ .oname = stringify(name), \ } +#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + .oname = onam, \ + }, \ + .oname = onam, \ +} #else #define GEN_OPCODE(name, op1, op2, op3, invl, _typ, _typ2) \ { \ @@ -720,6 +738,20 @@ EXTRACT_HELPER(IMM8, 11, 8); }, \ .oname = stringify(name), \ } +#define GEN_OPCODE4(name, onam, op1, op2, op3, op4, invl, _typ, _typ2) \ +{ \ + .opc1 = op1, \ + .opc2 = op2, \ + .opc3 = op3, \ + .opc4 = op4, \ + .handler = { \ + .inval1 = invl, \ + .type = _typ, \ + .type2 = _typ2, \ + .handler = &gen_##name, \ + }, \ + .oname = onam, \ +} #endif /* SPR load/store helpers */ diff --git a/target-ppc/translate/vmx-impl.inc.c b/target-ppc/translate/vmx-impl.inc.c index c8998f3eab..fc612d9f37 100644 --- a/target-ppc/translate/vmx-impl.inc.c +++ b/target-ppc/translate/vmx-impl.inc.c @@ -182,6 +182,52 @@ static void gen_mtvscr(DisasContext *ctx) tcg_temp_free_ptr(p); } +#define GEN_VX_VMUL10(name, add_cin, ret_carry) \ +static void glue(gen_, name)(DisasContext *ctx) \ +{ \ + TCGv_i64 t0 = tcg_temp_new_i64(); \ + TCGv_i64 t1 = tcg_temp_new_i64(); \ + TCGv_i64 t2 = tcg_temp_new_i64(); \ + TCGv_i64 ten, z; \ + \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + \ + ten = tcg_const_i64(10); \ + z = tcg_const_i64(0); \ + \ + if (add_cin) { \ + tcg_gen_mulu2_i64(t0, t1, cpu_avrl[rA(ctx->opcode)], ten); \ + tcg_gen_andi_i64(t2, cpu_avrl[rB(ctx->opcode)], 0xF); \ + tcg_gen_add2_i64(cpu_avrl[rD(ctx->opcode)], t2, t0, t1, t2, z); \ + } else { \ + tcg_gen_mulu2_i64(cpu_avrl[rD(ctx->opcode)], t2, \ + cpu_avrl[rA(ctx->opcode)], ten); \ + } \ + \ + if (ret_carry) { \ + tcg_gen_mulu2_i64(t0, t1, cpu_avrh[rA(ctx->opcode)], ten); \ + tcg_gen_add2_i64(t0, cpu_avrl[rD(ctx->opcode)], t0, t1, t2, z); \ + tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); \ + } else { \ + tcg_gen_mul_i64(t0, cpu_avrh[rA(ctx->opcode)], ten); \ + tcg_gen_add_i64(cpu_avrh[rD(ctx->opcode)], t0, t2); \ + } \ + \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ + tcg_temp_free_i64(t2); \ + tcg_temp_free_i64(ten); \ + tcg_temp_free_i64(z); \ +} \ + +GEN_VX_VMUL10(vmul10uq, 0, 0); +GEN_VX_VMUL10(vmul10euq, 1, 0); +GEN_VX_VMUL10(vmul10cuq, 0, 1); +GEN_VX_VMUL10(vmul10ecuq, 1, 1); + /* Logical operations */ #define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -276,8 +322,30 @@ static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ } \ } +/* Adds support to provide invalid mask */ +#define GEN_VXFORM_DUAL_EXT(name0, flg0, flg2_0, inval0, \ + name1, flg1, flg2_1, inval1) \ +static void glue(gen_, name0##_##name1)(DisasContext *ctx) \ +{ \ + if ((Rc(ctx->opcode) == 0) && \ + ((ctx->insns_flags & flg0) || (ctx->insns_flags2 & flg2_0)) && \ + !(ctx->opcode & inval0)) { \ + gen_##name0(ctx); \ + } else if ((Rc(ctx->opcode) == 1) && \ + ((ctx->insns_flags & flg1) || (ctx->insns_flags2 & flg2_1)) && \ + !(ctx->opcode & inval1)) { \ + gen_##name1(ctx); \ + } else { \ + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ + } \ +} + GEN_VXFORM(vaddubm, 0, 0); +GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \ + vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800) GEN_VXFORM(vadduhm, 0, 1); +GEN_VXFORM_DUAL(vadduhm, PPC_ALTIVEC, PPC_NONE, \ + vmul10ecuq, PPC_NONE, PPC2_ISA300) GEN_VXFORM(vadduwm, 0, 2); GEN_VXFORM(vaddudm, 0, 3); GEN_VXFORM(vsububm, 0, 16); @@ -390,7 +458,11 @@ GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); GEN_VXFORM(vsubcuw, 0, 22); GEN_VXFORM_ENV(vaddubs, 0, 8); +GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ + vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) GEN_VXFORM_ENV(vadduhs, 0, 9); +GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ + vmul10euq, PPC_NONE, PPC2_ISA300) GEN_VXFORM_ENV(vadduws, 0, 10); GEN_VXFORM_ENV(vaddsbs, 0, 12); GEN_VXFORM_ENV(vaddshs, 0, 13); @@ -815,6 +887,8 @@ GEN_VXFORM_NOA(vclzb, 1, 28) GEN_VXFORM_NOA(vclzh, 1, 29) GEN_VXFORM_NOA(vclzw, 1, 30) GEN_VXFORM_NOA(vclzd, 1, 31) +GEN_VXFORM_NOA_2(vnegw, 1, 24, 6) +GEN_VXFORM_NOA_2(vnegd, 1, 24, 7) GEN_VXFORM_NOA_2(vextsb2w, 1, 24, 16) GEN_VXFORM_NOA_2(vextsh2w, 1, 24, 17) GEN_VXFORM_NOA_2(vextsb2d, 1, 24, 24) diff --git a/target-ppc/translate/vmx-ops.inc.c b/target-ppc/translate/vmx-ops.inc.c index 68cba3e474..cc7ed7eeed 100644 --- a/target-ppc/translate/vmx-ops.inc.c +++ b/target-ppc/translate/vmx-ops.inc.c @@ -55,8 +55,8 @@ GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, type0, type1) GEN_HANDLER_E(name0##_##name1, 0x4, opc2, opc3, 0x00000000, tp0, tp1), \ GEN_HANDLER_E(name0##_##name1, 0x4, opc2, (opc3 | 0x10), 0x00000000, tp0, tp1), -GEN_VXFORM(vaddubm, 0, 0), -GEN_VXFORM(vadduhm, 0, 1), +GEN_VXFORM_DUAL(vaddubm, vmul10cuq, 0, 0, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vadduhm, vmul10ecuq, 0, 1, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vadduwm, 0, 2), GEN_VXFORM_207(vaddudm, 0, 3), GEN_VXFORM_DUAL(vsububm, bcdadd, 0, 16, PPC_ALTIVEC, PPC_NONE), @@ -123,8 +123,8 @@ GEN_VXFORM(vslo, 6, 16), GEN_VXFORM(vsro, 6, 17), GEN_VXFORM(vaddcuw, 0, 6), GEN_VXFORM(vsubcuw, 0, 22), -GEN_VXFORM(vaddubs, 0, 8), -GEN_VXFORM(vadduhs, 0, 9), +GEN_VXFORM_DUAL(vaddubs, vmul10uq, 0, 8, PPC_ALTIVEC, PPC_NONE), +GEN_VXFORM_DUAL(vadduhs, vmul10euq, 0, 9, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vadduws, 0, 10), GEN_VXFORM(vaddsbs, 0, 12), GEN_VXFORM(vaddshs, 0, 13), @@ -215,6 +215,8 @@ GEN_VXFORM_DUAL_INV(vspltish, vinserth, 6, 13, 0x00000000, 0x100000, GEN_VXFORM_DUAL_INV(vspltisw, vinsertw, 6, 14, 0x00000000, 0x100000, PPC_ALTIVEC), GEN_VXFORM_300_EXT(vinsertd, 6, 15, 0x100000), +GEN_VXFORM_300_EO(vnegw, 0x01, 0x18, 0x06), +GEN_VXFORM_300_EO(vnegd, 0x01, 0x18, 0x07), GEN_VXFORM_300_EO(vextsb2w, 0x01, 0x18, 0x10), GEN_VXFORM_300_EO(vextsh2w, 0x01, 0x18, 0x11), GEN_VXFORM_300_EO(vextsb2d, 0x01, 0x18, 0x18), diff --git a/target-ppc/translate/vsx-impl.inc.c b/target-ppc/translate/vsx-impl.inc.c index 23ec1e115c..5a27be4bd4 100644 --- a/target-ppc/translate/vsx-impl.inc.c +++ b/target-ppc/translate/vsx-impl.inc.c @@ -132,6 +132,22 @@ static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, tcg_temp_free_i64(mask); } +static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, + TCGv_i64 inh, TCGv_i64 inl) +{ + TCGv_i64 hi = tcg_temp_new_i64(); + TCGv_i64 lo = tcg_temp_new_i64(); + + tcg_gen_bswap64_i64(hi, inh); + tcg_gen_bswap64_i64(lo, inl); + tcg_gen_shri_i64(outh, hi, 32); + tcg_gen_deposit_i64(outh, outh, hi, 32, 32); + tcg_gen_shri_i64(outl, lo, 32); + tcg_gen_deposit_i64(outl, outl, lo, 32, 32); + + tcg_temp_free_i64(hi); + tcg_temp_free_i64(lo); +} static void gen_lxvh8x(DisasContext *ctx) { TCGv EA; @@ -604,6 +620,10 @@ GEN_VSX_HELPER_2(xsnmaddadp, 0x04, 0x14, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsnmaddmdp, 0x04, 0x15, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsnmsubadp, 0x04, 0x16, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsnmsubmdp, 0x04, 0x17, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xscmpeqdp, 0x0C, 0x00, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpgtdp, 0x0C, 0x01, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpgedp, 0x0C, 0x02, 0, PPC2_ISA300) +GEN_VSX_HELPER_2(xscmpnedp, 0x0C, 0x03, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xscmpodp, 0x0C, 0x05, 0, PPC2_VSX) GEN_VSX_HELPER_2(xscmpudp, 0x0C, 0x04, 0, PPC2_VSX) GEN_VSX_HELPER_2(xsmaxdp, 0x00, 0x14, 0, PPC2_VSX) @@ -665,6 +685,7 @@ GEN_VSX_HELPER_2(xvmindp, 0x00, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpeqdp, 0x0C, 0x0C, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgtdp, 0x0C, 0x0D, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgedp, 0x0C, 0x0E, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpnedp, 0x0C, 0x0F, 0, PPC2_ISA300) GEN_VSX_HELPER_2(xvcvdpsp, 0x12, 0x18, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvdpsxds, 0x10, 0x1D, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvdpsxws, 0x10, 0x0D, 0, PPC2_VSX) @@ -702,6 +723,7 @@ GEN_VSX_HELPER_2(xvminsp, 0x00, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpeqsp, 0x0C, 0x08, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgtsp, 0x0C, 0x09, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcmpgesp, 0x0C, 0x0A, 0, PPC2_VSX) +GEN_VSX_HELPER_2(xvcmpnesp, 0x0C, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspdp, 0x12, 0x1C, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspsxds, 0x10, 0x19, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvcvspsxws, 0x10, 0x09, 0, PPC2_VSX) @@ -717,6 +739,67 @@ GEN_VSX_HELPER_2(xvrspim, 0x12, 0x0B, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspip, 0x12, 0x0A, 0, PPC2_VSX) GEN_VSX_HELPER_2(xvrspiz, 0x12, 0x09, 0, PPC2_VSX) +static void gen_xxbrd(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_bswap64_i64(xth, xbh); + tcg_gen_bswap64_i64(xtl, xbl); +} + +static void gen_xxbrh(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_bswap16x8(xth, xtl, xbh, xbl); +} + +static void gen_xxbrq(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 t0 = tcg_temp_new_i64(); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + tcg_gen_bswap64_i64(t0, xbl); + tcg_gen_bswap64_i64(xtl, xbh); + tcg_gen_mov_i64(xth, t0); + tcg_temp_free_i64(t0); +} + +static void gen_xxbrw(DisasContext *ctx) +{ + TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); + TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + + if (unlikely(!ctx->vsx_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VSXU); + return; + } + gen_bswap32x4(xth, xtl, xbh, xbl); +} + #define VSX_LOGICAL(name, tcg_op) \ static void glue(gen_, name)(DisasContext * ctx) \ { \ diff --git a/target-ppc/translate/vsx-ops.inc.c b/target-ppc/translate/vsx-ops.inc.c index 10eb4b9470..3d9104155a 100644 --- a/target-ppc/translate/vsx-ops.inc.c +++ b/target-ppc/translate/vsx-ops.inc.c @@ -39,6 +39,10 @@ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2) +#define GEN_XX2FORM_EO(name, opc2, opc3, opc4, fl2) \ +GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 0, opc3, opc4, 0, PPC_NONE, fl2), \ +GEN_HANDLER2_E_2(name, #name, 0x3C, opc2 | 1, opc3, opc4, 0, PPC_NONE, fl2) + #define GEN_XX3FORM(name, opc2, opc3, fl2) \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 0, opc3, 0, PPC_NONE, fl2), \ GEN_HANDLER2_E(name, #name, 0x3C, opc2 | 1, opc3, 0, PPC_NONE, fl2), \ @@ -110,6 +114,10 @@ GEN_XX3FORM(xsnmaddadp, 0x04, 0x14, PPC2_VSX), GEN_XX3FORM(xsnmaddmdp, 0x04, 0x15, PPC2_VSX), GEN_XX3FORM(xsnmsubadp, 0x04, 0x16, PPC2_VSX), GEN_XX3FORM(xsnmsubmdp, 0x04, 0x17, PPC2_VSX), +GEN_XX3FORM(xscmpeqdp, 0x0C, 0x00, PPC2_ISA300), +GEN_XX3FORM(xscmpgtdp, 0x0C, 0x01, PPC2_ISA300), +GEN_XX3FORM(xscmpgedp, 0x0C, 0x02, PPC2_ISA300), +GEN_XX3FORM(xscmpnedp, 0x0C, 0x03, PPC2_ISA300), GEN_XX2IFORM(xscmpodp, 0x0C, 0x05, PPC2_VSX), GEN_XX2IFORM(xscmpudp, 0x0C, 0x04, PPC2_VSX), GEN_XX3FORM(xsmaxdp, 0x00, 0x14, PPC2_VSX), @@ -171,6 +179,7 @@ GEN_XX3FORM(xvmindp, 0x00, 0x1D, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpeqdp, 0x0C, 0x0C, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgtdp, 0x0C, 0x0D, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgedp, 0x0C, 0x0E, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpnedp, 0x0C, 0x0F, PPC2_ISA300), GEN_XX2FORM(xvcvdpsp, 0x12, 0x18, PPC2_VSX), GEN_XX2FORM(xvcvdpsxds, 0x10, 0x1D, PPC2_VSX), GEN_XX2FORM(xvcvdpsxws, 0x10, 0x0D, PPC2_VSX), @@ -208,6 +217,7 @@ GEN_XX3FORM(xvminsp, 0x00, 0x19, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpeqsp, 0x0C, 0x08, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgtsp, 0x0C, 0x09, PPC2_VSX), GEN_XX3_RC_FORM(xvcmpgesp, 0x0C, 0x0A, PPC2_VSX), +GEN_XX3_RC_FORM(xvcmpnesp, 0x0C, 0x0B, PPC2_ISA300), GEN_XX2FORM(xvcvspdp, 0x12, 0x1C, PPC2_VSX), GEN_XX2FORM(xvcvspsxds, 0x10, 0x19, PPC2_VSX), GEN_XX2FORM(xvcvspsxws, 0x10, 0x09, PPC2_VSX), @@ -222,6 +232,10 @@ GEN_XX2FORM(xvrspic, 0x16, 0x0A, PPC2_VSX), GEN_XX2FORM(xvrspim, 0x12, 0x0B, PPC2_VSX), GEN_XX2FORM(xvrspip, 0x12, 0x0A, PPC2_VSX), GEN_XX2FORM(xvrspiz, 0x12, 0x09, PPC2_VSX), +GEN_XX2FORM_EO(xxbrh, 0x16, 0x1D, 0x07, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrw, 0x16, 0x1D, 0x0F, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrd, 0x16, 0x1D, 0x17, PPC2_ISA300), +GEN_XX2FORM_EO(xxbrq, 0x16, 0x1D, 0x1F, PPC2_ISA300), #define VSX_LOGICAL(name, opc2, opc3, fl2) \ GEN_XX3FORM(name, opc2, opc3, fl2) diff --git a/tests/Makefile.include b/tests/Makefile.include index 22656eaf26..1a135d2340 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -282,6 +282,7 @@ check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF) gcov-files-ppc64-y += hw/usb/hcd-uhci.c check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF) gcov-files-ppc64-y += hw/usb/hcd-xhci.c +check-qtest-ppc64-y += $(check-qtest-virtio-y) check-qtest-sh4-y = tests/endianness-test$(EXESUF) @@ -615,7 +616,7 @@ libqos-pc-obj-y += tests/libqos/ahci.o libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o libqos-usb-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o -libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o +libqos-virtio-obj-y = $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o tests/rtc-test$(EXESUF): tests/rtc-test.o diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 9c0adce220..70bcafa9e4 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -78,25 +78,23 @@ static void string_bswap16(uint16_t *s, size_t bytes) /** * Verify that the transfer did not corrupt our state at all. */ -static void verify_state(AHCIQState *ahci) +static void verify_state(AHCIQState *ahci, uint64_t hba_old) { int i, j; uint32_t ahci_fingerprint; uint64_t hba_base; - uint64_t hba_stored; AHCICommandHeader cmd; ahci_fingerprint = qpci_config_readl(ahci->dev, PCI_VENDOR_ID); g_assert_cmphex(ahci_fingerprint, ==, ahci->fingerprint); /* If we haven't initialized, this is as much as can be validated. */ - if (!ahci->hba_base) { + if (!ahci->enabled) { return; } hba_base = (uint64_t)qpci_config_readl(ahci->dev, PCI_BASE_ADDRESS_5); - hba_stored = (uint64_t)(uintptr_t)ahci->hba_base; - g_assert_cmphex(hba_base, ==, hba_stored); + g_assert_cmphex(hba_base, ==, hba_old); g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP), ==, ahci->cap); g_assert_cmphex(ahci_rreg(ahci, AHCI_CAP2), ==, ahci->cap2); @@ -119,12 +117,15 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) QOSState *tmp = to->parent; QPCIDevice *dev = to->dev; char *uri_local = NULL; + uint64_t hba_old; if (uri == NULL) { uri_local = g_strdup_printf("%s%s", "unix:", mig_socket); uri = uri_local; } + hba_old = (uint64_t)qpci_config_readl(from->dev, PCI_BASE_ADDRESS_5); + /* context will be 'to' after completion. */ migrate(from->parent, to->parent, uri); @@ -141,7 +142,7 @@ static void ahci_migrate(AHCIQState *from, AHCIQState *to, const char *uri) from->parent = tmp; from->dev = dev; - verify_state(to); + verify_state(to, hba_old); g_free(uri_local); } diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index 3979b20bb0..8c42ca919f 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -87,7 +87,7 @@ typedef struct e1000e_device { QPCIDevice *pci_dev; - void *mac_regs; + QPCIBar mac_regs; uint64_t tx_ring; uint64_t rx_ring; @@ -119,12 +119,12 @@ static QPCIDevice *e1000e_device_find(QPCIBus *bus) static void e1000e_macreg_write(e1000e_device *d, uint32_t reg, uint32_t val) { - qpci_io_writel(d->pci_dev, d->mac_regs + reg, val); + qpci_io_writel(d->pci_dev, d->mac_regs, reg, val); } static uint32_t e1000e_macreg_read(e1000e_device *d, uint32_t reg) { - return qpci_io_readl(d->pci_dev, d->mac_regs + reg); + return qpci_io_readl(d->pci_dev, d->mac_regs, reg); } static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) @@ -138,7 +138,6 @@ static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) /* Map BAR0 (mac registers) */ d->mac_regs = qpci_iomap(d->pci_dev, 0, NULL); - g_assert_nonnull(d->mac_regs); /* Reset the device */ val = e1000e_macreg_read(d, E1000E_CTRL); diff --git a/tests/ide-test.c b/tests/ide-test.c index a8a4081f78..67c7df0c8d 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -137,7 +137,7 @@ static void ide_test_quit(void) qtest_end(); } -static QPCIDevice *get_pci_device(uint16_t *bmdma_base) +static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar) { QPCIDevice *dev; uint16_t vendor_id, device_id; @@ -156,7 +156,9 @@ static QPCIDevice *get_pci_device(uint16_t *bmdma_base) g_assert(device_id == PCI_DEVICE_ID_INTEL_82371SB_1); /* Map bmdma BAR */ - *bmdma_base = (uint16_t)(uintptr_t) qpci_iomap(dev, 4, NULL); + *bmdma_bar = qpci_iomap(dev, 4, NULL); + + *ide_bar = qpci_legacy_iomap(dev, IDE_BASE); qpci_device_enable(dev); @@ -179,17 +181,18 @@ typedef struct PrdtEntry { static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, PrdtEntry *prdt, int prdt_entries, - void(*post_exec)(uint64_t sector, int nb_sectors)) + void(*post_exec)(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t sector, int nb_sectors)) { QPCIDevice *dev; - uint16_t bmdma_base; + QPCIBar bmdma_bar, ide_bar; uintptr_t guest_prdt; size_t len; bool from_dev; uint8_t status; int flags; - dev = get_pci_device(&bmdma_base); + dev = get_pci_device(&bmdma_bar, &ide_bar); flags = cmd & ~0xff; cmd &= 0xff; @@ -214,59 +217,60 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, } /* Select device 0 */ - outb(IDE_BASE + reg_device, 0 | LBA); + qpci_io_writeb(dev, ide_bar, reg_device, 0 | LBA); /* Stop any running transfer, clear any pending interrupt */ - outb(bmdma_base + bmreg_cmd, 0); - outb(bmdma_base + bmreg_status, BM_STS_INTR); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_status, BM_STS_INTR); /* Setup PRDT */ len = sizeof(*prdt) * prdt_entries; guest_prdt = guest_alloc(guest_malloc, len); memwrite(guest_prdt, prdt, len); - outl(bmdma_base + bmreg_prdt, guest_prdt); + qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); /* ATA DMA command */ if (cmd == CMD_PACKET) { /* Enables ATAPI DMA; otherwise PIO is attempted */ - outb(IDE_BASE + reg_feature, 0x01); + qpci_io_writeb(dev, ide_bar, reg_feature, 0x01); } else { - outb(IDE_BASE + reg_nsectors, nb_sectors); - outb(IDE_BASE + reg_lba_low, sector & 0xff); - outb(IDE_BASE + reg_lba_middle, (sector >> 8) & 0xff); - outb(IDE_BASE + reg_lba_high, (sector >> 16) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_nsectors, nb_sectors); + qpci_io_writeb(dev, ide_bar, reg_lba_low, sector & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, (sector >> 8) & 0xff); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (sector >> 16) & 0xff); } - outb(IDE_BASE + reg_command, cmd); + qpci_io_writeb(dev, ide_bar, reg_command, cmd); if (post_exec) { - post_exec(sector, nb_sectors); + post_exec(dev, ide_bar, sector, nb_sectors); } /* Start DMA transfer */ - outb(bmdma_base + bmreg_cmd, BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, + BM_CMD_START | (from_dev ? BM_CMD_WRITE : 0)); if (flags & CMDF_ABORT) { - outb(bmdma_base + bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); } /* Wait for the DMA transfer to complete */ do { - status = inb(bmdma_base + bmreg_status); + status = qpci_io_readb(dev, bmdma_bar, bmreg_status); } while ((status & (BM_STS_ACTIVE | BM_STS_INTR)) == BM_STS_ACTIVE); g_assert_cmpint(get_irq(IDE_PRIMARY_IRQ), ==, !!(status & BM_STS_INTR)); /* Check IDE status code */ - assert_bit_set(inb(IDE_BASE + reg_status), DRDY); - assert_bit_clear(inb(IDE_BASE + reg_status), BSY | DRQ); + assert_bit_set(qpci_io_readb(dev, ide_bar, reg_status), DRDY); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), BSY | DRQ); /* Reading the status register clears the IRQ */ g_assert(!get_irq(IDE_PRIMARY_IRQ)); /* Stop DMA transfer if still active */ if (status & BM_STS_ACTIVE) { - outb(bmdma_base + bmreg_cmd, 0); + qpci_io_writeb(dev, bmdma_bar, bmreg_cmd, 0); } free_pci_device(dev); @@ -276,6 +280,8 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, static void test_bmdma_simple_rw(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; uint8_t *buf; uint8_t *cmpbuf; @@ -289,6 +295,8 @@ static void test_bmdma_simple_rw(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + buf = g_malloc(len); cmpbuf = g_malloc(len); @@ -299,7 +307,7 @@ static void test_bmdma_simple_rw(void) status = send_dma_request(CMD_WRITE_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Write 0xaa pattern to sector 1 */ memset(buf, 0xaa, len); @@ -308,14 +316,14 @@ static void test_bmdma_simple_rw(void) status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Read and verify 0x55 pattern in sector 0 */ memset(cmpbuf, 0x55, len); status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); memread(guest_buf, buf, len); g_assert(memcmp(buf, cmpbuf, len) == 0); @@ -325,7 +333,7 @@ static void test_bmdma_simple_rw(void) status = send_dma_request(CMD_READ_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); memread(guest_buf, buf, len); g_assert(memcmp(buf, cmpbuf, len) == 0); @@ -337,6 +345,8 @@ static void test_bmdma_simple_rw(void) static void test_bmdma_short_prdt(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; PrdtEntry prdt[] = { @@ -346,21 +356,25 @@ static void test_bmdma_short_prdt(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_one_sector_short_prdt(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; /* Read 2 sectors but only give 1 sector in PRDT */ @@ -371,21 +385,25 @@ static void test_bmdma_one_sector_short_prdt(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 2, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 2, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, 0); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_long_prdt(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; PrdtEntry prdt[] = { @@ -395,23 +413,29 @@ static void test_bmdma_long_prdt(void) }, }; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Normal request */ status = send_dma_request(CMD_READ_DMA, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); /* Abort the request before it completes */ status = send_dma_request(CMD_READ_DMA | CMDF_ABORT, 0, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_no_busmaster(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* No PRDT_EOT, each entry addr 0/size 64k, and in theory qemu shouldn't be * able to access it anyway because the Bus Master bit in the PCI command * register isn't set. This is complete nonsense, but it used to be pretty @@ -424,7 +448,7 @@ static void test_bmdma_no_busmaster(void) /* Not entirely clear what the expected result is, but this is what we get * in practice. At least we want to be aware of any changes. */ g_assert_cmphex(status, ==, BM_STS_ACTIVE | BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); } static void test_bmdma_setup(void) @@ -454,6 +478,8 @@ static void string_cpu_to_be16(uint16_t *s, size_t bytes) static void test_identify(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; uint16_t buf[256]; int i; @@ -464,23 +490,25 @@ static void test_identify(void) "-global ide-hd.ver=%s", tmp_path, "testdisk", "version"); + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* IDENTIFY command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_IDENTIFY); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_IDENTIFY); /* Read in the IDENTIFY buffer and check registers */ - data = inb(IDE_BASE + reg_device); + data = qpci_io_readb(dev, ide_bar, reg_device); g_assert_cmpint(data & DEV, ==, 0); for (i = 0; i < 256; i++) { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, DRDY | DRQ); assert_bit_clear(data, BSY | DF | ERR); - ((uint16_t*) buf)[i] = inw(IDE_BASE + reg_data); + buf[i] = qpci_io_readw(dev, ide_bar, reg_data); } - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, DRDY); assert_bit_clear(data, BSY | DF | ERR | DRQ); @@ -505,11 +533,15 @@ static void test_identify(void) */ static void make_dirty(uint8_t device) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t status; size_t len = 512; uintptr_t guest_buf; void* buf; + dev = get_pci_device(&bmdma_bar, &ide_bar); + guest_buf = guest_alloc(guest_malloc, len); buf = g_malloc(len); g_assert(guest_buf); @@ -527,19 +559,23 @@ static void make_dirty(uint8_t device) status = send_dma_request(CMD_WRITE_DMA, 1, 1, prdt, ARRAY_SIZE(prdt), NULL); g_assert_cmphex(status, ==, BM_STS_INTR); - assert_bit_clear(inb(IDE_BASE + reg_status), DF | ERR); + assert_bit_clear(qpci_io_readb(dev, ide_bar, reg_status), DF | ERR); g_free(buf); } static void test_flush(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; ide_test_start( "-drive file=blkdebug::%s,if=ide,cache=writeback,format=raw", tmp_path); + dev = get_pci_device(&bmdma_bar, &ide_bar); + qtest_irq_intercept_in(global_qtest, "ioapic"); /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ @@ -549,11 +585,11 @@ static void test_flush(void) g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\"")); /* FLUSH CACHE command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); /* Check status while request is in flight*/ - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, BSY | DRDY); assert_bit_clear(data, DF | ERR | DRQ); @@ -561,11 +597,11 @@ static void test_flush(void) g_free(hmp("qemu-io ide0-hd0 \"resume A\"")); /* Check registers */ - data = inb(IDE_BASE + reg_device); + data = qpci_io_readb(dev, ide_bar, reg_device); g_assert_cmpint(data & DEV, ==, 0); do { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); } while (data & BSY); assert_bit_set(data, DRDY); @@ -576,6 +612,8 @@ static void test_flush(void) static void test_retry_flush(const char *machine) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; const char *s; @@ -587,17 +625,19 @@ static void test_retry_flush(const char *machine) "rerror=stop,werror=stop", debug_path, tmp_path); + dev = get_pci_device(&bmdma_bar, &ide_bar); + qtest_irq_intercept_in(global_qtest, "ioapic"); /* Dirty media so that CMD_FLUSH_CACHE will actually go to disk */ make_dirty(0); /* FLUSH CACHE command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); /* Check status while request is in flight*/ - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); assert_bit_set(data, BSY | DRDY); assert_bit_clear(data, DF | ERR | DRQ); @@ -608,11 +648,11 @@ static void test_retry_flush(const char *machine) qmp_discard_response(s); /* Check registers */ - data = inb(IDE_BASE + reg_device); + data = qpci_io_readb(dev, ide_bar, reg_device); g_assert_cmpint(data & DEV, ==, 0); do { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); } while (data & BSY); assert_bit_set(data, DRDY); @@ -623,11 +663,16 @@ static void test_retry_flush(const char *machine) static void test_flush_nodev(void) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; + ide_test_start(""); + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* FLUSH CACHE command on device 0*/ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_command, CMD_FLUSH_CACHE); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_FLUSH_CACHE); /* Just testing that qemu doesn't crash... */ @@ -654,7 +699,8 @@ typedef struct Read10CDB { uint16_t padding; } __attribute__((__packed__)) Read10CDB; -static void send_scsi_cdb_read10(uint64_t lba, int nblocks) +static void send_scsi_cdb_read10(QPCIDevice *dev, QPCIBar ide_bar, + uint64_t lba, int nblocks) { Read10CDB pkt = { .padding = 0 }; int i; @@ -670,7 +716,8 @@ static void send_scsi_cdb_read10(uint64_t lba, int nblocks) /* Send Packet */ for (i = 0; i < sizeof(Read10CDB)/2; i++) { - outw(IDE_BASE + reg_data, cpu_to_le16(((uint16_t *)&pkt)[i])); + qpci_io_writew(dev, ide_bar, reg_data, + le16_to_cpu(((uint16_t *)&pkt)[i])); } } @@ -683,13 +730,17 @@ static void nsleep(int64_t nsecs) static uint8_t ide_wait_clear(uint8_t flag) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; uint8_t data; time_t st; + dev = get_pci_device(&bmdma_bar, &ide_bar); + /* Wait with a 5 second timeout */ time(&st); while (true) { - data = inb(IDE_BASE + reg_status); + data = qpci_io_readb(dev, ide_bar, reg_status); if (!(data & flag)) { return data; } @@ -723,6 +774,8 @@ static void ide_wait_intr(int irq) static void cdrom_pio_impl(int nblocks) { + QPCIDevice *dev; + QPCIBar bmdma_bar, ide_bar; FILE *fh; int patt_blocks = MAX(16, nblocks); size_t patt_len = ATAPI_BLOCK_SIZE * patt_blocks; @@ -741,13 +794,14 @@ static void cdrom_pio_impl(int nblocks) ide_test_start("-drive if=none,file=%s,media=cdrom,format=raw,id=sr0,index=0 " "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); + dev = get_pci_device(&bmdma_bar, &ide_bar); qtest_irq_intercept_in(global_qtest, "ioapic"); /* PACKET command on device 0 */ - outb(IDE_BASE + reg_device, 0); - outb(IDE_BASE + reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); - outb(IDE_BASE + reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); - outb(IDE_BASE + reg_command, CMD_PACKET); + qpci_io_writeb(dev, ide_bar, reg_device, 0); + qpci_io_writeb(dev, ide_bar, reg_lba_middle, BYTE_COUNT_LIMIT & 0xFF); + qpci_io_writeb(dev, ide_bar, reg_lba_high, (BYTE_COUNT_LIMIT >> 8 & 0xFF)); + qpci_io_writeb(dev, ide_bar, reg_command, CMD_PACKET); /* HP0: Check_Status_A State */ nsleep(400); data = ide_wait_clear(BSY); @@ -756,7 +810,7 @@ static void cdrom_pio_impl(int nblocks) assert_bit_clear(data, ERR | DF | BSY); /* SCSI CDB (READ10) -- read n*2048 bytes from block 0 */ - send_scsi_cdb_read10(0, nblocks); + send_scsi_cdb_read10(dev, ide_bar, 0, nblocks); /* Read data back: occurs in bursts of 'BYTE_COUNT_LIMIT' bytes. * If BYTE_COUNT_LIMIT is odd, we transfer BYTE_COUNT_LIMIT - 1 bytes. @@ -780,7 +834,8 @@ static void cdrom_pio_impl(int nblocks) /* HP4: Transfer_Data */ for (j = 0; j < MIN((limit / 2), rem); j++) { - rx[offset + j] = le16_to_cpu(inw(IDE_BASE + reg_data)); + rx[offset + j] = cpu_to_le16(qpci_io_readw(dev, ide_bar, + reg_data)); } } diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index f36bfe7d0a..04a5c5dc7d 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -41,7 +41,7 @@ static QPCIDevice *get_device(QPCIBus *pcibus) typedef struct _IVState { QTestState *qtest; - void *reg_base, *mem_base; + QPCIBar reg_bar, mem_bar; QPCIBus *pcibus; QPCIDevice *dev; } IVState; @@ -75,7 +75,7 @@ static inline unsigned in_reg(IVState *s, enum Reg reg) unsigned res; global_qtest = s->qtest; - res = qpci_io_readl(s->dev, s->reg_base + reg); + res = qpci_io_readl(s->dev, s->reg_bar, reg); g_test_message("*%s -> %x\n", name, res); global_qtest = qtest; @@ -89,7 +89,26 @@ static inline void out_reg(IVState *s, enum Reg reg, unsigned v) global_qtest = s->qtest; g_test_message("%x -> *%s\n", v, name); - qpci_io_writel(s->dev, s->reg_base + reg, v); + qpci_io_writel(s->dev, s->reg_bar, reg, v); + global_qtest = qtest; +} + +static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) +{ + QTestState *qtest = global_qtest; + + global_qtest = s->qtest; + qpci_memread(s->dev, s->mem_bar, off, buf, len); + global_qtest = qtest; +} + +static inline void write_mem(IVState *s, uint64_t off, + const void *buf, size_t len) +{ + QTestState *qtest = global_qtest; + + global_qtest = s->qtest; + qpci_memwrite(s->dev, s->mem_bar, off, buf, len); global_qtest = qtest; } @@ -108,16 +127,14 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) s->pcibus = qpci_init_pc(NULL); s->dev = get_device(s->pcibus); - s->reg_base = qpci_iomap(s->dev, 0, &barsize); - g_assert_nonnull(s->reg_base); + s->reg_bar = qpci_iomap(s->dev, 0, &barsize); g_assert_cmpuint(barsize, ==, 256); if (msix) { qpci_msix_enable(s->dev); } - s->mem_base = qpci_iomap(s->dev, 2, &barsize); - g_assert_nonnull(s->mem_base); + s->mem_bar = qpci_iomap(s->dev, 2, &barsize); g_assert_cmpuint(barsize, ==, TMPSHMSIZE); qpci_device_enable(s->dev); @@ -169,7 +186,7 @@ static void test_ivshmem_single(void) for (i = 0; i < G_N_ELEMENTS(data); i++) { data[i] = i; } - qtest_memwrite(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + write_mem(s, 0, data, sizeof(data)); /* verify write */ for (i = 0; i < G_N_ELEMENTS(data); i++) { @@ -178,7 +195,7 @@ static void test_ivshmem_single(void) /* read it back and verify read */ memset(data, 0, sizeof(data)); - qtest_memread(s->qtest, (uintptr_t)s->mem_base, data, sizeof(data)); + read_mem(s, 0, data, sizeof(data)); for (i = 0; i < G_N_ELEMENTS(data); i++) { g_assert_cmpuint(data[i], ==, i); } @@ -201,29 +218,29 @@ static void test_ivshmem_pair(void) /* host write, guest 1 & 2 read */ memset(tmpshmem, 0x42, TMPSHMSIZE); - qtest_memread(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE); + read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x42); } - qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x42); } /* guest 1 write, guest 2 read */ memset(data, 0x43, TMPSHMSIZE); - qtest_memwrite(s1->qtest, (uintptr_t)s1->mem_base, data, TMPSHMSIZE); + write_mem(s1, 0, data, TMPSHMSIZE); memset(data, 0, TMPSHMSIZE); - qtest_memread(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + read_mem(s2, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x43); } /* guest 2 write, guest 1 read */ memset(data, 0x44, TMPSHMSIZE); - qtest_memwrite(s2->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + write_mem(s2, 0, data, TMPSHMSIZE); memset(data, 0, TMPSHMSIZE); - qtest_memread(s1->qtest, (uintptr_t)s2->mem_base, data, TMPSHMSIZE); + read_mem(s1, 0, data, TMPSHMSIZE); for (i = 0; i < TMPSHMSIZE; i++) { g_assert_cmpuint(data[i], ==, 0x44); } diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 716ab7939e..5180d65279 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -210,8 +210,7 @@ void ahci_pci_enable(AHCIQState *ahci) void start_ahci_device(AHCIQState *ahci) { /* Map AHCI's ABAR (BAR5) */ - ahci->hba_base = qpci_iomap(ahci->dev, 5, &ahci->barsize); - g_assert(ahci->hba_base); + ahci->hba_bar = qpci_iomap(ahci->dev, 5, &ahci->barsize); /* turns on pci.cmd.iose, pci.cmd.mse and pci.cmd.bme */ qpci_device_enable(ahci->dev); @@ -351,6 +350,7 @@ void ahci_hba_enable(AHCIQState *ahci) reg = ahci_rreg(ahci, AHCI_GHC); ASSERT_BIT_SET(reg, AHCI_GHC_IE); + ahci->enabled = true; /* TODO: The device should now be idling and waiting for commands. * In the future, a small test-case to inspect the Register D2H FIS * and clear the initial interrupts might be good. */ diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index c69fb5ae90..caaafe3fdf 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -321,12 +321,13 @@ typedef struct AHCIPortQState { typedef struct AHCIQState { QOSState *parent; QPCIDevice *dev; - void *hba_base; + QPCIBar hba_bar; uint64_t barsize; uint32_t fingerprint; uint32_t cap; uint32_t cap2; AHCIPortQState port[32]; + bool enabled; } AHCIQState; /** @@ -488,12 +489,12 @@ typedef struct AHCIOpts { static inline uint32_t ahci_mread(AHCIQState *ahci, size_t offset) { - return qpci_io_readl(ahci->dev, ahci->hba_base + offset); + return qpci_io_readl(ahci->dev, ahci->hba_bar, offset); } static inline void ahci_mwrite(AHCIQState *ahci, size_t offset, uint32_t value) { - qpci_io_writel(ahci->dev, ahci->hba_base + offset, value); + qpci_io_writel(ahci->dev, ahci->hba_bar, offset, value); } static inline uint32_t ahci_rreg(AHCIQState *ahci, uint32_t reg_num) diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 7abb48254e..6226546c28 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -10,6 +10,8 @@ /** * Launch QEMU with the given command line, * and then set up interrupts and our guest malloc interface. + * Never returns NULL: + * Terminates the application in case an error is encountered. */ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) { diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 9600ed6e41..ded1c54c06 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -17,7 +17,6 @@ #include "hw/pci/pci_regs.h" #include "qemu-common.h" -#include "qemu/host-utils.h" #define ACPI_PCIHP_ADDR 0xae00 @@ -26,89 +25,58 @@ typedef struct QPCIBusPC { QPCIBus bus; - - uint32_t pci_hole_start; - uint32_t pci_hole_size; - uint32_t pci_hole_alloc; - - uint16_t pci_iohole_start; - uint16_t pci_iohole_size; - uint16_t pci_iohole_alloc; } QPCIBusPC; -static uint8_t qpci_pc_io_readb(QPCIBus *bus, void *addr) +static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; - uint8_t value; - - if (port < 0x10000) { - value = inb(port); - } else { - value = readb(port); - } - - return value; + return inb(addr); } -static uint16_t qpci_pc_io_readw(QPCIBus *bus, void *addr) +static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { - uintptr_t port = (uintptr_t)addr; - uint16_t value; - - if (port < 0x10000) { - value = inw(port); - } else { - value = readw(port); - } - - return value; + outb(addr, val); } -static uint32_t qpci_pc_io_readl(QPCIBus *bus, void *addr) +static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; - uint32_t value; - - if (port < 0x10000) { - value = inl(port); - } else { - value = readl(port); - } + return inw(addr); +} - return value; +static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) +{ + outw(addr, val); } -static void qpci_pc_io_writeb(QPCIBus *bus, void *addr, uint8_t value) +static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; + return inl(addr); +} - if (port < 0x10000) { - outb(port, value); - } else { - writeb(port, value); - } +static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) +{ + outl(addr, val); } -static void qpci_pc_io_writew(QPCIBus *bus, void *addr, uint16_t value) +static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr) { - uintptr_t port = (uintptr_t)addr; + return (uint64_t)inl(addr) + ((uint64_t)inl(addr + 4) << 32); +} - if (port < 0x10000) { - outw(port, value); - } else { - writew(port, value); - } +static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) +{ + outl(addr, val & 0xffffffff); + outl(addr + 4, val >> 32); } -static void qpci_pc_io_writel(QPCIBus *bus, void *addr, uint32_t value) +static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { - uintptr_t port = (uintptr_t)addr; + memread(addr, buf, len); +} - if (port < 0x10000) { - outl(port, value); - } else { - writel(port, value); - } +static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + memwrite(addr, buf, len); } static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) @@ -147,84 +115,24 @@ static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint3 outl(0xcfc, value); } -static void *qpci_pc_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr) -{ - QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); - static const int bar_reg_map[] = { - PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, - }; - int bar_reg; - uint32_t addr; - uint64_t size; - uint32_t io_type; - - g_assert(barno >= 0 && barno <= 5); - bar_reg = bar_reg_map[barno]; - - qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); - addr = qpci_config_readl(dev, bar_reg); - - io_type = addr & PCI_BASE_ADDRESS_SPACE; - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - addr &= PCI_BASE_ADDRESS_IO_MASK; - } else { - addr &= PCI_BASE_ADDRESS_MEM_MASK; - } - - size = (1ULL << ctzl(addr)); - if (size == 0) { - return NULL; - } - if (sizeptr) { - *sizeptr = size; - } - - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - uint16_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size - <= s->pci_iohole_size); - s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); - loc = s->pci_iohole_start + s->pci_iohole_alloc; - s->pci_iohole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); - - return (void *)(intptr_t)loc; - } else { - uint64_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size - <= s->pci_hole_size); - s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); - loc = s->pci_hole_start + s->pci_hole_alloc; - s->pci_hole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc); - - return (void *)(intptr_t)loc; - } -} - -static void qpci_pc_iounmap(QPCIBus *bus, void *data) -{ - /* FIXME */ -} - QPCIBus *qpci_init_pc(QGuestAllocator *alloc) { QPCIBusPC *ret; ret = g_malloc(sizeof(*ret)); - ret->bus.io_readb = qpci_pc_io_readb; - ret->bus.io_readw = qpci_pc_io_readw; - ret->bus.io_readl = qpci_pc_io_readl; + ret->bus.pio_readb = qpci_pc_pio_readb; + ret->bus.pio_readw = qpci_pc_pio_readw; + ret->bus.pio_readl = qpci_pc_pio_readl; + ret->bus.pio_readq = qpci_pc_pio_readq; - ret->bus.io_writeb = qpci_pc_io_writeb; - ret->bus.io_writew = qpci_pc_io_writew; - ret->bus.io_writel = qpci_pc_io_writel; + ret->bus.pio_writeb = qpci_pc_pio_writeb; + ret->bus.pio_writew = qpci_pc_pio_writew; + ret->bus.pio_writel = qpci_pc_pio_writel; + ret->bus.pio_writeq = qpci_pc_pio_writeq; + + ret->bus.memread = qpci_pc_memread; + ret->bus.memwrite = qpci_pc_memwrite; ret->bus.config_readb = qpci_pc_config_readb; ret->bus.config_readw = qpci_pc_config_readw; @@ -234,16 +142,9 @@ QPCIBus *qpci_init_pc(QGuestAllocator *alloc) ret->bus.config_writew = qpci_pc_config_writew; ret->bus.config_writel = qpci_pc_config_writel; - ret->bus.iomap = qpci_pc_iomap; - ret->bus.iounmap = qpci_pc_iounmap; - - ret->pci_hole_start = 0xE0000000; - ret->pci_hole_size = 0x20000000; - ret->pci_hole_alloc = 0; - - ret->pci_iohole_start = 0xc000; - ret->pci_iohole_size = 0x4000; - ret->pci_iohole_alloc = 0; + ret->bus.pio_alloc_ptr = 0xc000; + ret->bus.mmio_alloc_ptr = 0xE0000000; + ret->bus.mmio_limit = 0x100000000ULL; return &ret->bus; } diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index 2eaaf9159a..1e5d015bd4 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -34,14 +34,6 @@ typedef struct QPCIBusSPAPR { uint64_t mmio32_cpu_base; QPCIWindow mmio32; - - uint64_t pci_hole_start; - uint64_t pci_hole_size; - uint64_t pci_hole_alloc; - - uint32_t pci_iohole_start; - uint32_t pci_iohole_size; - uint32_t pci_iohole_alloc; } QPCIBusSPAPR; /* @@ -50,78 +42,66 @@ typedef struct QPCIBusSPAPR { * so PCI accessors need to swap data endianness */ -static uint8_t qpci_spapr_io_readb(QPCIBus *bus, void *addr) +static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return readb(s->pio_cpu_base + addr); +} + +static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + writeb(s->pio_cpu_base + addr, val); +} + +static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + return bswap16(readw(s->pio_cpu_base + addr)); +} + +static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - uint8_t v; - if (port < s->pio.size) { - v = readb(s->pio_cpu_base + port); - } else { - v = readb(s->mmio32_cpu_base + port); - } - return v; + writew(s->pio_cpu_base + addr, bswap16(val)); } -static uint16_t qpci_spapr_io_readw(QPCIBus *bus, void *addr) +static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - uint16_t v; - if (port < s->pio.size) { - v = readw(s->pio_cpu_base + port); - } else { - v = readw(s->mmio32_cpu_base + port); - } - return bswap16(v); + return bswap32(readl(s->pio_cpu_base + addr)); } -static uint32_t qpci_spapr_io_readl(QPCIBus *bus, void *addr) +static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - uint32_t v; - if (port < s->pio.size) { - v = readl(s->pio_cpu_base + port); - } else { - v = readl(s->mmio32_cpu_base + port); - } - return bswap32(v); + writel(s->pio_cpu_base + addr, bswap32(val)); } -static void qpci_spapr_io_writeb(QPCIBus *bus, void *addr, uint8_t value) +static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - if (port < s->pio.size) { - writeb(s->pio_cpu_base + port, value); - } else { - writeb(s->mmio32_cpu_base + port, value); - } + return bswap64(readq(s->pio_cpu_base + addr)); } -static void qpci_spapr_io_writew(QPCIBus *bus, void *addr, uint16_t value) +static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - value = bswap16(value); - if (port < s->pio.size) { - writew(s->pio_cpu_base + port, value); - } else { - writew(s->mmio32_cpu_base + port, value); - } + writeq(s->pio_cpu_base + addr, bswap64(val)); } -static void qpci_spapr_io_writel(QPCIBus *bus, void *addr, uint32_t value) +static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr, + void *buf, size_t len) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - uint64_t port = (uintptr_t)addr; - value = bswap32(value); - if (port < s->pio.size) { - writel(s->pio_cpu_base + port, value); - } else { - writel(s->mmio32_cpu_base + port, value); - } + memread(s->mmio32_cpu_base + addr, buf, len); +} + +static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr, + const void *buf, size_t len) +{ + QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + memwrite(s->mmio32_cpu_base + addr, buf, len); } static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset) @@ -169,72 +149,6 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, qrtas_ibm_write_pci_config(s->alloc, s->buid, config_addr, 4, value); } -static void *qpci_spapr_iomap(QPCIBus *bus, QPCIDevice *dev, int barno, - uint64_t *sizeptr) -{ - QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - static const int bar_reg_map[] = { - PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, - PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, - }; - int bar_reg; - uint32_t addr; - uint64_t size; - uint32_t io_type; - - g_assert(barno >= 0 && barno <= 5); - bar_reg = bar_reg_map[barno]; - - qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); - addr = qpci_config_readl(dev, bar_reg); - - io_type = addr & PCI_BASE_ADDRESS_SPACE; - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - addr &= PCI_BASE_ADDRESS_IO_MASK; - } else { - addr &= PCI_BASE_ADDRESS_MEM_MASK; - } - - size = (1ULL << ctzl(addr)); - if (size == 0) { - return NULL; - } - if (sizeptr) { - *sizeptr = size; - } - - if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { - uint16_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_iohole_alloc, size) + size - <= s->pci_iohole_size); - s->pci_iohole_alloc = QEMU_ALIGN_UP(s->pci_iohole_alloc, size); - loc = s->pci_iohole_start + s->pci_iohole_alloc; - s->pci_iohole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); - - return (void *)(unsigned long)loc; - } else { - uint64_t loc; - - g_assert(QEMU_ALIGN_UP(s->pci_hole_alloc, size) + size - <= s->pci_hole_size); - s->pci_hole_alloc = QEMU_ALIGN_UP(s->pci_hole_alloc, size); - loc = s->pci_hole_start + s->pci_hole_alloc; - s->pci_hole_alloc += size; - - qpci_config_writel(dev, bar_reg, loc); - - return (void *)(unsigned long)loc; - } -} - -static void qpci_spapr_iounmap(QPCIBus *bus, void *data) -{ - /* FIXME */ -} - #define SPAPR_PCI_BASE (1ULL << 45) #define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ @@ -248,13 +162,18 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->alloc = alloc; - ret->bus.io_readb = qpci_spapr_io_readb; - ret->bus.io_readw = qpci_spapr_io_readw; - ret->bus.io_readl = qpci_spapr_io_readl; + ret->bus.pio_readb = qpci_spapr_pio_readb; + ret->bus.pio_readw = qpci_spapr_pio_readw; + ret->bus.pio_readl = qpci_spapr_pio_readl; + ret->bus.pio_readq = qpci_spapr_pio_readq; - ret->bus.io_writeb = qpci_spapr_io_writeb; - ret->bus.io_writew = qpci_spapr_io_writew; - ret->bus.io_writel = qpci_spapr_io_writel; + ret->bus.pio_writeb = qpci_spapr_pio_writeb; + ret->bus.pio_writew = qpci_spapr_pio_writew; + ret->bus.pio_writel = qpci_spapr_pio_writel; + ret->bus.pio_writeq = qpci_spapr_pio_writeq; + + ret->bus.memread = qpci_spapr_memread; + ret->bus.memwrite = qpci_spapr_memwrite; ret->bus.config_readb = qpci_spapr_config_readb; ret->bus.config_readw = qpci_spapr_config_readw; @@ -264,9 +183,6 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->bus.config_writew = qpci_spapr_config_writew; ret->bus.config_writel = qpci_spapr_config_writel; - ret->bus.iomap = qpci_spapr_iomap; - ret->bus.iounmap = qpci_spapr_iounmap; - /* FIXME: We assume the default location of the PHB for now. * Ideally we'd parse the device tree deposited in the guest to * get the window locations */ @@ -281,15 +197,9 @@ QPCIBus *qpci_init_spapr(QGuestAllocator *alloc) ret->mmio32.pci_base = 0x80000000; /* 2 GiB */ ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; - ret->pci_hole_start = 0xC0000000; - ret->pci_hole_size = - ret->mmio32.pci_base + ret->mmio32.size - ret->pci_hole_start; - ret->pci_hole_alloc = 0; - - ret->pci_iohole_start = 0xc000; - ret->pci_iohole_size = - ret->pio.pci_base + ret->pio.size - ret->pci_iohole_start; - ret->pci_iohole_alloc = 0; + ret->bus.pio_alloc_ptr = 0xc000; + ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base; + ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size; return &ret->bus; } diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index c3f3382b7c..2dcdeade2a 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -14,6 +14,7 @@ #include "libqos/pci.h" #include "hw/pci/pci_regs.h" +#include "qemu/host-utils.h" void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), @@ -103,7 +104,6 @@ void qpci_msix_enable(QPCIDevice *dev) uint32_t table; uint8_t bir_table; uint8_t bir_pba; - void *offset; addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); g_assert_cmphex(addr, !=, 0); @@ -113,18 +113,16 @@ void qpci_msix_enable(QPCIDevice *dev) table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE); bir_table = table & PCI_MSIX_FLAGS_BIRMASK; - offset = qpci_iomap(dev, bir_table, NULL); - dev->msix_table = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL); + dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK; table = qpci_config_readl(dev, addr + PCI_MSIX_PBA); bir_pba = table & PCI_MSIX_FLAGS_BIRMASK; if (bir_pba != bir_table) { - offset = qpci_iomap(dev, bir_pba, NULL); + dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL); } - dev->msix_pba = offset + (table & ~PCI_MSIX_FLAGS_BIRMASK); + dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK; - g_assert(dev->msix_table != NULL); - g_assert(dev->msix_pba != NULL); dev->msix_enabled = true; } @@ -140,22 +138,23 @@ void qpci_msix_disable(QPCIDevice *dev) qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val & ~PCI_MSIX_FLAGS_ENABLE); - qpci_iounmap(dev, dev->msix_table); - qpci_iounmap(dev, dev->msix_pba); + qpci_iounmap(dev, dev->msix_table_bar); + qpci_iounmap(dev, dev->msix_pba_bar); dev->msix_enabled = 0; - dev->msix_table = NULL; - dev->msix_pba = NULL; + dev->msix_table_off = 0; + dev->msix_pba_off = 0; } bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) { uint32_t pba_entry; uint8_t bit_n = entry % 32; - void *addr = dev->msix_pba + (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; + uint64_t off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4; g_assert(dev->msix_enabled); - pba_entry = qpci_io_readl(dev, addr); - qpci_io_writel(dev, addr, pba_entry & ~(1 << bit_n)); + pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); + qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, + pba_entry & ~(1 << bit_n)); return (pba_entry & (1 << bit_n)) != 0; } @@ -163,7 +162,7 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) { uint8_t addr; uint16_t val; - void *vector_addr = dev->msix_table + (entry * PCI_MSIX_ENTRY_SIZE); + uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE; g_assert(dev->msix_enabled); addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX); @@ -173,8 +172,9 @@ bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry) if (val & PCI_MSIX_FLAGS_MASKALL) { return true; } else { - return (qpci_io_readl(dev, vector_addr + PCI_MSIX_ENTRY_VECTOR_CTRL) - & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; + return (qpci_io_readl(dev, dev->msix_table_bar, + vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL) + & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0; } } @@ -221,46 +221,174 @@ void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value) dev->bus->config_writel(dev->bus, dev->devfn, offset, value); } +uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readb(dev->bus, token.addr + off); + } else { + uint8_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return val; + } +} + +uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readw(dev->bus, token.addr + off); + } else { + uint16_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le16_to_cpu(val); + } +} + +uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off) +{ + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readl(dev->bus, token.addr + off); + } else { + uint32_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le32_to_cpu(val); + } +} -uint8_t qpci_io_readb(QPCIDevice *dev, void *data) +uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off) { - return dev->bus->io_readb(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + return dev->bus->pio_readq(dev->bus, token.addr + off); + } else { + uint64_t val; + dev->bus->memread(dev->bus, token.addr + off, &val, sizeof(val)); + return le64_to_cpu(val); + } } -uint16_t qpci_io_readw(QPCIDevice *dev, void *data) +void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint8_t value) { - return dev->bus->io_readw(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writeb(dev->bus, token.addr + off, value); + } else { + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } +} + +void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint16_t value) +{ + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writew(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le16(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } -uint32_t qpci_io_readl(QPCIDevice *dev, void *data) +void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint32_t value) { - return dev->bus->io_readl(dev->bus, data); + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writel(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le32(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } } +void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint64_t value) +{ + if (token.addr < QPCI_PIO_LIMIT) { + dev->bus->pio_writeq(dev->bus, token.addr + off, value); + } else { + value = cpu_to_le64(value); + dev->bus->memwrite(dev->bus, token.addr + off, &value, sizeof(value)); + } +} -void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value) +void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off, + void *buf, size_t len) { - dev->bus->io_writeb(dev->bus, data, value); + g_assert(token.addr >= QPCI_PIO_LIMIT); + dev->bus->memread(dev->bus, token.addr + off, buf, len); } -void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value) +void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off, + const void *buf, size_t len) { - dev->bus->io_writew(dev->bus, data, value); + g_assert(token.addr >= QPCI_PIO_LIMIT); + dev->bus->memwrite(dev->bus, token.addr + off, buf, len); } -void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value) +QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) { - dev->bus->io_writel(dev->bus, data, value); + QPCIBus *bus = dev->bus; + static const int bar_reg_map[] = { + PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2, + PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5, + }; + QPCIBar bar; + int bar_reg; + uint32_t addr, size; + uint32_t io_type; + uint64_t loc; + + g_assert(barno >= 0 && barno <= 5); + bar_reg = bar_reg_map[barno]; + + qpci_config_writel(dev, bar_reg, 0xFFFFFFFF); + addr = qpci_config_readl(dev, bar_reg); + + io_type = addr & PCI_BASE_ADDRESS_SPACE; + if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { + addr &= PCI_BASE_ADDRESS_IO_MASK; + } else { + addr &= PCI_BASE_ADDRESS_MEM_MASK; + } + + g_assert(addr); /* Must have *some* size bits */ + + size = 1U << ctz32(addr); + if (sizeptr) { + *sizeptr = size; + } + + if (io_type == PCI_BASE_ADDRESS_SPACE_IO) { + loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size); + + g_assert(loc >= bus->pio_alloc_ptr); + g_assert(loc + size <= QPCI_PIO_LIMIT); /* Keep PIO below 64kiB */ + + bus->pio_alloc_ptr = loc + size; + + qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO); + } else { + loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size); + + /* Check for space */ + g_assert(loc >= bus->mmio_alloc_ptr); + g_assert(loc + size <= bus->mmio_limit); + + bus->mmio_alloc_ptr = loc + size; + + qpci_config_writel(dev, bar_reg, loc); + } + + bar.addr = loc; + return bar; } -void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr) +void qpci_iounmap(QPCIDevice *dev, QPCIBar bar) { - return dev->bus->iomap(dev->bus, dev, barno, sizeptr); + /* FIXME */ } -void qpci_iounmap(QPCIDevice *dev, void *data) +QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) { - dev->bus->iounmap(dev->bus, data); + QPCIBar bar = { .addr = addr }; + return bar; } void qpci_plug_device_test(const char *driver, const char *id, diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index c06add8dbf..ed480614ff 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -15,20 +15,27 @@ #include "libqtest.h" +#define QPCI_PIO_LIMIT 0x10000 + #define QPCI_DEVFN(dev, fn) (((dev) << 3) | (fn)) typedef struct QPCIDevice QPCIDevice; typedef struct QPCIBus QPCIBus; +typedef struct QPCIBar QPCIBar; -struct QPCIBus -{ - uint8_t (*io_readb)(QPCIBus *bus, void *addr); - uint16_t (*io_readw)(QPCIBus *bus, void *addr); - uint32_t (*io_readl)(QPCIBus *bus, void *addr); +struct QPCIBus { + uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr); + uint16_t (*pio_readw)(QPCIBus *bus, uint32_t addr); + uint32_t (*pio_readl)(QPCIBus *bus, uint32_t addr); + uint64_t (*pio_readq)(QPCIBus *bus, uint32_t addr); - void (*io_writeb)(QPCIBus *bus, void *addr, uint8_t value); - void (*io_writew)(QPCIBus *bus, void *addr, uint16_t value); - void (*io_writel)(QPCIBus *bus, void *addr, uint32_t value); + void (*pio_writeb)(QPCIBus *bus, uint32_t addr, uint8_t value); + void (*pio_writew)(QPCIBus *bus, uint32_t addr, uint16_t value); + void (*pio_writel)(QPCIBus *bus, uint32_t addr, uint32_t value); + void (*pio_writeq)(QPCIBus *bus, uint32_t addr, uint64_t value); + + void (*memread)(QPCIBus *bus, uint32_t addr, void *buf, size_t len); + void (*memwrite)(QPCIBus *bus, uint32_t addr, const void *buf, size_t len); uint8_t (*config_readb)(QPCIBus *bus, int devfn, uint8_t offset); uint16_t (*config_readw)(QPCIBus *bus, int devfn, uint8_t offset); @@ -41,8 +48,12 @@ struct QPCIBus void (*config_writel)(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value); - void *(*iomap)(QPCIBus *bus, QPCIDevice *dev, int barno, uint64_t *sizeptr); - void (*iounmap)(QPCIBus *bus, void *data); + uint16_t pio_alloc_ptr; + uint64_t mmio_alloc_ptr, mmio_limit; +}; + +struct QPCIBar { + uint64_t addr; }; struct QPCIDevice @@ -50,8 +61,8 @@ struct QPCIDevice QPCIBus *bus; int devfn; bool msix_enabled; - void *msix_table; - void *msix_pba; + QPCIBar msix_table_bar, msix_pba_bar; + uint64_t msix_table_off, msix_pba_off; }; void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, @@ -75,16 +86,27 @@ void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value); void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value); void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value); -uint8_t qpci_io_readb(QPCIDevice *dev, void *data); -uint16_t qpci_io_readw(QPCIDevice *dev, void *data); -uint32_t qpci_io_readl(QPCIDevice *dev, void *data); - -void qpci_io_writeb(QPCIDevice *dev, void *data, uint8_t value); -void qpci_io_writew(QPCIDevice *dev, void *data, uint16_t value); -void qpci_io_writel(QPCIDevice *dev, void *data, uint32_t value); - -void *qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); -void qpci_iounmap(QPCIDevice *dev, void *data); +uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off); +uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off); + +void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint8_t value); +void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint16_t value); +void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint32_t value); +void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off, + uint64_t value); + +void qpci_memread(QPCIDevice *bus, QPCIBar token, uint64_t off, + void *buf, size_t len); +void qpci_memwrite(QPCIDevice *bus, QPCIBar token, uint64_t off, + const void *buf, size_t len); +QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); +void qpci_iounmap(QPCIDevice *dev, QPCIBar addr); +QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr); void qpci_plug_device_test(const char *driver, const char *id, uint8_t slot, const char *opts); diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c index f794d92da5..72d7a961fe 100644 --- a/tests/libqos/usb.c +++ b/tests/libqos/usb.c @@ -21,14 +21,12 @@ void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, uint32_t devfn, int bar) hc->dev = qpci_device_find(pcibus, devfn); g_assert(hc->dev != NULL); qpci_device_enable(hc->dev); - hc->base = qpci_iomap(hc->dev, bar, NULL); - g_assert(hc->base != NULL); + hc->bar = qpci_iomap(hc->dev, bar, NULL); } void uhci_port_test(struct qhc *hc, int port, uint16_t expect) { - void *addr = hc->base + 0x10 + 2 * port; - uint16_t value = qpci_io_readw(hc->dev, addr); + uint16_t value = qpci_io_readw(hc->dev, hc->bar, 0x10 + 2 * port); uint16_t mask = ~(UHCI_PORT_WRITE_CLEAR | UHCI_PORT_RSVD1); g_assert((value & mask) == (expect & mask)); diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h index 8fe56872b7..423dcfd82f 100644 --- a/tests/libqos/usb.h +++ b/tests/libqos/usb.h @@ -5,7 +5,7 @@ struct qhc { QPCIDevice *dev; - void *base; + QPCIBar bar; }; void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index 0cab38f296..7aa8383338 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -15,28 +15,28 @@ #include "libqos/malloc-generic.h" #include "standard-headers/linux/virtio_ring.h" -static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t addr) +static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readb(dev->addr + addr); + return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t addr) +static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readw(dev->addr + addr); + return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t addr) +static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readl(dev->addr + addr); + return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } -static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t addr) +static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off) { QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readq(dev->addr + addr); + return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) @@ -199,6 +199,7 @@ QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) dev->addr = addr; dev->page_size = page_size; dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + dev->vdev.bus = &qvirtio_mmio; writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 6e005c1835..d4bf841f23 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -62,73 +62,87 @@ static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) *vpcidev = (QVirtioPCIDevice *)d; } -static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t addr) +#define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) + +static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, (void *)(uintptr_t)addr); + return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); } -static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t addr) +/* PCI is always read in little-endian order + * but virtio ( < 1.0) is in guest order + * so with a big-endian guest the order has been reversed, + * reverse it again + * virtio-1.0 is always little-endian, like PCI, but this + * case will be managed inside qvirtio_is_big_endian() + */ + +static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, (void *)(uintptr_t)addr); + uint16_t value; + + value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + value = bswap16(value); + } + return value; } -static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t addr) +static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, (void *)(uintptr_t)addr); + uint32_t value; + + value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + value = bswap32(value); + } + return value; } -static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t addr) +static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - int i; - uint64_t u64 = 0; + uint64_t val; - if (target_big_endian()) { - for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, - (void *)(uintptr_t)addr + i) << (7 - i) * 8; - } - } else { - for (i = 0; i < 8; ++i) { - u64 |= (uint64_t)qpci_io_readb(dev->pdev, - (void *)(uintptr_t)addr + i) << i * 8; - } + val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); + if (qvirtio_is_big_endian(d)) { + val = bswap64(val); } - return u64; + return val; } static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_HOST_FEATURES); + return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES, features); + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readl(dev->pdev, dev->addr + VIRTIO_PCI_GUEST_FEATURES); + return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS); + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_STATUS, status); + qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) @@ -152,7 +166,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) } } } else { - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 1; + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 1; } } @@ -176,26 +190,26 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) } } } else { - return qpci_io_readb(dev->pdev, dev->addr + VIRTIO_PCI_ISR) & 2; + return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_ISR) & 2; } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writeb(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_SEL, index); + qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - return qpci_io_readw(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NUM); + return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writel(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_PFN, pfn); + qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); } static QVirtQueue *qvirtio_pci_virtqueue_setup(QVirtioDevice *d, @@ -247,7 +261,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; - qpci_io_writew(dev->pdev, dev->addr + VIRTIO_PCI_QUEUE_NOTIFY, vq->index); + qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_pci = { @@ -286,20 +300,20 @@ QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) QVirtioPCIDevice *dev = NULL; qvirtio_pci_foreach(bus, device_type, qvirtio_pci_assign_device, &dev); + dev->vdev.bus = &qvirtio_pci; + return dev; } void qvirtio_pci_device_enable(QVirtioPCIDevice *d) { qpci_device_enable(d->pdev); - d->addr = qpci_iomap(d->pdev, 0, NULL); - g_assert(d->addr != NULL); + d->bar = qpci_iomap(d->pdev, 0, NULL); } void qvirtio_pci_device_disable(QVirtioPCIDevice *d) { - qpci_iounmap(d->pdev, d->addr); - d->addr = NULL; + qpci_iounmap(d->pdev, d->bar); } void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, @@ -307,29 +321,33 @@ void qvirtqueue_pci_msix_setup(QVirtioPCIDevice *d, QVirtQueuePCI *vqpci, { uint16_t vector; uint32_t control; - void *addr; + uint64_t off; g_assert(d->pdev->msix_enabled); - addr = d->pdev->msix_table + (entry * 16); + off = d->pdev->msix_table_off + (entry * 16); g_assert_cmpint(entry, >=, 0); g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); vqpci->msix_entry = entry; vqpci->msix_addr = guest_alloc(alloc, 4); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, - vqpci->msix_addr & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, - (vqpci->msix_addr >> 32) & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); - - control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, - control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_LOWER_ADDR, vqpci->msix_addr & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_UPPER_ADDR, + (vqpci->msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_DATA, vqpci->msix_data); + + control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); qvirtio_pci_queue_select(&d->vdev, vqpci->vq.index); - qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_QUEUE_VECTOR); + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_QUEUE_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } @@ -338,10 +356,10 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, { uint16_t vector; uint32_t control; - void *addr; + uint64_t off; g_assert(d->pdev->msix_enabled); - addr = d->pdev->msix_table + (entry * 16); + off = d->pdev->msix_table_off + (entry * 16); g_assert_cmpint(entry, >=, 0); g_assert_cmpint(entry, <, qpci_msix_table_size(d->pdev)); @@ -350,17 +368,21 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, d->config_msix_data = 0x12345678; d->config_msix_addr = guest_alloc(alloc, 4); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_LOWER_ADDR, - d->config_msix_addr & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_UPPER_ADDR, - (d->config_msix_addr >> 32) & ~0UL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_DATA, d->config_msix_data); - - control = qpci_io_readl(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL); - qpci_io_writel(d->pdev, addr + PCI_MSIX_ENTRY_VECTOR_CTRL, - control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); - - qpci_io_writew(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR, entry); - vector = qpci_io_readw(d->pdev, d->addr + VIRTIO_MSI_CONFIG_VECTOR); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_LOWER_ADDR, d->config_msix_addr & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_UPPER_ADDR, + (d->config_msix_addr >> 32) & ~0UL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_DATA, d->config_msix_data); + + control = qpci_io_readl(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL); + qpci_io_writel(d->pdev, d->pdev->msix_table_bar, + off + PCI_MSIX_ENTRY_VECTOR_CTRL, + control & ~PCI_MSIX_ENTRY_CTRL_MASKBIT); + + qpci_io_writew(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR, entry); + vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index efcac2d3de..38c54c63ea 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -16,7 +16,7 @@ typedef struct QVirtioPCIDevice { QVirtioDevice vdev; QPCIDevice *pdev; - void *addr; + QPCIBar bar; uint16_t config_msix_entry; uint64_t config_msix_addr; uint32_t config_msix_data; diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 105bccecaa..ec30cb99b2 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -13,45 +13,40 @@ #include "standard-headers/linux/virtio_config.h" #include "standard-headers/linux/virtio_ring.h" -uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) { - return bus->config_readb(d, addr); + return d->bus->config_readb(d, addr); } -uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr) { - return bus->config_readw(d, addr); + return d->bus->config_readw(d, addr); } -uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr) { - return bus->config_readl(d, addr); + return d->bus->config_readl(d, addr); } -uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr) +uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr) { - return bus->config_readq(d, addr); + return d->bus->config_readq(d, addr); } -uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d) +uint32_t qvirtio_get_features(QVirtioDevice *d) { - return bus->get_features(d); + return d->bus->get_features(d); } -void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, - uint32_t features) +void qvirtio_set_features(QVirtioDevice *d, uint32_t features) { - bus->set_features(d, features); + d->bus->set_features(d, features); } -QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index) +QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index) { - return bus->virtqueue_setup(d, alloc, index); + return d->bus->virtqueue_setup(d, alloc, index); } void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, @@ -60,40 +55,40 @@ void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, return bus->virtqueue_cleanup(vq, alloc); } -void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_reset(QVirtioDevice *d) { - bus->set_status(d, 0); - g_assert_cmphex(bus->get_status(d), ==, 0); + d->bus->set_status(d, 0); + g_assert_cmphex(d->bus->get_status(d), ==, 0); } -void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_acknowledge(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); - g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_ACKNOWLEDGE); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_driver(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); - g_assert_cmphex(bus->get_status(d), ==, + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d) +void qvirtio_set_driver_ok(QVirtioDevice *d) { - bus->set_status(d, bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); - g_assert_cmphex(bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | + d->bus->set_status(d, d->bus->get_status(d) | VIRTIO_CONFIG_S_DRIVER_OK); + g_assert_cmphex(d->bus->get_status(d), ==, VIRTIO_CONFIG_S_DRIVER_OK | VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_ACKNOWLEDGE); } -void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, +void qvirtio_wait_queue_isr(QVirtioDevice *d, QVirtQueue *vq, gint64 timeout_us) { gint64 start_time = g_get_monotonic_time(); for (;;) { clock_step(100); - if (bus->get_queue_isr_status(d, vq)) { + if (d->bus->get_queue_isr_status(d, vq)) { return; } g_assert(g_get_monotonic_time() - start_time <= timeout_us); @@ -105,8 +100,7 @@ void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, * The virtqueue interrupt must not be raised, making this useful for testing * event_index functionality. */ -uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, - QVirtioDevice *d, +uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d, QVirtQueue *vq, uint64_t addr, gint64 timeout_us) @@ -116,20 +110,19 @@ uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, while ((val = readb(addr)) == 0xff) { clock_step(100); - g_assert(!bus->get_queue_isr_status(d, vq)); + g_assert(!d->bus->get_queue_isr_status(d, vq)); g_assert(g_get_monotonic_time() - start_time <= timeout_us); } return val; } -void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, - gint64 timeout_us) +void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us) { gint64 start_time = g_get_monotonic_time(); for (;;) { clock_step(100); - if (bus->get_config_isr_status(d)) { + if (d->bus->get_config_isr_status(d)) { return; } g_assert(g_get_monotonic_time() - start_time <= timeout_us); @@ -253,8 +246,7 @@ uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect) return vq->free_head++; /* Return and increase, in this order */ } -void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, - uint32_t free_head) +void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head) { /* vq->avail->idx */ uint16_t idx = readw(vq->avail + 2); @@ -276,7 +268,7 @@ void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, /* < 1 because we add elements to avail queue one by one */ if ((flags & VRING_USED_F_NO_NOTIFY) == 0 && (!vq->event || (uint16_t)(idx-avail_event) < 1)) { - bus->virtqueue_kick(d, vq); + d->bus->virtqueue_kick(d, vq); } } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 0250842bf2..3397a080e9 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -15,7 +15,10 @@ #define QVIRTIO_F_BAD_FEATURE 0x40000000 +typedef struct QVirtioBus QVirtioBus; + typedef struct QVirtioDevice { + const QVirtioBus *bus; /* Device type */ uint16_t device_type; } QVirtioDevice; @@ -39,7 +42,7 @@ typedef struct QVRingIndirectDesc { uint16_t elem; } QVRingIndirectDesc; -typedef struct QVirtioBus { +struct QVirtioBus { uint8_t (*config_readb)(QVirtioDevice *d, uint64_t addr); uint16_t (*config_readw)(QVirtioDevice *d, uint64_t addr); uint32_t (*config_readl)(QVirtioDevice *d, uint64_t addr); @@ -84,7 +87,13 @@ typedef struct QVirtioBus { /* Notify changes in virtqueue */ void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq); -} QVirtioBus; +}; + +static inline bool qvirtio_is_big_endian(QVirtioDevice *d) +{ + /* FIXME: virtio 1.0 is always little-endian */ + return qtest_big_endian(global_qtest); +} static inline uint32_t qvring_size(uint32_t num, uint32_t align) { @@ -93,34 +102,27 @@ static inline uint32_t qvring_size(uint32_t num, uint32_t align) + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num; } -uint8_t qvirtio_config_readb(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint16_t qvirtio_config_readw(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint32_t qvirtio_config_readl(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint64_t qvirtio_config_readq(const QVirtioBus *bus, QVirtioDevice *d, - uint64_t addr); -uint32_t qvirtio_get_features(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_features(const QVirtioBus *bus, QVirtioDevice *d, - uint32_t features); - -void qvirtio_reset(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_acknowledge(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_driver(const QVirtioBus *bus, QVirtioDevice *d); -void qvirtio_set_driver_ok(const QVirtioBus *bus, QVirtioDevice *d); - -void qvirtio_wait_queue_isr(const QVirtioBus *bus, QVirtioDevice *d, +uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr); +uint16_t qvirtio_config_readw(QVirtioDevice *d, uint64_t addr); +uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr); +uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr); +uint32_t qvirtio_get_features(QVirtioDevice *d); +void qvirtio_set_features(QVirtioDevice *d, uint32_t features); + +void qvirtio_reset(QVirtioDevice *d); +void qvirtio_set_acknowledge(QVirtioDevice *d); +void qvirtio_set_driver(QVirtioDevice *d); +void qvirtio_set_driver_ok(QVirtioDevice *d); + +void qvirtio_wait_queue_isr(QVirtioDevice *d, QVirtQueue *vq, gint64 timeout_us); -uint8_t qvirtio_wait_status_byte_no_isr(const QVirtioBus *bus, - QVirtioDevice *d, +uint8_t qvirtio_wait_status_byte_no_isr(QVirtioDevice *d, QVirtQueue *vq, uint64_t addr, gint64 timeout_us); -void qvirtio_wait_config_isr(const QVirtioBus *bus, QVirtioDevice *d, - gint64 timeout_us); -QVirtQueue *qvirtqueue_setup(const QVirtioBus *bus, QVirtioDevice *d, - QGuestAllocator *alloc, uint16_t index); +void qvirtio_wait_config_isr(QVirtioDevice *d, gint64 timeout_us); +QVirtQueue *qvirtqueue_setup(QVirtioDevice *d, + QGuestAllocator *alloc, uint16_t index); void qvirtqueue_cleanup(const QVirtioBus *bus, QVirtQueue *vq, QGuestAllocator *alloc); @@ -132,8 +134,7 @@ void qvring_indirect_desc_add(QVRingIndirectDesc *indirect, uint64_t data, uint32_t qvirtqueue_add(QVirtQueue *vq, uint64_t data, uint32_t len, bool write, bool next); uint32_t qvirtqueue_add_indirect(QVirtQueue *vq, QVRingIndirectDesc *indirect); -void qvirtqueue_kick(const QVirtioBus *bus, QVirtioDevice *d, QVirtQueue *vq, - uint32_t free_head); +void qvirtqueue_kick(QVirtioDevice *d, QVirtQueue *vq, uint32_t free_head); void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); #endif diff --git a/tests/libqtest.h b/tests/libqtest.h index 4be1f77877..0224f06d65 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -881,16 +881,6 @@ static inline int64_t clock_set(int64_t val) return qtest_clock_set(global_qtest, val); } -/** - * target_big_endian: - * - * Returns: True if the architecture under test has a big endian configuration. - */ -static inline bool target_big_endian(void) -{ - return qtest_big_endian(global_qtest); -} - QDict *qmp_fd_receive(int fd); void qmp_fd_sendv(int fd, const char *fmt, va_list ap); void qmp_fd_send(int fd, const char *fmt, ...); diff --git a/tests/postcopy-test.c b/tests/postcopy-test.c index 41ed1a976f..d6613c5fa4 100644 --- a/tests/postcopy-test.c +++ b/tests/postcopy-test.c @@ -18,7 +18,7 @@ #include "qemu/sockets.h" #include "sysemu/char.h" #include "sysemu/sysemu.h" -#include "hw/nvram/openbios_firmware_abi.h" +#include "hw/nvram/chrp_nvram.h" #define MIN_NVRAM_SIZE 8192 /* from spapr_nvram.c */ @@ -137,15 +137,15 @@ static void init_bootfile_ppc(const char *bootpath) { FILE *bootfile; char buf[MIN_NVRAM_SIZE]; - struct OpenBIOS_nvpart_v1 *header = (struct OpenBIOS_nvpart_v1 *)buf; + ChrpNvramPartHdr *header = (ChrpNvramPartHdr *)buf; memset(buf, 0, MIN_NVRAM_SIZE); /* Create a "common" partition in nvram to store boot-command property */ - header->signature = OPENBIOS_PART_SYSTEM; + header->signature = CHRP_NVPART_SYSTEM; memcpy(header->name, "common", 6); - OpenBIOS_finish_partition(header, MIN_NVRAM_SIZE); + chrp_nvram_finish_partition(header, MIN_NVRAM_SIZE); /* FW_MAX_SIZE is 4MB, but slof.bin is only 900KB, * so let's modify memory between 1MB and 100MB diff --git a/tests/prom-env-test.c b/tests/prom-env-test.c index 7a628574c3..0ba6f48607 100644 --- a/tests/prom-env-test.c +++ b/tests/prom-env-test.c @@ -9,11 +9,12 @@ * This work is licensed under the terms of the GNU GPL, version 2 * or later. See the COPYING file in the top-level directory. * - * This test is used to check that some OpenBIOS machines can be started - * successfully in TCG mode. To do this, we first put some Forth code into - * the "boot-command" Open Firmware environment variable. This Forth code - * writes a well-known magic value to a known location in memory. Then we - * start the guest so that OpenBIOS can boot and finally run the Forth code. + * This test is used to check that some Open Firmware based machines (i.e. + * OpenBIOS or SLOF) can be started successfully in TCG mode. To do this, we + * first put some Forth code into the "boot-command" Open Firmware environment + * variable. This Forth code writes a well-known magic value to a known location + * in memory. Then we start the guest so that the firmware can boot and finally + * run the Forth code. * The testing code here then can finally check whether the value has been * successfully written into the guest memory. */ @@ -71,13 +72,16 @@ int main(int argc, char *argv[]) { const char *sparc_machines[] = { "SPARCbook", "Voyager", "SS-20", NULL }; const char *sparc64_machines[] = { "sun4u", "sun4v", NULL }; - const char *mac_machines[] = { "mac99", "g3beige", NULL }; + const char *ppc_machines[] = { "mac99", "g3beige", NULL }; + const char *ppc64_machines[] = { "mac99", "g3beige", "pseries", NULL }; const char *arch = qtest_get_arch(); g_test_init(&argc, &argv, NULL); - if (!strcmp(arch, "ppc") || !strcmp(arch, "ppc64")) { - add_tests(mac_machines); + if (!strcmp(arch, "ppc")) { + add_tests(ppc_machines); + } else if (!strcmp(arch, "ppc64")) { + add_tests(ppc64_machines); } else if (!strcmp(arch, "sparc")) { add_tests(sparc_machines); } else if (!strcmp(arch, "sparc64")) { diff --git a/tests/rtas-test.c b/tests/rtas-test.c index ba0867afbd..276c87ef84 100644 --- a/tests/rtas-test.c +++ b/tests/rtas-test.c @@ -14,7 +14,6 @@ static void test_rtas_get_time_of_day(void) time_t t1, t2; qs = qtest_spapr_boot("-machine pseries"); - g_assert(qs != NULL); t1 = time(NULL); ret = qrtas_get_time_of_day(qs->alloc, &tm, &ns); diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index c2f601a380..7de7dc45ae 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -22,7 +22,7 @@ static void nop(void) static QPCIBus *pcibus; static QPCIDevice *dev; -static void *dev_base; +static QPCIBar dev_bar; static void save_fn(QPCIDevice *dev, int devfn, void *data) { @@ -45,14 +45,14 @@ static QPCIDevice *get_device(void) #define PORT(name, len, val) \ static unsigned __attribute__((unused)) in_##name(void) \ { \ - unsigned res = qpci_io_read##len(dev, dev_base+(val)); \ + unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ g_test_message("*%s -> %x\n", #name, res); \ return res; \ } \ static void out_##name(unsigned v) \ { \ g_test_message("%x -> *%s\n", v, #name); \ - qpci_io_write##len(dev, dev_base+(val), v); \ + qpci_io_write##len(dev, dev_bar, (val), v); \ } PORT(Timer, l, 0x48) @@ -186,9 +186,7 @@ static void test_init(void) dev = get_device(); - dev_base = qpci_iomap(dev, 0, &barsize); - - g_assert(dev_base != NULL); + dev_bar = qpci_iomap(dev, 0, &barsize); qpci_device_enable(dev); diff --git a/tests/tco-test.c b/tests/tco-test.c index 0d201b1fcb..ef02ec5903 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -41,7 +41,7 @@ typedef struct { const char *args; bool noreboot; QPCIDevice *dev; - void *tco_io_base; + QPCIBar tco_io_bar; } TestData; static void test_init(TestData *d) @@ -70,42 +70,42 @@ static void test_init(TestData *d) /* set Root Complex BAR */ qpci_config_writel(d->dev, ICH9_LPC_RCBA, RCBA_BASE_ADDR | 0x1); - d->tco_io_base = (void *)((uintptr_t)PM_IO_BASE_ADDR + 0x60); + d->tco_io_bar = qpci_legacy_iomap(d->dev, PM_IO_BASE_ADDR + 0x60); } static void stop_tco(const TestData *d) { uint32_t val; - val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT); + val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); val |= TCO_TMR_HLT; - qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val); + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); } static void start_tco(const TestData *d) { uint32_t val; - val = qpci_io_readw(d->dev, d->tco_io_base + TCO1_CNT); + val = qpci_io_readw(d->dev, d->tco_io_bar, TCO1_CNT); val &= ~TCO_TMR_HLT; - qpci_io_writew(d->dev, d->tco_io_base + TCO1_CNT, val); + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_CNT, val); } static void load_tco(const TestData *d) { - qpci_io_writew(d->dev, d->tco_io_base + TCO_RLD, 4); + qpci_io_writew(d->dev, d->tco_io_bar, TCO_RLD, 4); } static void set_tco_timeout(const TestData *d, uint16_t ticks) { - qpci_io_writew(d->dev, d->tco_io_base + TCO_TMR, ticks); + qpci_io_writew(d->dev, d->tco_io_bar, TCO_TMR, ticks); } static void clear_tco_status(const TestData *d) { - qpci_io_writew(d->dev, d->tco_io_base + TCO1_STS, 0x0008); - qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0002); - qpci_io_writew(d->dev, d->tco_io_base + TCO2_STS, 0x0004); + qpci_io_writew(d->dev, d->tco_io_bar, TCO1_STS, 0x0008); + qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0002); + qpci_io_writew(d->dev, d->tco_io_bar, TCO2_STS, 0x0004); } static void reset_on_second_timeout(bool enable) @@ -128,25 +128,25 @@ static void test_tco_defaults(void) d.args = NULL; d.noreboot = true; test_init(&d); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD), ==, TCO_RLD_DEFAULT); /* TCO_DAT_IN & TCO_DAT_OUT */ - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_DAT_IN), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_DAT_IN), ==, (TCO_DAT_OUT_DEFAULT << 8) | TCO_DAT_IN_DEFAULT); /* TCO1_STS & TCO2_STS */ - g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_STS), ==, + g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_STS), ==, (TCO2_STS_DEFAULT << 16) | TCO1_STS_DEFAULT); /* TCO1_CNT & TCO2_CNT */ - g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_base + TCO1_CNT), ==, + g_assert_cmpint(qpci_io_readl(d.dev, d.tco_io_bar, TCO1_CNT), ==, (TCO2_CNT_DEFAULT << 16) | TCO1_CNT_DEFAULT); /* TCO_MESSAGE1 & TCO_MESSAGE2 */ - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_MESSAGE1), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_MESSAGE1), ==, (TCO_MESSAGE2_DEFAULT << 8) | TCO_MESSAGE1_DEFAULT); - g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + TCO_WDCNT), ==, + g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, TCO_WDCNT), ==, TCO_WDCNT_DEFAULT); - g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_base + SW_IRQ_GEN), ==, + g_assert_cmpint(qpci_io_readb(d.dev, d.tco_io_bar, SW_IRQ_GEN), ==, SW_IRQ_GEN_DEFAULT); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO_TMR), ==, + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO_TMR), ==, TCO_TMR_DEFAULT); qtest_end(); } @@ -171,23 +171,23 @@ static void test_tco_timeout(void) clock_step(ticks * TCO_TICK_NSEC); /* test first timeout */ - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); /* test clearing timeout bit */ val |= TCO_TIMEOUT; - qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 0); /* test second timeout */ clock_step(ticks * TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); ret = val & TCO_SECOND_TO_STS ? 1 : 0; g_assert(ret == 1); @@ -214,13 +214,13 @@ static void test_tco_max_timeout(void) start_tco(&d); clock_step(((ticks & TCO_TMR_MASK) - 1) * TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD); g_assert_cmpint(val & TCO_RLD_MASK, ==, 1); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 0); clock_step(TCO_TICK_NSEC); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & TCO_TIMEOUT ? 1 : 0; g_assert(ret == 1); @@ -358,11 +358,11 @@ static void test_tco_ticks_counter(void) start_tco(&d); do { - rld = qpci_io_readw(d.dev, d.tco_io_base + TCO_RLD) & TCO_RLD_MASK; + rld = qpci_io_readw(d.dev, d.tco_io_bar, TCO_RLD) & TCO_RLD_MASK; g_assert_cmpint(rld, ==, ticks); clock_step(TCO_TICK_NSEC); ticks--; - } while (!(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS) & TCO_TIMEOUT)); + } while (!(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS) & TCO_TIMEOUT)); stop_tco(&d); qtest_end(); @@ -378,10 +378,10 @@ static void test_tco1_control_bits(void) test_init(&d); val = TCO_LOCK; - qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); val &= ~TCO_LOCK; - qpci_io_writew(d.dev, d.tco_io_base + TCO1_CNT, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_CNT), ==, + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_CNT, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_CNT), ==, TCO_LOCK); qtest_end(); } @@ -405,13 +405,13 @@ static void test_tco1_status_bits(void) start_tco(&d); clock_step(ticks * TCO_TICK_NSEC); - qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_IN, 0); - qpci_io_writeb(d.dev, d.tco_io_base + TCO_DAT_OUT, 0); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS); + qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_IN, 0); + qpci_io_writeb(d.dev, d.tco_io_bar, TCO_DAT_OUT, 0); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS); ret = val & (TCO_TIMEOUT | SW_TCO_SMI | TCO_INT_STS) ? 1 : 0; g_assert(ret == 1); - qpci_io_writew(d.dev, d.tco_io_base + TCO1_STS, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO1_STS), ==, 0); + qpci_io_writew(d.dev, d.tco_io_bar, TCO1_STS, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO1_STS), ==, 0); qtest_end(); } @@ -434,11 +434,11 @@ static void test_tco2_status_bits(void) start_tco(&d); clock_step(ticks * TCO_TICK_NSEC * 2); - val = qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS); + val = qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS); ret = val & (TCO_SECOND_TO_STS | TCO_BOOT_STS) ? 1 : 0; g_assert(ret == 1); - qpci_io_writew(d.dev, d.tco_io_base + TCO2_STS, val); - g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_base + TCO2_STS), ==, 0); + qpci_io_writew(d.dev, d.tco_io_bar, TCO2_STS, val); + g_assert_cmpint(qpci_io_readw(d.dev, d.tco_io_bar, TCO2_STS), ==, 0); qtest_end(); } diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index a4ceeaaa43..57af8a034e 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -38,8 +38,7 @@ static void uhci_port_update(struct qhc *hc, int port, static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) { - void *addr = hc->base + 0x64 + 4 * port; - uint32_t value = qpci_io_readl(hc->dev, addr); + uint32_t value = qpci_io_readl(hc->dev, hc->bar, 0x64 + 4 * port); uint16_t mask = ~(PORTSC_CSC | PORTSC_PEDC | PORTSC_OCC); #if 0 @@ -91,7 +90,7 @@ static void pci_ehci_port_1(void) static void pci_ehci_config(void) { /* hands over all ports from companion uhci to ehci */ - qpci_io_writew(ehci1.dev, ehci1.base + 0x60, 1); + qpci_io_writew(ehci1.dev, ehci1.bar, 0x60, 1); } static void pci_uhci_port_2(void) diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index a7f06291cb..e4b2900898 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -172,15 +172,15 @@ static void init_virtio_dev(TestServer *s) g_assert_nonnull(dev); qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, &dev->vdev); - qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); - qvirtio_set_driver(&qvirtio_pci, &dev->vdev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); features = features & VIRTIO_NET_F_MAC; - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); } static void wait_for_fds(TestServer *s) @@ -847,24 +847,24 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, &dev->vdev); - qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); - qvirtio_set_driver(&qvirtio_pci, &dev->vdev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); return dev; } -static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev) +static void driver_init(QVirtioDevice *dev) { uint32_t features; - features = qvirtio_get_features(bus, dev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(bus, dev, features); + qvirtio_set_features(dev, features); - qvirtio_set_driver_ok(bus, dev); + qvirtio_set_driver_ok(dev); } #define PCI_SLOT 0x04 @@ -896,16 +896,15 @@ static void test_multiqueue(void) alloc = pc_alloc_init(); for (i = 0; i < queues * 2; i++) { - vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, i); + vq[i] = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, alloc, i); } - driver_init(&qvirtio_pci, &dev->vdev); + driver_init(&dev->vdev); wait_for_rings_started(s, queues * 2); /* End test */ for (i = 0; i < queues * 2; i++) { - qvirtqueue_cleanup(&qvirtio_pci, &vq[i]->vq, alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vq[i]->vq, alloc); } pc_alloc_uninit(alloc); qvirtio_pci_device_disable(dev); diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index e8b21967d8..9c4f6cb406 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -10,112 +10,111 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qemu-common.h" -#include "libqos/pci-pc.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" static const char mount_tag[] = "qtest"; static char *test_share; -static void qvirtio_9p_start(void) + +static QOSState *qvirtio_9p_start(void) { - char *args; + const char *arch = qtest_get_arch(); + const char *cmd = "-fsdev local,id=fsdev0,security_model=none,path=%s " + "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s"; test_share = g_strdup("/tmp/qtest.XXXXXX"); g_assert_nonnull(mkdtemp(test_share)); - args = g_strdup_printf("-fsdev local,id=fsdev0,security_model=none,path=%s " - "-device virtio-9p-pci,fsdev=fsdev0,mount_tag=%s", - test_share, mount_tag); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + return qtest_pc_boot(cmd, test_share, mount_tag); + } + if (strcmp(arch, "ppc64") == 0) { + return qtest_spapr_boot(cmd, test_share, mount_tag); + } - qtest_start(args); - g_free(args); + g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } -static void qvirtio_9p_stop(void) +static void qvirtio_9p_stop(QOSState *qs) { - qtest_end(); + qtest_shutdown(qs); rmdir(test_share); g_free(test_share); } static void pci_nop(void) { - qvirtio_9p_start(); - qvirtio_9p_stop(); + QOSState *qs; + + qs = qvirtio_9p_start(); + qvirtio_9p_stop(qs); } typedef struct { QVirtioDevice *dev; - QGuestAllocator *alloc; - QPCIBus *bus; + QOSState *qs; QVirtQueue *vq; } QVirtIO9P; -static QVirtIO9P *qvirtio_9p_pci_init(void) +static QVirtIO9P *qvirtio_9p_pci_init(QOSState *qs) { QVirtIO9P *v9p; QVirtioPCIDevice *dev; v9p = g_new0(QVirtIO9P, 1); - v9p->alloc = pc_alloc_init(); - v9p->bus = qpci_init_pc(NULL); - dev = qvirtio_pci_device_find(v9p->bus, VIRTIO_ID_9P); + v9p->qs = qs; + dev = qvirtio_pci_device_find(v9p->qs->pcibus, VIRTIO_ID_9P); g_assert_nonnull(dev); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P); v9p->dev = (QVirtioDevice *) dev; qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, v9p->dev); - qvirtio_set_acknowledge(&qvirtio_pci, v9p->dev); - qvirtio_set_driver(&qvirtio_pci, v9p->dev); + qvirtio_reset(v9p->dev); + qvirtio_set_acknowledge(v9p->dev); + qvirtio_set_driver(v9p->dev); - v9p->vq = qvirtqueue_setup(&qvirtio_pci, v9p->dev, v9p->alloc, 0); + v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0); return v9p; } static void qvirtio_9p_pci_free(QVirtIO9P *v9p) { - qvirtqueue_cleanup(&qvirtio_pci, v9p->vq, v9p->alloc); - pc_alloc_uninit(v9p->alloc); + qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc); qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev)); g_free(v9p->dev); - qpci_free_pc(v9p->bus); g_free(v9p); } static void pci_basic_config(void) { QVirtIO9P *v9p; - void *addr; size_t tag_len; char *tag; int i; + QOSState *qs; - qvirtio_9p_start(); - v9p = qvirtio_9p_pci_init(); + qs = qvirtio_9p_start(); + v9p = qvirtio_9p_pci_init(qs); - addr = ((QVirtioPCIDevice *) v9p->dev)->addr + VIRTIO_PCI_CONFIG_OFF(false); - tag_len = qvirtio_config_readw(&qvirtio_pci, v9p->dev, - (uint64_t)(uintptr_t)addr); + tag_len = qvirtio_config_readw(v9p->dev, 0); g_assert_cmpint(tag_len, ==, strlen(mount_tag)); - addr += sizeof(uint16_t); tag = g_malloc(tag_len); for (i = 0; i < tag_len; i++) { - tag[i] = qvirtio_config_readb(&qvirtio_pci, v9p->dev, - (uint64_t)(uintptr_t)addr + i); + tag[i] = qvirtio_config_readb(v9p->dev, i + 2); } g_assert_cmpmem(tag, tag_len, mount_tag, tag_len); g_free(tag); qvirtio_9p_pci_free(v9p); - qvirtio_9p_stop(); + qvirtio_9p_stop(qs); } int main(int argc, char **argv) diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 0506917341..0e32e416dd 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -10,12 +10,11 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" #include "libqos/virtio-mmio.h" -#include "libqos/pci-pc.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" #include "libqos/malloc-generic.h" #include "qemu/bswap.h" #include "standard-headers/linux/virtio_ids.h" @@ -58,24 +57,29 @@ static char *drive_create(void) return tmp_path; } -static QPCIBus *pci_test_start(void) +static QOSState *pci_test_start(void) { - char *cmdline; + QOSState *qs; + const char *arch = qtest_get_arch(); char *tmp_path; + const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " + "-drive if=none,id=drive1,file=/dev/null,format=raw " + "-device virtio-blk-pci,id=drv0,drive=drive0," + "addr=%x.%x"; tmp_path = drive_create(); - cmdline = g_strdup_printf("-drive if=none,id=drive0,file=%s,format=raw " - "-drive if=none,id=drive1,file=/dev/null,format=raw " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x", - tmp_path, PCI_SLOT, PCI_FN); - qtest_start(cmdline); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); + } else if (strcmp(arch, "ppc64") == 0) { + qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); + } else { + g_printerr("virtio-blk tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); + } unlink(tmp_path); g_free(tmp_path); - g_free(cmdline); - - return qpci_init_pc(NULL); + return qs; } static void arm_test_start(void) @@ -110,30 +114,30 @@ static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN)); qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, &dev->vdev); - qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); - qvirtio_set_driver(&qvirtio_pci, &dev->vdev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); return dev; } -static inline void virtio_blk_fix_request(QVirtioBlkReq *req) +static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) { #ifdef HOST_WORDS_BIGENDIAN - bool host_endian = true; + const bool host_is_big_endian = true; #else - bool host_endian = false; + const bool host_is_big_endian = false; #endif - if (target_big_endian() != host_endian) { + if (qvirtio_is_big_endian(d) != host_is_big_endian) { req->type = bswap32(req->type); req->ioprio = bswap32(req->ioprio); req->sector = bswap64(req->sector); } } -static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, - uint64_t data_size) +static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, + QVirtioBlkReq *req, uint64_t data_size) { uint64_t addr; uint8_t status = 0xFF; @@ -141,7 +145,7 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, g_assert_cmpuint(data_size % 512, ==, 0); addr = guest_alloc(alloc, sizeof(*req) + data_size); - virtio_blk_fix_request(req); + virtio_blk_fix_request(d, req); memwrite(addr, req, 16); memwrite(addr + 16, req->data, data_size); @@ -150,8 +154,8 @@ static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioBlkReq *req, return addr; } -static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *vq, uint64_t device_specific) +static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, + QVirtQueue *vq) { QVirtioBlkReq req; uint64_t req_addr; @@ -161,18 +165,18 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, uint8_t status; char *data; - capacity = qvirtio_config_readq(bus, dev, device_specific); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(bus, dev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(bus, dev, features); + qvirtio_set_features(dev, features); - qvirtio_set_driver_ok(bus, dev); + qvirtio_set_driver_ok(dev); /* Write and read with 3 descriptor layout */ /* Write request */ @@ -182,7 +186,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); @@ -190,9 +194,9 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, qvirtqueue_add(vq, req_addr + 16, 512, false, true); qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -204,7 +208,7 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); @@ -212,9 +216,9 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, qvirtqueue_add(vq, req_addr + 16, 512, true, true); qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -234,15 +238,15 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); free_head = qvirtqueue_add(vq, req_addr, 528, false, true); qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -254,16 +258,16 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(alloc, dev, &req, 512); g_free(req.data); free_head = qvirtqueue_add(vq, req_addr, 16, false, true); qvirtqueue_add(vq, req_addr + 16, 513, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -279,42 +283,30 @@ static void test_basic(const QVirtioBus *bus, QVirtioDevice *dev, static void pci_basic(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; - void *addr; - bus = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + qs = pci_test_start(); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - alloc = pc_alloc_init(); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - /* MSI-X is not enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); - - test_basic(&qvirtio_pci, &dev->vdev, alloc, &vqpci->vq, - (uint64_t)(uintptr_t)addr); + test_basic(&dev->vdev, qs->alloc, &vqpci->vq); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_indirect(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; + QOSState *qs; QVirtioBlkReq req; QVRingIndirectDesc *indirect; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -322,28 +314,22 @@ static void pci_indirect(void) uint8_t status; char *data; - bus = pci_test_start(); - - dev = virtio_blk_pci_init(bus, PCI_SLOT); + qs = pci_test_start(); - /* MSI-X is not enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - alloc = pc_alloc_init(); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + qvirtio_set_driver_ok(&dev->vdev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -352,23 +338,23 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2); + indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 528, false); qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); g_free(indirect); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -377,17 +363,17 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, alloc, 2); + indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 16, false); qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -398,61 +384,51 @@ static void pci_indirect(void) g_free(data); g_free(indirect); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_config(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; int n_size = TEST_IMAGE_SIZE / 2; - void *addr; uint64_t capacity; - bus = pci_test_start(); + qs = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - /* MSI-X is not enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, n_size / 512); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + + qtest_shutdown(qs); } static void pci_msix(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; QVirtioBlkReq req; int n_size = TEST_IMAGE_SIZE / 2; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -460,41 +436,34 @@ static void pci_msix(void) uint8_t status; char *data; - bus = pci_test_start(); - alloc = pc_alloc_init(); + qs = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); qpci_msix_enable(dev->pdev); - qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); + qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); - /* MSI-X is enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true); - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&qvirtio_pci, &dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -504,22 +473,22 @@ static void pci_msix(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -527,7 +496,7 @@ static void pci_msix(void) req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -535,10 +504,10 @@ static void pci_msix(void) qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); @@ -549,26 +518,22 @@ static void pci_msix(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_idx(void) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *vqpci; - QGuestAllocator *alloc; QVirtioBlkReq req; - void *addr; uint64_t req_addr; uint64_t capacity; uint32_t features; @@ -576,33 +541,27 @@ static void pci_idx(void) uint8_t status; char *data; - bus = pci_test_start(); - alloc = pc_alloc_init(); + qs = pci_test_start(); - dev = virtio_blk_pci_init(bus, PCI_SLOT); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); qpci_msix_enable(dev->pdev); - qvirtio_pci_set_msix_configuration_vector(dev, alloc, 0); + qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); - /* MSI-X is enabled */ - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(true); - - capacity = qvirtio_config_readq(&qvirtio_pci, &dev->vdev, - (uint64_t)(uintptr_t)addr); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&qvirtio_pci, &dev->vdev); + features = qvirtio_get_features(&dev->vdev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&qvirtio_pci, &dev->vdev, features); + qvirtio_set_features(&dev->vdev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, alloc, 1); + vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); - qvirtio_set_driver_ok(&qvirtio_pci, &dev->vdev); + qvirtio_set_driver_ok(&dev->vdev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -611,17 +570,16 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -630,7 +588,7 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -639,15 +597,15 @@ static void pci_idx(void) free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); /* No notification expected */ - status = qvirtio_wait_status_byte_no_isr(&qvirtio_pci, &dev->vdev, + status = qvirtio_wait_status_byte_no_isr(&dev->vdev, &vqpci->vq, req_addr + 528, QVIRTIO_BLK_TIMEOUT_US); g_assert_cmpint(status, ==, 0); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -655,7 +613,7 @@ static void pci_idx(void) req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(alloc, &req, 512); + req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); g_free(req.data); @@ -663,9 +621,9 @@ static void pci_idx(void) qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&qvirtio_pci, &dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, &dev->vdev, &vqpci->vq, + qvirtio_wait_queue_isr(&dev->vdev, &vqpci->vq, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); @@ -676,38 +634,38 @@ static void pci_idx(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(alloc, req_addr); + guest_free(qs->alloc, req_addr); /* End test */ - qvirtqueue_cleanup(&qvirtio_pci, &vqpci->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); qpci_msix_disable(dev->pdev); qvirtio_pci_device_disable(dev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } static void pci_hotplug(void) { - QPCIBus *bus; QVirtioPCIDevice *dev; + QOSState *qs; + const char *arch = qtest_get_arch(); - bus = pci_test_start(); + qs = pci_test_start(); /* plug secondary disk */ qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, "'drive': 'drive1'"); - dev = virtio_blk_pci_init(bus, PCI_SLOT_HP); + dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); g_assert(dev); qvirtio_pci_device_disable(dev); g_free(dev); /* unplug secondary disk */ - qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); - qpci_free_pc(bus); - test_end(); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); + } + qtest_shutdown(qs); } static void mmio_basic(void) @@ -724,30 +682,27 @@ static void mmio_basic(void) g_assert(dev != NULL); g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - qvirtio_reset(&qvirtio_mmio, &dev->vdev); - qvirtio_set_acknowledge(&qvirtio_mmio, &dev->vdev); - qvirtio_set_driver(&qvirtio_mmio, &dev->vdev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); - vq = qvirtqueue_setup(&qvirtio_mmio, &dev->vdev, alloc, 0); + vq = qvirtqueue_setup(&dev->vdev, alloc, 0); - test_basic(&qvirtio_mmio, &dev->vdev, alloc, vq, - QVIRTIO_MMIO_DEVICE_SPECIFIC); + test_basic(&dev->vdev, alloc, vq); qmp("{ 'execute': 'block_resize', 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_queue_isr(&qvirtio_mmio, &dev->vdev, vq, - QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&qvirtio_mmio, &dev->vdev, - QVIRTIO_MMIO_DEVICE_SPECIFIC); + capacity = qvirtio_config_readq(&dev->vdev, 0); g_assert_cmpint(capacity, ==, n_size / 512); /* End test */ - qvirtqueue_cleanup(&qvirtio_mmio, vq, alloc); - generic_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); g_free(dev); + generic_alloc_uninit(alloc); test_end(); } @@ -757,12 +712,15 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || + strcmp(arch, "ppc64") == 0) { qtest_add_func("/virtio/blk/pci/basic", pci_basic); qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); qtest_add_func("/virtio/blk/pci/config", pci_config); - qtest_add_func("/virtio/blk/pci/msix", pci_msix); - qtest_add_func("/virtio/blk/pci/idx", pci_idx); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/virtio/blk/pci/msix", pci_msix); + qtest_add_func("/virtio/blk/pci/idx", pci_idx); + } qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); } else if (strcmp(arch, "arm") == 0) { qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index a343a6b048..8f94360480 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -12,12 +12,10 @@ #include "qemu-common.h" #include "qemu/sockets.h" #include "qemu/iov.h" -#include "libqos/pci-pc.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" -#include "libqos/malloc-generic.h" #include "qemu/bswap.h" #include "hw/virtio/virtio-net.h" #include "standard-headers/linux/virtio_ids.h" @@ -46,39 +44,43 @@ static QVirtioPCIDevice *virtio_net_pci_init(QPCIBus *bus, int slot) g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, &dev->vdev); - qvirtio_set_acknowledge(&qvirtio_pci, &dev->vdev); - qvirtio_set_driver(&qvirtio_pci, &dev->vdev); + qvirtio_reset(&dev->vdev); + qvirtio_set_acknowledge(&dev->vdev); + qvirtio_set_driver(&dev->vdev); return dev; } -static QPCIBus *pci_test_start(int socket) +static QOSState *pci_test_start(int socket) { - char *cmdline; - - cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 -device " - "virtio-net-pci,netdev=hs0", socket); - qtest_start(cmdline); - g_free(cmdline); - - return qpci_init_pc(NULL); + const char *arch = qtest_get_arch(); + const char *cmd = "-netdev socket,fd=%d,id=hs0 -device " + "virtio-net-pci,netdev=hs0"; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + return qtest_pc_boot(cmd, socket); + } + if (strcmp(arch, "ppc64") == 0) { + return qtest_spapr_boot(cmd, socket); + } + g_printerr("virtio-net tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } -static void driver_init(const QVirtioBus *bus, QVirtioDevice *dev) +static void driver_init(QVirtioDevice *dev) { uint32_t features; - features = qvirtio_get_features(bus, dev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(bus, dev, features); + qvirtio_set_features(dev, features); - qvirtio_set_driver_ok(bus, dev); + qvirtio_set_driver_ok(dev); } -static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void rx_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) { @@ -101,19 +103,19 @@ static void rx_test(const QVirtioBus *bus, QVirtioDevice *dev, req_addr = guest_alloc(alloc, 64); free_head = qvirtqueue_add(vq, req_addr, 64, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); ret = iov_send(socket, iov, 2, 0, sizeof(len) + sizeof(test)); g_assert_cmpint(ret, ==, sizeof(test) + sizeof(len)); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US); memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); g_assert_cmpstr(buffer, ==, "TEST"); guest_free(alloc, req_addr); } -static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void tx_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) { @@ -127,9 +129,9 @@ static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, memwrite(req_addr + VNET_HDR_SIZE, "TEST", 4); free_head = qvirtqueue_add(vq, req_addr, 64, false, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US); guest_free(alloc, req_addr); ret = qemu_recv(socket, &len, sizeof(len), 0); @@ -140,7 +142,7 @@ static void tx_test(const QVirtioBus *bus, QVirtioDevice *dev, g_assert_cmpstr(buffer, ==, "TEST"); } -static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void rx_stop_cont_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) { @@ -164,7 +166,7 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, req_addr = guest_alloc(alloc, 64); free_head = qvirtqueue_add(vq, req_addr, 64, true, false); - qvirtqueue_kick(bus, dev, vq, free_head); + qvirtqueue_kick(dev, vq, free_head); rsp = qmp("{ 'execute' : 'stop'}"); QDECREF(rsp); @@ -180,36 +182,34 @@ static void rx_stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, rsp = qmp("{ 'execute' : 'cont'}"); QDECREF(rsp); - qvirtio_wait_queue_isr(bus, dev, vq, QVIRTIO_NET_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_NET_TIMEOUT_US); memread(req_addr + VNET_HDR_SIZE, buffer, sizeof(test)); g_assert_cmpstr(buffer, ==, "TEST"); guest_free(alloc, req_addr); } -static void send_recv_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void send_recv_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *rvq, QVirtQueue *tvq, int socket) { - rx_test(bus, dev, alloc, rvq, socket); - tx_test(bus, dev, alloc, tvq, socket); + rx_test(dev, alloc, rvq, socket); + tx_test(dev, alloc, tvq, socket); } -static void stop_cont_test(const QVirtioBus *bus, QVirtioDevice *dev, +static void stop_cont_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *rvq, QVirtQueue *tvq, int socket) { - rx_stop_cont_test(bus, dev, alloc, rvq, socket); + rx_stop_cont_test(dev, alloc, rvq, socket); } static void pci_basic(gconstpointer data) { QVirtioPCIDevice *dev; - QPCIBus *bus; + QOSState *qs; QVirtQueuePCI *tx, *rx; - QGuestAllocator *alloc; - void (*func) (const QVirtioBus *bus, - QVirtioDevice *dev, + void (*func) (QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *rvq, QVirtQueue *tvq, @@ -219,37 +219,37 @@ static void pci_basic(gconstpointer data) ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); g_assert_cmpint(ret, !=, -1); - bus = pci_test_start(sv[1]); - dev = virtio_net_pci_init(bus, PCI_SLOT); + qs = pci_test_start(sv[1]); + dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT); - alloc = pc_alloc_init(); - rx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 0); - tx = (QVirtQueuePCI *)qvirtqueue_setup(&qvirtio_pci, &dev->vdev, - alloc, 1); + rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); + tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1); - driver_init(&qvirtio_pci, &dev->vdev); - func(&qvirtio_pci, &dev->vdev, alloc, &rx->vq, &tx->vq, sv[0]); + driver_init(&dev->vdev); + func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]); /* End test */ close(sv[0]); - qvirtqueue_cleanup(&qvirtio_pci, &tx->vq, alloc); - qvirtqueue_cleanup(&qvirtio_pci, &rx->vq, alloc); - pc_alloc_uninit(alloc); + qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc); + qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc); qvirtio_pci_device_disable(dev); g_free(dev->pdev); g_free(dev); - qpci_free_pc(bus); - test_end(); + qtest_shutdown(qs); } #endif static void hotplug(void) { + const char *arch = qtest_get_arch(); + qtest_start("-device virtio-net-pci"); qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL); - qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); + } test_end(); } diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index e1b26401f9..dcecf77463 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -20,8 +20,13 @@ static void pci_nop(void) static void hotplug(void) { + const char *arch = qtest_get_arch(); + qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL); - qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); + } } int main(int argc, char **argv) diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 79088bb249..69220ef07b 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -11,12 +11,10 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "block/scsi.h" +#include "libqos/libqos-pc.h" +#include "libqos/libqos-spapr.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" -#include "libqos/pci-pc.h" -#include "libqos/malloc.h" -#include "libqos/malloc-pc.h" -#include "libqos/malloc-generic.h" #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" #include "standard-headers/linux/virtio_scsi.h" @@ -29,28 +27,32 @@ typedef struct { QVirtioDevice *dev; - QGuestAllocator *alloc; - QPCIBus *bus; + QOSState *qs; int num_queues; QVirtQueue *vq[MAX_NUM_QUEUES + 2]; } QVirtIOSCSI; -static void qvirtio_scsi_start(const char *extra_opts) +static QOSState *qvirtio_scsi_start(const char *extra_opts) { - char *cmdline; - - cmdline = g_strdup_printf( - "-drive id=drv0,if=none,file=/dev/null,format=raw " - "-device virtio-scsi-pci,id=vs0 " - "-device scsi-hd,bus=vs0.0,drive=drv0 %s", - extra_opts ? : ""); - qtest_start(cmdline); - g_free(cmdline); + const char *arch = qtest_get_arch(); + const char *cmd = "-drive id=drv0,if=none,file=/dev/null,format=raw " + "-device virtio-scsi-pci,id=vs0 " + "-device scsi-hd,bus=vs0.0,drive=drv0 %s"; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + return qtest_pc_boot(cmd, extra_opts ? : ""); + } + if (strcmp(arch, "ppc64") == 0) { + return qtest_spapr_boot(cmd, extra_opts ? : ""); + } + + g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); + exit(EXIT_FAILURE); } -static void qvirtio_scsi_stop(void) +static void qvirtio_scsi_stop(QOSState *qs) { - qtest_end(); + qtest_shutdown(qs); } static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) @@ -58,12 +60,12 @@ static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) int i; for (i = 0; i < vs->num_queues + 2; i++) { - qvirtqueue_cleanup(&qvirtio_pci, vs->vq[i], vs->alloc); + qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], vs->qs->alloc); } - pc_alloc_uninit(vs->alloc); qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev)); g_free(vs->dev); - qpci_free_pc(vs->bus); + qvirtio_scsi_stop(vs->qs); + g_free(vs); } static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, @@ -71,7 +73,7 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, { uint64_t addr; - addr = guest_alloc(vs->alloc, alloc_size); + addr = guest_alloc(vs->qs->alloc, alloc_size); if (data) { memwrite(addr, data, alloc_size); } @@ -118,8 +120,8 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, qvirtqueue_add(vq, data_in_addr, data_in_len, true, false); } - qvirtqueue_kick(&qvirtio_pci, vs->dev, vq, free_head); - qvirtio_wait_queue_isr(&qvirtio_pci, vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US); + qvirtqueue_kick(vs->dev, vq, free_head); + qvirtio_wait_queue_isr(vs->dev, vq, QVIRTIO_SCSI_TIMEOUT_US); response = readb(resp_addr + offsetof(struct virtio_scsi_cmd_resp, response)); @@ -128,10 +130,10 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, memread(resp_addr, resp_out, sizeof(*resp_out)); } - guest_free(vs->alloc, req_addr); - guest_free(vs->alloc, resp_addr); - guest_free(vs->alloc, data_in_addr); - guest_free(vs->alloc, data_out_addr); + guest_free(vs->qs->alloc, req_addr); + guest_free(vs->qs->alloc, resp_addr); + guest_free(vs->qs->alloc, data_in_addr); + guest_free(vs->qs->alloc, data_out_addr); return response; } @@ -141,31 +143,29 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) QVirtIOSCSI *vs; QVirtioPCIDevice *dev; struct virtio_scsi_cmd_resp resp; - void *addr; int i; vs = g_new0(QVirtIOSCSI, 1); - vs->alloc = pc_alloc_init(); - vs->bus = qpci_init_pc(NULL); - dev = qvirtio_pci_device_find(vs->bus, VIRTIO_ID_SCSI); + vs->qs = qvirtio_scsi_start("-drive file=blkdebug::null-co://," + "if=none,id=dr1,format=raw,file.align=4k " + "-device scsi-disk,drive=dr1,lun=0,scsi-id=1"); + dev = qvirtio_pci_device_find(vs->qs->pcibus, VIRTIO_ID_SCSI); vs->dev = (QVirtioDevice *)dev; g_assert(dev != NULL); g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI); qvirtio_pci_device_enable(dev); - qvirtio_reset(&qvirtio_pci, vs->dev); - qvirtio_set_acknowledge(&qvirtio_pci, vs->dev); - qvirtio_set_driver(&qvirtio_pci, vs->dev); + qvirtio_reset(vs->dev); + qvirtio_set_acknowledge(vs->dev); + qvirtio_set_driver(vs->dev); - addr = dev->addr + VIRTIO_PCI_CONFIG_OFF(false); - vs->num_queues = qvirtio_config_readl(&qvirtio_pci, vs->dev, - (uint64_t)(uintptr_t)addr); + vs->num_queues = qvirtio_config_readl(vs->dev, 0); g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(&qvirtio_pci, vs->dev, vs->alloc, i); + vs->vq[i] = qvirtqueue_setup(vs->dev, vs->qs->alloc, i); } /* Clear the POWER ON OCCURRED unit attention */ @@ -184,15 +184,18 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) { - qvirtio_scsi_start(NULL); - qvirtio_scsi_stop(); + QOSState *qs; + + qs = qvirtio_scsi_start(NULL); + qvirtio_scsi_stop(qs); } static void hotplug(void) { QDict *response; + QOSState *qs; - qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw"); + qs = qvirtio_scsi_start("-drive id=drv1,if=none,file=/dev/null,format=raw"); response = qmp("{\"execute\": \"device_add\"," " \"arguments\": {" " \"driver\": \"scsi-hd\"," @@ -214,7 +217,7 @@ static void hotplug(void) g_assert(qdict_haskey(response, "event")); g_assert(!strcmp(qdict_get_str(response, "event"), "DEVICE_DELETED")); QDECREF(response); - qvirtio_scsi_stop(); + qvirtio_scsi_stop(qs); } /* Test WRITE SAME with the lba not aligned */ @@ -230,9 +233,6 @@ static void test_unaligned_write_same(void) 0x41, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; - qvirtio_scsi_start("-drive file=blkdebug::null-co://,if=none,id=dr1" - ",format=raw,file.align=4k " - "-device scsi-disk,drive=dr1,lun=0,scsi-id=1"); vs = qvirtio_scsi_pci_init(PCI_SLOT); g_assert_cmphex(0, ==, @@ -242,7 +242,6 @@ static void test_unaligned_write_same(void) virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, NULL)); qvirtio_scsi_pci_free(vs); - qvirtio_scsi_stop(); } int main(int argc, char **argv) |