From 09b04845a7b7ffba2fa0cd99a2329f6e8ffa9027 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 27 Aug 2013 13:42:47 +1000 Subject: pseries: Update SLOF firmware image This has reworked USB OHCI and adds support of USB EHCI, VIRTIO-SCSI and various fixes (IBM VSCSI, VGA and more). The full list of fixes is: * usb-ohci: Convert td-phys every time to td-virt * usb-storage: Fix cbwflags field * Add -fno-strict-aliasing in global CFLAGS * usb: fix various issues found with js2x * Move hex64-{decode,encode}-unit to node.fs * usb: Use separate in-memory endian swap * usb-ohci: collect TDs from done list * js2x: more fixes * js2x: Fix build of takeover image * js2x: use new usb stack * usb-ohci: Use proper memory barriers always * usb: Fix a couple of warnings * Fix $cat-instance-unit * Cache phandle of /chosen * Use root.fs on qemu as well * usb-ehci: Add ehci handshake * usb: add mb for write accessors * usb-ohci: add missing memory barriers * usb-ohci: suspend the controller in exit code path * usb-ohci: Add a reset when closing the OHCI * usb: Use proper accessors for MMIO and separate in-memory endian swap * Use a global definition of sync() and mb() * net-snk: Remove exception handling * usb: unmap buffers * slof: call quiesce on closing of stdin * usb-kbd: accept "s" to drop to OF prompt * USB storage driver * usb-ohci: add Bulk transfer support * usb-ehci: Add bulk support * usb-core: add usb bulk support * USB generic hub device driver * usb-ehci: setup new device * usb-ehci: Check ehci ports * usb-ehci: initialize controller * USB keyboard driver * usb-core: setup new device * usb-core: create dev pool allocation * usb-ohci: implement ohci send control * usb-core: usb send control * usb-core: implement usb_{get,put}_pipe routines * usb-ohci: allocate pipe pool * usb-ohci: reset, init and check-ports * Add standard header stdbool.h * usb-slof: forth support routines for C * usb-ehci: Add USB EHCI skeleton * usb-core: Add register accessor functions * Use __builtin_bswap routines for endianness swapping * usb-core: hcd registration and query routines * usb-core: adding generic dev-hci.fs * usb-core: registration and makefiles * Add new USB code * Remove old usb code * vga: fix hcall-invert-screen and hcall-blink-screen * Enumerate disk/cdrom aliases for multiple disks or cdroms * scsi: unify scsi probing code * vscsi: generalizing probe code * virtio-scsi: iterate through targets * scsi: unify and use make-disk-alias * nvram: remove unnecessary prints * Add hack to client interface finddevice of "/memory" * scsi: Fix cdrom boot crash when no medium present * Look for /memory@0, not just /memory * Fix instance>qname crashing when displaying instance arguments * Fix js2x build * scsi-disk: Bound check read-blocks * Fix off by one error in scsi-disk get-capacity * scsi: fix report-luns handling * SLOF: virtio-scsi block driver code * scsi: Move bits of vio-vscsi.fs to a common helpers file * scsi: Move scsi-disk.fs to a generic place * SLOF: virtio-scsi helper routines * SLOF: virtio-scsi - add pci device file * iso9660: Don't constantly reallocate the read buffer * vscsi: Sanitize interface between scsi-disk.fs and vio-vscsi.fs * vio-vscsi: Rework vio-vscsi support * virtio: Add a virtio-set-qaddr helper * disk-label: Allocate 4096 bytes for 4k block devices * disk-label: Increase the max size of the PReP boot partition * Make load-base a real environment variable * vio-vscsi: Switch to using a wildcard "disk" node and make scsi-disk generic * Fix disk-label package to use proper instance path * Increase size of catpad * Fix instance>path to contain unit address for wildcard nodes * Fix handling of wildcard nodes in open-dev * vio-vscsi: Get CRQ on open and release on close Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- pc-bios/README | 2 +- pc-bios/slof.bin | Bin 909720 -> 875424 bytes roms/SLOF | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pc-bios/README b/pc-bios/README index be8dae0..b4138d1 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-20130430. + built from git tag qemu-slof-20130827. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 092e58a..0e8b51a 100644 Binary files a/pc-bios/slof.bin and b/pc-bios/slof.bin differ diff --git a/roms/SLOF b/roms/SLOF index 8cfdfc4..a523d1b 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 8cfdfc43f4c4c8c8dfa4b7cf16f7c19c84eee812 +Subproject commit a523d1b0cd6e96cf5e393f0a10f897e8ed639fdc -- cgit v1.1 From 16457e7f4a10125db06b84e5e843d9544552436e Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 25 Sep 2013 17:40:15 +1000 Subject: pseries: Fix loading of little endian kernels Try loading the kernel as little endian if it fails big endian. Signed-off-by: Benjamin Herrenschmidt Reviewed-by: Anton Blanchard Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 004184d..5bf6c3b 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -273,6 +273,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, hwaddr initrd_base, hwaddr initrd_size, hwaddr kernel_size, + bool little_endian, const char *boot_device, const char *kernel_cmdline, uint32_t epow_irq) @@ -326,6 +327,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model, 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_device) { _FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device))); @@ -1102,6 +1106,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) uint32_t initrd_base = 0; long kernel_size = 0, initrd_size = 0; long load_limit, rtas_limit, fw_size; + bool kernel_le = false; char *filename; msi_supported = true; @@ -1282,6 +1287,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0); if (kernel_size < 0) { + kernel_size = load_elf(kernel_filename, + translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0); + kernel_le = kernel_size > 0; + } + if (kernel_size < 0) { kernel_size = load_image_targphys(kernel_filename, KERNEL_LOAD_ADDR, load_limit - KERNEL_LOAD_ADDR); @@ -1331,7 +1342,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) /* Prepare the device tree */ spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, initrd_base, initrd_size, - kernel_size, + kernel_size, kernel_le, boot_device, kernel_cmdline, spapr->epow_irq); assert(spapr->fdt_skel != NULL); -- cgit v1.1 From 04f1f7842e18c4b5e50203cc5b207cafb7c62974 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Wed, 25 Sep 2013 17:41:13 +1000 Subject: ppc: Add CFAR, DAR and DSISR to the dictionary of printable registers The CFAR, DAR and DSISR registers are currently missing from the dictionary of registers that may be printed in the QEMU console. These are interesting registers when debugging. With this patch, the following commands work properly: (qemu) print $cfar (qemu) print $dar (qemu) print $dsisr Signed-off-by: Tom Musta Reviewed-by: Anton Blanchard Signed-off-by: Alexander Graf --- monitor.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monitor.c b/monitor.c index 74f3f1b..b02b21c 100644 --- a/monitor.c +++ b/monitor.c @@ -3186,6 +3186,9 @@ static const MonitorDef monitor_defs[] = { { "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) }, { "srr1", offsetof(CPUPPCState, spr[SPR_SRR1]) }, + { "dar", offsetof(CPUPPCState, spr[SPR_DAR]) }, + { "dsisr", offsetof(CPUPPCState, spr[SPR_DSISR]) }, + { "cfar", offsetof(CPUPPCState, spr[SPR_CFAR]) }, { "sprg0", offsetof(CPUPPCState, spr[SPR_SPRG0]) }, { "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) }, { "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) }, -- cgit v1.1 From bbfb6f132abc032229f5c1f25e6e959861c6f759 Mon Sep 17 00:00:00 2001 From: Tom Musta Date: Wed, 25 Sep 2013 17:42:46 +1000 Subject: target-ppc: Little Endian Correction to Load/Store Vector Element The Load Vector Element (lve*x) and Store Vector Element (stve*x) instructions not only byte-swap in Little Endian mode, they also invert the element that is accessed. For example, the RTL for lvehx contains this: eb <-- EA[60:63] if Big-Endian byte ordering then VRT[8*eb:8*eb+15] <-- MEM(EA,2) else VRT[112-(8*eb):127-(8*eb)] <-- MEM(EA,2) This patch adds the element inversion, as described in the last line of the RTL. Signed-off-by: Tom Musta Reviewed-by: Anton Blanchard Signed-off-by: Alexander Graf --- target-ppc/mem_helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target-ppc/mem_helper.c b/target-ppc/mem_helper.c index d8e63ca..f35ed03 100644 --- a/target-ppc/mem_helper.c +++ b/target-ppc/mem_helper.c @@ -212,6 +212,7 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, int index = (addr & 0xf) >> sh; \ \ if (msr_le) { \ + index = n_elems - index - 1; \ r->element[LO_IDX ? index : (adjust - index)] = \ swap(access(env, addr)); \ } else { \ @@ -236,6 +237,7 @@ LVE(lvewx, cpu_ldl_data, bswap32, u32) int index = (addr & 0xf) >> sh; \ \ if (msr_le) { \ + index = n_elems - index - 1; \ access(env, addr, swap(r->element[LO_IDX ? index : \ (adjust - index)])); \ } else { \ -- cgit v1.1 From 9633fcc6a02f23e3ef00aa5fe3fe9c41f57c3456 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 25 Sep 2013 15:41:12 +0200 Subject: PPC: Fix L2CR write accesses Commit 2345f1c01 was supposed to render L2CR writes into noops. Instead, it made them illegal instruction traps which apparently didn't confuse XNU, but can easily confuse other OSs. Fix it up by actually doing nothing when we write to L2CR. Reported-by: Julio Guerra Signed-off-by: Alexander Graf Tested-by: Julio Guerra --- target-ppc/translate_init.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 651da6b..807dab3 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -108,6 +108,11 @@ static void spr_write_clear (void *opaque, int sprn, int gprn) tcg_temp_free(t0); tcg_temp_free(t1); } + +static void spr_access_nop(void *opaque, int sprn, int gprn) +{ +} + #endif /* SPR common to all PowerPC */ @@ -1382,7 +1387,7 @@ static void gen_spr_74xx (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Not strictly an SPR */ vscr_init(env, 0x00010000); @@ -5170,7 +5175,7 @@ static void init_proc_750 (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5233,7 +5238,7 @@ static void init_proc_750cl (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5419,7 +5424,7 @@ static void init_proc_750cx (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5486,7 +5491,7 @@ static void init_proc_750fx (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5558,7 +5563,7 @@ static void init_proc_750gx (CPUPPCState *env) /* XXX : not implemented (XXX: different from 750fx) */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Time base */ gen_tbl(env); @@ -5694,7 +5699,7 @@ static void init_proc_755 (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* XXX : not implemented */ spr_register(env, SPR_L2PMCR, "L2PMCR", @@ -6650,7 +6655,7 @@ static void init_proc_970 (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -6750,7 +6755,7 @@ static void init_proc_970FX (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -6862,7 +6867,7 @@ static void init_proc_970GX (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -6962,7 +6967,7 @@ static void init_proc_970MP (CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ @@ -7054,7 +7059,7 @@ static void init_proc_power5plus(CPUPPCState *env) /* XXX : not implemented */ spr_register(env, SPR_L2CR, "L2CR", SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, NULL, + &spr_read_generic, spr_access_nop, 0x00000000); /* Memory management */ /* XXX: not correct */ -- cgit v1.1 From 3bf6eedd4b6ee5cb7be53aa962583a24293d3441 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Tue, 24 Sep 2013 15:59:55 +1000 Subject: spapr: increase temporary fdt buffer size At the moment the size of the buffer is set to 64K which is enough for approximately 150 VCPUs which is not the limit. This increases the buffer up to 256K which allows having a tree for approximately 600 VCPUs which is way beyond the real number we need. As only the real size of the tree is copied to the guest, there will be no impact on existing configurations. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 5bf6c3b..6322c98 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -62,7 +62,7 @@ * * We load our kernel at 4M, leaving space for SLOF initial image */ -#define FDT_MAX_SIZE 0x10000 +#define FDT_MAX_SIZE 0x40000 #define RTAS_MAX_SIZE 0x10000 #define FW_MAX_SIZE 0x400000 #define FW_FILE_NAME "slof.bin" -- cgit v1.1 From dcb861cb883e9e6d236514a4d0b4def4db736d13 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Fri, 27 Sep 2013 18:11:51 +1000 Subject: spapr: Add ibm, purr property on power7 and newer PAPR+ says that no "ibm,purr" tells the guest that H_PURR is not supported. However some guests still try calling H_PURR on POWER7 unless the property is present and equal to 0. This adds the property for CPUs supporting the PURR special register. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 6322c98..259df92 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -422,6 +422,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s", gservers_prop, sizeof(gservers_prop)))); + if (env->spr_cb[SPR_PURR].oea_read) { + _FDT((fdt_property(fdt, "ibm,purr", NULL, 0))); + } + if (env->mmu_model & POWERPC_MMU_1TSEG) { _FDT((fdt_property(fdt, "ibm,processor-segment-sizes", segs, sizeof(segs)))); -- cgit v1.1 From 4fe822e075d6befa3714f7066158678e92cedb8b Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Fri, 27 Sep 2013 18:10:18 +1000 Subject: spapr-rtas: fix h_rtas parameters reading On the real hardware, RTAS is called in real mode and therefore top 4 bits of the address passed in the call are ignored. So does the patch. This converts h_rtas() to use existing rtas_ld() handlers. This fixed rtas_ld()/rtas_st() to ignore top 4 bits. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr_hcall.c | 6 +++--- include/hw/ppc/spapr.h | 9 +++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index f10ba8a..f755a53 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -521,9 +521,9 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { target_ulong rtas_r3 = args[0]; - uint32_t token = ldl_be_phys(rtas_r3); - uint32_t nargs = ldl_be_phys(rtas_r3 + 4); - uint32_t nret = ldl_be_phys(rtas_r3 + 8); + uint32_t token = rtas_ld(rtas_r3, 0); + uint32_t nargs = rtas_ld(rtas_r3, 1); + uint32_t nret = rtas_ld(rtas_r3, 2); return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12, nret, rtas_r3 + 12 + 4*nargs); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index e37b419..6407c8a 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -332,14 +332,19 @@ static inline int spapr_allocate_lsi(int hint) return spapr_allocate_irq(hint, true); } +static inline uint64_t ppc64_phys_to_real(uint64_t addr) +{ + return addr & ~0xF000000000000000ULL; +} + static inline uint32_t rtas_ld(target_ulong phys, int n) { - return ldl_be_phys(phys + 4*n); + return ldl_be_phys(ppc64_phys_to_real(phys + 4*n)); } static inline void rtas_st(target_ulong phys, int n, uint32_t val) { - stl_be_phys(phys + 4*n, val); + stl_be_phys(ppc64_phys_to_real(phys + 4*n), val); } typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr, -- cgit v1.1 From feaa64c41f56b1b3111c31f652999799b28b9e12 Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 26 Sep 2013 16:18:35 +1000 Subject: target-ppc: Add helper for KVM_PPC_RTAS_DEFINE_TOKEN Recent PowerKVM allows the kernel to intercept some RTAS calls from the guest directly. This is used to implement the more efficient in-kernel XICS for example. qemu is still responsible for assigning the RTAS token numbers however, and needs to tell the kernel which RTAS function name is assigned to a given token value. This patch adds a convenience wrapper for the KVM_PPC_RTAS_DEFINE_TOKEN ioctl() which is used for this purpose. Signed-off-by: David Gibson Signed-off-by: Alexey Kardashevskiy Acked-by: David Gibson Signed-off-by: Alexander Graf --- target-ppc/kvm.c | 14 ++++++++++++++ target-ppc/kvm_ppc.h | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 8a196c6..0b5d391 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1789,6 +1789,20 @@ static int kvm_ppc_register_host_cpu_type(void) return 0; } +int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function) +{ + struct kvm_rtas_token_args args = { + .token = token, + }; + + if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_RTAS)) { + return -ENOENT; + } + + strncpy(args.name, function, sizeof(args.name)); + + return kvm_vm_ioctl(kvm_state, KVM_PPC_RTAS_DEFINE_TOKEN, &args); +} int kvmppc_get_htab_fd(bool write) { diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 4ae7bf2..5f78e4b 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -38,6 +38,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); #endif /* !CONFIG_USER_ONLY */ int kvmppc_fixup_cpu(PowerPCCPU *cpu); bool kvmppc_has_cap_epr(void); +int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function); int kvmppc_get_htab_fd(bool write); int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns); int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, @@ -164,6 +165,12 @@ static inline bool kvmppc_has_cap_epr(void) return false; } +static inline int kvmppc_define_rtas_kernel_token(uint32_t token, + const char *function) +{ + return -1; +} + static inline int kvmppc_get_htab_fd(bool write) { return -1; -- cgit v1.1 From 8ffe04ed2ed44b32f97575bc3cb7c29eefdd70da Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:36 +1000 Subject: xics: move reset and cpu_setup This simple change makes following patches nicer. Signed-off-by: Alexey Kardashevskiy Acked-by: David Gibson Signed-off-by: Alexander Graf --- hw/intc/xics.c | 72 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index bb018d1..a0d71ef 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -30,6 +30,42 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" +void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUPPCState *env = &cpu->env; + ICPState *ss = &icp->ss[cs->cpu_index]; + + assert(cs->cpu_index < icp->nr_servers); + + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER7: + ss->output = env->irq_inputs[POWER7_INPUT_INT]; + break; + + case PPC_FLAGS_INPUT_970: + ss->output = env->irq_inputs[PPC970_INPUT_INT]; + break; + + default: + fprintf(stderr, "XICS interrupt controller does not support this CPU " + "bus model\n"); + abort(); + } +} + +static void xics_reset(DeviceState *d) +{ + XICSState *icp = XICS(d); + int i; + + for (i = 0; i < icp->nr_servers; i++) { + device_reset(DEVICE(&icp->ss[i])); + } + + device_reset(DEVICE(icp->ics)); +} + /* * ICP: Presentation layer */ @@ -600,42 +636,6 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, * XICS */ -static void xics_reset(DeviceState *d) -{ - XICSState *icp = XICS(d); - int i; - - for (i = 0; i < icp->nr_servers; i++) { - device_reset(DEVICE(&icp->ss[i])); - } - - device_reset(DEVICE(icp->ics)); -} - -void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUPPCState *env = &cpu->env; - ICPState *ss = &icp->ss[cs->cpu_index]; - - assert(cs->cpu_index < icp->nr_servers); - - switch (PPC_INPUT(env)) { - case PPC_FLAGS_INPUT_POWER7: - ss->output = env->irq_inputs[POWER7_INPUT_INT]; - break; - - case PPC_FLAGS_INPUT_970: - ss->output = env->irq_inputs[PPC970_INPUT_INT]; - break; - - default: - fprintf(stderr, "XICS interrupt controller does not support this CPU " - "bus model\n"); - abort(); - } -} - static void xics_realize(DeviceState *dev, Error **errp) { XICSState *icp = XICS(dev); -- cgit v1.1 From 24408a7d2b459bed3697367b81ada76518ca96ef Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:37 +1000 Subject: spapr: move cpu_setup after kvmppc_set_papr This moves the xics_cpu_setup() call after kvmppc_set_papr() in order to get VCPUs initialized as this is required by upcoming XICS-KVM. Signed-off-by: Alexey Kardashevskiy Acked-by: David Gibson Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 259df92..a276377 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1184,8 +1184,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) } env = &cpu->env; - xics_cpu_setup(spapr->icp, cpu); - /* Set time-base frequency to 512 MHz */ cpu_ppc_tb_init(env, TIMEBASE_FREQ); @@ -1199,6 +1197,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) kvmppc_set_papr(cpu); } + xics_cpu_setup(spapr->icp, cpu); + qemu_register_reset(spapr_cpu_reset, cpu); } -- cgit v1.1 From 9ccff2a4d604d31f01398190758072253dc3c188 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:38 +1000 Subject: xics: replace fprintf with error_report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This replaces old-style fprintf with new style error_report. Signed-off-by: Alexey Kardashevskiy Reviewed-by: Andreas Färber Acked-by: David Gibson Signed-off-by: Alexander Graf --- hw/intc/xics.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index a0d71ef..666888d 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -29,6 +29,7 @@ #include "trace.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" +#include "qemu/error-report.h" void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) { @@ -48,8 +49,8 @@ void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) break; default: - fprintf(stderr, "XICS interrupt controller does not support this CPU " - "bus model\n"); + error_report("XICS interrupt controller does not support this CPU " + "bus model"); abort(); } } -- cgit v1.1 From d1b5682d88f72f8662ce6d20e07af3adfbf39ed0 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:39 +1000 Subject: xics: add pre_save/post_load dispatchers The upcoming support of in-kernel XICS will redefine migration callbacks for both ICS and ICP so classes and callback pointers are added. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/intc/xics.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++--- include/hw/ppc/xics.h | 26 ++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 666888d..eeb64f5 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -190,11 +190,35 @@ static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority) } } +static void icp_dispatch_pre_save(void *opaque) +{ + ICPState *ss = opaque; + ICPStateClass *info = ICP_GET_CLASS(ss); + + if (info->pre_save) { + info->pre_save(ss); + } +} + +static int icp_dispatch_post_load(void *opaque, int version_id) +{ + ICPState *ss = opaque; + ICPStateClass *info = ICP_GET_CLASS(ss); + + if (info->post_load) { + return info->post_load(ss, version_id); + } + + return 0; +} + static const VMStateDescription vmstate_icp_server = { .name = "icp/server", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, + .pre_save = icp_dispatch_pre_save, + .post_load = icp_dispatch_post_load, .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32(xirr, ICPState), @@ -229,6 +253,7 @@ static TypeInfo icp_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(ICPState), .class_init = icp_class_init, + .class_size = sizeof(ICPStateClass), }; /* @@ -390,10 +415,9 @@ static void ics_reset(DeviceState *dev) } } -static int ics_post_load(void *opaque, int version_id) +static int ics_post_load(ICSState *ics, int version_id) { int i; - ICSState *ics = opaque; for (i = 0; i < ics->icp->nr_servers; i++) { icp_resend(ics->icp, i); @@ -402,6 +426,28 @@ static int ics_post_load(void *opaque, int version_id) return 0; } +static void ics_dispatch_pre_save(void *opaque) +{ + ICSState *ics = opaque; + ICSStateClass *info = ICS_GET_CLASS(ics); + + if (info->pre_save) { + info->pre_save(ics); + } +} + +static int ics_dispatch_post_load(void *opaque, int version_id) +{ + ICSState *ics = opaque; + ICSStateClass *info = ICS_GET_CLASS(ics); + + if (info->post_load) { + return info->post_load(ics, version_id); + } + + return 0; +} + static const VMStateDescription vmstate_ics_irq = { .name = "ics/irq", .version_id = 1, @@ -421,7 +467,8 @@ static const VMStateDescription vmstate_ics = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, - .post_load = ics_post_load, + .pre_save = ics_dispatch_pre_save, + .post_load = ics_dispatch_post_load, .fields = (VMStateField []) { /* Sanity check */ VMSTATE_UINT32_EQUAL(nr_irqs, ICSState), @@ -446,10 +493,12 @@ static int ics_realize(DeviceState *dev) static void ics_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *isc = ICS_CLASS(klass); dc->init = ics_realize; dc->vmsd = &vmstate_ics; dc->reset = ics_reset; + isc->post_load = ics_post_load; } static TypeInfo ics_info = { @@ -457,6 +506,7 @@ static TypeInfo ics_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(ICSState), .class_init = ics_class_init, + .class_size = sizeof(ICSStateClass), }; /* diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 66364c5..6e3b605 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -42,7 +42,9 @@ * that yet) */ typedef struct XICSState XICSState; +typedef struct ICPStateClass ICPStateClass; typedef struct ICPState ICPState; +typedef struct ICSStateClass ICSStateClass; typedef struct ICSState ICSState; typedef struct ICSIRQState ICSIRQState; @@ -59,6 +61,18 @@ struct XICSState { #define TYPE_ICP "icp" #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) +#define ICP_CLASS(klass) \ + OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) +#define ICP_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ICPStateClass, (obj), TYPE_ICP) + +struct ICPStateClass { + DeviceClass parent_class; + + void (*pre_save)(ICPState *s); + int (*post_load)(ICPState *s, int version_id); +}; + struct ICPState { /*< private >*/ DeviceState parent_obj; @@ -72,6 +86,18 @@ struct ICPState { #define TYPE_ICS "ics" #define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS) +#define ICS_CLASS(klass) \ + OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS) +#define ICS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ICSStateClass, (obj), TYPE_ICS) + +struct ICSStateClass { + DeviceClass parent_class; + + void (*pre_save)(ICSState *s); + int (*post_load)(ICSState *s, int version_id); +}; + struct ICSState { /*< private >*/ DeviceState parent_obj; -- cgit v1.1 From b45ff2d942022d7ee139a153f17f638d87935e03 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:40 +1000 Subject: xics: convert init() to realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes XICS according new QOM rules. This converts ICS's init() callbacks to realize(). This converts legacy qdev_init_nofail() to property_set(realized). Signed-off-by: Alexey Kardashevskiy Reviewed-by: Andreas Färber Signed-off-by: Alexander Graf --- hw/intc/xics.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index eeb64f5..76654db 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -479,15 +479,17 @@ static const VMStateDescription vmstate_ics = { }, }; -static int ics_realize(DeviceState *dev) +static void ics_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS(dev); + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; + } ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs); - - return 0; } static void ics_class_init(ObjectClass *klass, void *data) @@ -495,7 +497,7 @@ static void ics_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); - dc->init = ics_realize; + dc->realize = ics_realize; dc->vmsd = &vmstate_ics; dc->reset = ics_reset; isc->post_load = ics_post_load; @@ -691,8 +693,14 @@ static void xics_realize(DeviceState *dev, Error **errp) { XICSState *icp = XICS(dev); ICSState *ics = icp->ics; + Error *error = NULL; int i; + if (!icp->nr_servers) { + error_setg(errp, "Number of servers needs to be greater 0"); + return; + } + /* Registration of global state belongs into realize */ spapr_rtas_register("ibm,set-xive", rtas_set_xive); spapr_rtas_register("ibm,get-xive", rtas_get_xive); @@ -707,7 +715,11 @@ static void xics_realize(DeviceState *dev, Error **errp) ics->nr_irqs = icp->nr_irqs; ics->offset = XICS_IRQ_BASE; ics->icp = icp; - qdev_init_nofail(DEVICE(ics)); + object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); for (i = 0; i < icp->nr_servers; i++) { @@ -715,7 +727,11 @@ static void xics_realize(DeviceState *dev, Error **errp) object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); snprintf(buffer, sizeof(buffer), "icp[%d]", i); object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL); - qdev_init_nofail(DEVICE(&icp->ss[i])); + object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); + if (error) { + error_propagate(errp, error); + return; + } } } -- cgit v1.1 From 456df19cf7fd7f6d9ce986a3fb8f7603df5c3b22 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:41 +1000 Subject: xics: add missing const specifiers to TypeInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds missing const specifiers to ICS and ICP TypeInfo's. Signed-off-by: Alexey Kardashevskiy Reviewed-by: Andreas Färber Signed-off-by: Alexander Graf --- hw/intc/xics.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 76654db..c90eb0a 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -248,7 +248,7 @@ static void icp_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_icp_server; } -static TypeInfo icp_info = { +static const TypeInfo icp_info = { .name = TYPE_ICP, .parent = TYPE_DEVICE, .instance_size = sizeof(ICPState), @@ -503,7 +503,7 @@ static void ics_class_init(ObjectClass *klass, void *data) isc->post_load = ics_post_load; } -static TypeInfo ics_info = { +static const TypeInfo ics_info = { .name = TYPE_ICS, .parent = TYPE_DEVICE, .instance_size = sizeof(ICSState), -- cgit v1.1 From 5a3d7b23ba41b4884b43b6bc936ea18f999d5c6b Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:42 +1000 Subject: xics: split to xics and xics-common The upcoming XICS-KVM support will use bits of emulated XICS code. So this introduces new level of hierarchy - "xics-common" class. Both emulated XICS and XICS-KVM will inherit from it and override class callbacks when required. The new "xics-common" class implements: 1. replaces static "nr_irqs" and "nr_servers" properties with the dynamic ones and adds callbacks to be executed when properties are set. 2. xics_cpu_setup() callback renamed to xics_common_cpu_setup() as it is a common part for both XICS'es 3. xics_reset() renamed to xics_common_reset() for the same reason. The emulated XICS changes: 1. the part of xics_realize() which creates ICPs is moved to the "nr_servers" property callback as realize() is too late to create/initialize devices and instance_init() is too early to create devices as the number of child devices comes via the "nr_servers" property. 2. added ics_initfn() which does a little part of what xics_realize() did. Signed-off-by: Alexey Kardashevskiy Reviewed-by: Alexander Graf Signed-off-by: Alexander Graf --- hw/intc/xics.c | 156 +++++++++++++++++++++++++++++++++++++++++++------- hw/ppc/spapr.c | 2 +- include/hw/ppc/xics.h | 20 +++++++ 3 files changed, 157 insertions(+), 21 deletions(-) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index c90eb0a..5ed2618 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -30,6 +30,7 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" #include "qemu/error-report.h" +#include "qapi/visitor.h" void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) { @@ -55,9 +56,12 @@ void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) } } -static void xics_reset(DeviceState *d) +/* + * XICS Common class - parent for emulated XICS and KVM-XICS + */ +static void xics_common_reset(DeviceState *d) { - XICSState *icp = XICS(d); + XICSState *icp = XICS_COMMON(d); int i; for (i = 0; i < icp->nr_servers; i++) { @@ -67,6 +71,99 @@ static void xics_reset(DeviceState *d) device_reset(DEVICE(icp->ics)); } +static void xics_prop_get_nr_irqs(Object *obj, Visitor *v, + void *opaque, const char *name, Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + int64_t value = icp->nr_irqs; + + visit_type_int(v, &value, name, errp); +} + +static void xics_prop_set_nr_irqs(Object *obj, Visitor *v, + void *opaque, const char *name, Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); + Error *error = NULL; + int64_t value; + + visit_type_int(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + if (icp->nr_irqs) { + error_setg(errp, "Number of interrupts is already set to %u", + icp->nr_irqs); + return; + } + + assert(info->set_nr_irqs); + assert(icp->ics); + info->set_nr_irqs(icp, value, errp); +} + +static void xics_prop_get_nr_servers(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + int64_t value = icp->nr_servers; + + visit_type_int(v, &value, name, errp); +} + +static void xics_prop_set_nr_servers(Object *obj, Visitor *v, + void *opaque, const char *name, + Error **errp) +{ + XICSState *icp = XICS_COMMON(obj); + XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); + Error *error = NULL; + int64_t value; + + visit_type_int(v, &value, name, &error); + if (error) { + error_propagate(errp, error); + return; + } + if (icp->nr_servers) { + error_setg(errp, "Number of servers is already set to %u", + icp->nr_servers); + return; + } + + assert(info->set_nr_servers); + info->set_nr_servers(icp, value, errp); +} + +static void xics_common_initfn(Object *obj) +{ + object_property_add(obj, "nr_irqs", "int", + xics_prop_get_nr_irqs, xics_prop_set_nr_irqs, + NULL, NULL, NULL); + object_property_add(obj, "nr_servers", "int", + xics_prop_get_nr_servers, xics_prop_set_nr_servers, + NULL, NULL, NULL); +} + +static void xics_common_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->reset = xics_common_reset; +} + +static const TypeInfo xics_common_info = { + .name = TYPE_XICS_COMMON, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XICSState), + .class_size = sizeof(XICSStateClass), + .instance_init = xics_common_initfn, + .class_init = xics_common_class_init, +}; + /* * ICP: Presentation layer */ @@ -479,6 +576,13 @@ static const VMStateDescription vmstate_ics = { }, }; +static void ics_initfn(Object *obj) +{ + ICSState *ics = ICS(obj); + + ics->offset = XICS_IRQ_BASE; +} + static void ics_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS(dev); @@ -509,6 +613,7 @@ static const TypeInfo ics_info = { .instance_size = sizeof(ICSState), .class_init = ics_class_init, .class_size = sizeof(ICSStateClass), + .instance_init = ics_initfn, }; /* @@ -689,10 +794,31 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr, * XICS */ +static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) +{ + icp->nr_irqs = icp->ics->nr_irqs = nr_irqs; +} + +static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers, + Error **errp) +{ + int i; + + icp->nr_servers = nr_servers; + + icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); + for (i = 0; i < icp->nr_servers; i++) { + char buffer[32]; + object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); + snprintf(buffer, sizeof(buffer), "icp[%d]", i); + object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), + errp); + } +} + static void xics_realize(DeviceState *dev, Error **errp) { XICSState *icp = XICS(dev); - ICSState *ics = icp->ics; Error *error = NULL; int i; @@ -712,21 +838,13 @@ static void xics_realize(DeviceState *dev, Error **errp) spapr_register_hypercall(H_XIRR, h_xirr); spapr_register_hypercall(H_EOI, h_eoi); - ics->nr_irqs = icp->nr_irqs; - ics->offset = XICS_IRQ_BASE; - ics->icp = icp; object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); if (error) { error_propagate(errp, error); return; } - icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); for (i = 0; i < icp->nr_servers; i++) { - char buffer[32]; - object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP); - snprintf(buffer, sizeof(buffer), "icp[%d]", i); - object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL); object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); if (error) { error_propagate(errp, error); @@ -741,33 +859,31 @@ static void xics_initfn(Object *obj) xics->ics = ICS(object_new(TYPE_ICS)); object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); + xics->ics->icp = xics; } -static Property xics_properties[] = { - DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1), - DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1), - DEFINE_PROP_END_OF_LIST(), -}; - static void xics_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + XICSStateClass *xsc = XICS_CLASS(oc); dc->realize = xics_realize; - dc->props = xics_properties; - dc->reset = xics_reset; + xsc->set_nr_irqs = xics_set_nr_irqs; + xsc->set_nr_servers = xics_set_nr_servers; } static const TypeInfo xics_info = { .name = TYPE_XICS, - .parent = TYPE_SYS_BUS_DEVICE, + .parent = TYPE_XICS_COMMON, .instance_size = sizeof(XICSState), + .class_size = sizeof(XICSStateClass), .class_init = xics_class_init, .instance_init = xics_initfn, }; static void xics_register_types(void) { + type_register_static(&xics_common_info); type_register_static(&xics_info); type_register_static(&ics_info); type_register_static(&icp_info); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index a276377..f65dadc 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -161,7 +161,7 @@ static XICSState *try_create_xics(const char *type, int nr_servers, return NULL; } - return XICS(dev); + return XICS_COMMON(dev); } static XICSState *xics_system_init(int nr_servers, int nr_irqs) diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 6e3b605..7e702a0 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -29,9 +29,21 @@ #include "hw/sysbus.h" +#define TYPE_XICS_COMMON "xics-common" +#define XICS_COMMON(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_COMMON) + #define TYPE_XICS "xics" #define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS) +#define XICS_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON) +#define XICS_CLASS(klass) \ + OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS) +#define XICS_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON) +#define XICS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS) + #define XICS_IPI 0x2 #define XICS_BUID 0x1 #define XICS_IRQ_BASE (XICS_BUID << 12) @@ -41,6 +53,7 @@ * (the kernel implementation supports more but we don't exploit * that yet) */ +typedef struct XICSStateClass XICSStateClass; typedef struct XICSState XICSState; typedef struct ICPStateClass ICPStateClass; typedef struct ICPState ICPState; @@ -48,6 +61,13 @@ typedef struct ICSStateClass ICSStateClass; typedef struct ICSState ICSState; typedef struct ICSIRQState ICSIRQState; +struct XICSStateClass { + DeviceClass parent_class; + + void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp); + void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp); +}; + struct XICSState { /*< private >*/ SysBusDevice parent_obj; -- cgit v1.1 From 5eb92ccc3f23f958c0d21bed7c22abe6c1f1adda Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:43 +1000 Subject: xics: add cpu_setup callback This adds a cpu_setup callback to the XICS device class (as XICS-KVM will do it different), xics_cpu_setup() will call it if it is set. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/intc/xics.c | 5 +++++ include/hw/ppc/xics.h | 1 + 2 files changed, 6 insertions(+) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 5ed2618..1c6e6f5 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -37,9 +37,14 @@ void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; ICPState *ss = &icp->ss[cs->cpu_index]; + XICSStateClass *info = XICS_COMMON_GET_CLASS(icp); assert(cs->cpu_index < icp->nr_servers); + if (info->cpu_setup) { + info->cpu_setup(icp, cpu); + } + switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_POWER7: ss->output = env->irq_inputs[POWER7_INPUT_INT]; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 7e702a0..343bba8 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -64,6 +64,7 @@ typedef struct ICSIRQState ICSIRQState; struct XICSStateClass { DeviceClass parent_class; + void (*cpu_setup)(XICSState *icp, PowerPCCPU *cpu); void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp); void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp); }; -- cgit v1.1 From 11ad93f68195f68cc94d988f2aa50b4d190ee52a Mon Sep 17 00:00:00 2001 From: David Gibson Date: Thu, 26 Sep 2013 16:18:44 +1000 Subject: xics-kvm: Support for in-kernel XICS interrupt controller Recent (host) kernels support emulating the PAPR defined "XICS" interrupt controller system within KVM. This patch allows qemu to initialize and configure the in-kernel XICS, and keep its state in sync with qemu's XICS state as necessary. This should give considerable performance improvements. e.g. on a simple IPI ping-pong test between hardware threads, using qemu XICS gives us around 5,000 irqs/second, whereas the in-kernel XICS gives us around 70,000 irqs/s on the same hardware configuration. Signed-off-by: David Gibson [Mike Qiu : fixed mistype which caused ics_set_kvm_state() to fail] Signed-off-by: Alexey Kardashevskiy Reviewed-by: Alexander Graf Signed-off-by: Alexander Graf --- default-configs/ppc64-softmmu.mak | 1 + hw/intc/Makefile.objs | 1 + hw/intc/xics_kvm.c | 488 ++++++++++++++++++++++++++++++++++++++ hw/ppc/spapr.c | 21 +- include/hw/ppc/xics.h | 10 + 5 files changed, 520 insertions(+), 1 deletion(-) create mode 100644 hw/intc/xics_kvm.c diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index 975112a..fb34a9b 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -46,6 +46,7 @@ CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) # For pSeries CONFIG_XICS=$(CONFIG_PSERIES) +CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM)) # For PReP CONFIG_I82378=y CONFIG_I8259=y diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 2851eed..47ac442 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -23,3 +23,4 @@ obj-$(CONFIG_OMAP) += omap_intc.o obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o +obj-$(CONFIG_XICS_KVM) += xics_kvm.o diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c new file mode 100644 index 0000000..a2ccafa --- /dev/null +++ b/hw/intc/xics_kvm.c @@ -0,0 +1,488 @@ +/* + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator + * + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics, in-kernel emulation + * + * Copyright (c) 2013 David Gibson, IBM Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "hw/hw.h" +#include "trace.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/xics.h" +#include "kvm_ppc.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" + +#include + +typedef struct KVMXICSState { + XICSState parent_obj; + + uint32_t set_xive_token; + uint32_t get_xive_token; + uint32_t int_off_token; + uint32_t int_on_token; + int kernel_xics_fd; +} KVMXICSState; + +/* + * ICP-KVM + */ +static void icp_get_kvm_state(ICPState *ss) +{ + uint64_t state; + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_ICP_STATE, + .addr = (uintptr_t)&state, + }; + int ret; + + /* ICP for this CPU thread is not in use, exiting */ + if (!ss->cs) { + return; + } + + ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve KVM interrupt controller state" + " for CPU %d: %s", ss->cs->cpu_index, strerror(errno)); + exit(1); + } + + ss->xirr = state >> KVM_REG_PPC_ICP_XISR_SHIFT; + ss->mfrr = (state >> KVM_REG_PPC_ICP_MFRR_SHIFT) + & KVM_REG_PPC_ICP_MFRR_MASK; + ss->pending_priority = (state >> KVM_REG_PPC_ICP_PPRI_SHIFT) + & KVM_REG_PPC_ICP_PPRI_MASK; +} + +static int icp_set_kvm_state(ICPState *ss, int version_id) +{ + uint64_t state; + struct kvm_one_reg reg = { + .id = KVM_REG_PPC_ICP_STATE, + .addr = (uintptr_t)&state, + }; + int ret; + + /* ICP for this CPU thread is not in use, exiting */ + if (!ss->cs) { + return 0; + } + + state = ((uint64_t)ss->xirr << KVM_REG_PPC_ICP_XISR_SHIFT) + | ((uint64_t)ss->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT) + | ((uint64_t)ss->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT); + + ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to restore KVM interrupt controller state (0x%" + PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index, + strerror(errno)); + return ret; + } + + return 0; +} + +static void icp_kvm_reset(DeviceState *dev) +{ + ICPState *icp = ICP(dev); + + icp->xirr = 0; + icp->pending_priority = 0xff; + icp->mfrr = 0xff; + + /* Make all outputs are deasserted */ + qemu_set_irq(icp->output, 0); + + icp_set_kvm_state(icp, 1); +} + +static void icp_kvm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICPStateClass *icpc = ICP_CLASS(klass); + + dc->reset = icp_kvm_reset; + icpc->pre_save = icp_get_kvm_state; + icpc->post_load = icp_set_kvm_state; +} + +static const TypeInfo icp_kvm_info = { + .name = TYPE_KVM_ICP, + .parent = TYPE_ICP, + .instance_size = sizeof(ICPState), + .class_init = icp_kvm_class_init, + .class_size = sizeof(ICPStateClass), +}; + +/* + * ICS-KVM + */ +static void ics_get_kvm_state(ICSState *ics) +{ + KVMXICSState *icpkvm = KVM_XICS(ics->icp); + uint64_t state; + struct kvm_device_attr attr = { + .flags = 0, + .group = KVM_DEV_XICS_GRP_SOURCES, + .addr = (uint64_t)(uintptr_t)&state, + }; + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = &ics->irqs[i]; + int ret; + + attr.attr = i + ics->offset; + + ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr); + if (ret != 0) { + error_report("Unable to retrieve KVM interrupt controller state" + " for IRQ %d: %s", i + ics->offset, strerror(errno)); + exit(1); + } + + irq->server = state & KVM_XICS_DESTINATION_MASK; + irq->saved_priority = (state >> KVM_XICS_PRIORITY_SHIFT) + & KVM_XICS_PRIORITY_MASK; + /* + * To be consistent with the software emulation in xics.c, we + * split out the masked state + priority that we get from the + * kernel into 'current priority' (0xff if masked) and + * 'saved priority' (if masked, this is the priority the + * interrupt had before it was masked). Masking and unmasking + * are done with the ibm,int-off and ibm,int-on RTAS calls. + */ + if (state & KVM_XICS_MASKED) { + irq->priority = 0xff; + } else { + irq->priority = irq->saved_priority; + } + + if (state & KVM_XICS_PENDING) { + if (state & KVM_XICS_LEVEL_SENSITIVE) { + irq->status |= XICS_STATUS_ASSERTED; + } else { + /* + * A pending edge-triggered interrupt (or MSI) + * must have been rejected previously when we + * first detected it and tried to deliver it, + * so mark it as pending and previously rejected + * for consistency with how xics.c works. + */ + irq->status |= XICS_STATUS_MASKED_PENDING + | XICS_STATUS_REJECTED; + } + } + } +} + +static int ics_set_kvm_state(ICSState *ics, int version_id) +{ + KVMXICSState *icpkvm = KVM_XICS(ics->icp); + uint64_t state; + struct kvm_device_attr attr = { + .flags = 0, + .group = KVM_DEV_XICS_GRP_SOURCES, + .addr = (uint64_t)(uintptr_t)&state, + }; + int i; + + for (i = 0; i < ics->nr_irqs; i++) { + ICSIRQState *irq = &ics->irqs[i]; + int ret; + + attr.attr = i + ics->offset; + + state = irq->server; + state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) + << KVM_XICS_PRIORITY_SHIFT; + if (irq->priority != irq->saved_priority) { + assert(irq->priority == 0xff); + state |= KVM_XICS_MASKED; + } + + if (ics->islsi[i]) { + state |= KVM_XICS_LEVEL_SENSITIVE; + if (irq->status & XICS_STATUS_ASSERTED) { + state |= KVM_XICS_PENDING; + } + } else { + if (irq->status & XICS_STATUS_MASKED_PENDING) { + state |= KVM_XICS_PENDING; + } + } + + ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr); + if (ret != 0) { + error_report("Unable to restore KVM interrupt controller state" + " for IRQs %d: %s", i + ics->offset, strerror(errno)); + return ret; + } + } + + return 0; +} + +static void ics_kvm_set_irq(void *opaque, int srcno, int val) +{ + ICSState *ics = opaque; + struct kvm_irq_level args; + int rc; + + args.irq = srcno + ics->offset; + if (!ics->islsi[srcno]) { + if (!val) { + return; + } + args.level = KVM_INTERRUPT_SET; + } else { + args.level = val ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET; + } + rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args); + if (rc < 0) { + perror("kvm_irq_line"); + } +} + +static void ics_kvm_reset(DeviceState *dev) +{ + ics_set_kvm_state(ICS(dev), 1); +} + +static void ics_kvm_realize(DeviceState *dev, Error **errp) +{ + ICSState *ics = ICS(dev); + + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; + } + ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); + ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool)); + ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); +} + +static void ics_kvm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ICSStateClass *icsc = ICS_CLASS(klass); + + dc->realize = ics_kvm_realize; + dc->reset = ics_kvm_reset; + icsc->pre_save = ics_get_kvm_state; + icsc->post_load = ics_set_kvm_state; +} + +static const TypeInfo ics_kvm_info = { + .name = TYPE_KVM_ICS, + .parent = TYPE_ICS, + .instance_size = sizeof(ICSState), + .class_init = ics_kvm_class_init, +}; + +/* + * XICS-KVM + */ +static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu) +{ + CPUState *cs; + ICPState *ss; + KVMXICSState *icpkvm = KVM_XICS(icp); + + cs = CPU(cpu); + ss = &icp->ss[cs->cpu_index]; + + assert(cs->cpu_index < icp->nr_servers); + if (icpkvm->kernel_xics_fd == -1) { + abort(); + } + + if (icpkvm->kernel_xics_fd != -1) { + int ret; + struct kvm_enable_cap xics_enable_cap = { + .cap = KVM_CAP_IRQ_XICS, + .flags = 0, + .args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0}, + }; + + ss->cs = cs; + + ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap); + if (ret < 0) { + error_report("Unable to connect CPU%d to kernel XICS: %s", + cs->cpu_index, strerror(errno)); + exit(1); + } + } +} + +static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp) +{ + icp->nr_irqs = icp->ics->nr_irqs = nr_irqs; +} + +static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers, + Error **errp) +{ + int i; + + icp->nr_servers = nr_servers; + + icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState)); + for (i = 0; i < icp->nr_servers; i++) { + char buffer[32]; + object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP); + snprintf(buffer, sizeof(buffer), "icp[%d]", i); + object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), + errp); + } +} + +static void rtas_dummy(PowerPCCPU *cpu, sPAPREnvironment *spapr, + uint32_t token, + uint32_t nargs, target_ulong args, + uint32_t nret, target_ulong rets) +{ + error_report("pseries: %s must never be called for in-kernel XICS", + __func__); +} + +static void xics_kvm_realize(DeviceState *dev, Error **errp) +{ + KVMXICSState *icpkvm = KVM_XICS(dev); + XICSState *icp = XICS_COMMON(dev); + int i, rc; + Error *error = NULL; + struct kvm_create_device xics_create_device = { + .type = KVM_DEV_TYPE_XICS, + .flags = 0, + }; + + if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) { + error_setg(errp, + "KVM and IRQ_XICS capability must be present for in-kernel XICS"); + goto fail; + } + + icpkvm->set_xive_token = spapr_rtas_register("ibm,set-xive", rtas_dummy); + icpkvm->get_xive_token = spapr_rtas_register("ibm,get-xive", rtas_dummy); + icpkvm->int_off_token = spapr_rtas_register("ibm,int-off", rtas_dummy); + icpkvm->int_on_token = spapr_rtas_register("ibm,int-on", rtas_dummy); + + rc = kvmppc_define_rtas_kernel_token(icpkvm->set_xive_token, + "ibm,set-xive"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive"); + goto fail; + } + + rc = kvmppc_define_rtas_kernel_token(icpkvm->get_xive_token, + "ibm,get-xive"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive"); + goto fail; + } + + rc = kvmppc_define_rtas_kernel_token(icpkvm->int_on_token, "ibm,int-on"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on"); + goto fail; + } + + rc = kvmppc_define_rtas_kernel_token(icpkvm->int_off_token, "ibm,int-off"); + if (rc < 0) { + error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off"); + goto fail; + } + + /* Create the kernel ICP */ + rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device); + if (rc < 0) { + error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS"); + goto fail; + } + + icpkvm->kernel_xics_fd = xics_create_device.fd; + + object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); + if (error) { + error_propagate(errp, error); + goto fail; + } + + assert(icp->nr_servers); + for (i = 0; i < icp->nr_servers; i++) { + object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error); + if (error) { + error_propagate(errp, error); + goto fail; + } + } + return; + +fail: + kvmppc_define_rtas_kernel_token(0, "ibm,set-xive"); + kvmppc_define_rtas_kernel_token(0, "ibm,get-xive"); + kvmppc_define_rtas_kernel_token(0, "ibm,int-on"); + kvmppc_define_rtas_kernel_token(0, "ibm,int-off"); +} + +static void xics_kvm_initfn(Object *obj) +{ + XICSState *xics = XICS_COMMON(obj); + + xics->ics = ICS(object_new(TYPE_KVM_ICS)); + object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL); + xics->ics->icp = xics; +} + +static void xics_kvm_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + XICSStateClass *xsc = XICS_COMMON_CLASS(oc); + + dc->realize = xics_kvm_realize; + xsc->cpu_setup = xics_kvm_cpu_setup; + xsc->set_nr_irqs = xics_kvm_set_nr_irqs; + xsc->set_nr_servers = xics_kvm_set_nr_servers; +} + +static const TypeInfo xics_kvm_info = { + .name = TYPE_KVM_XICS, + .parent = TYPE_XICS_COMMON, + .instance_size = sizeof(KVMXICSState), + .class_init = xics_kvm_class_init, + .instance_init = xics_kvm_initfn, +}; + +static void xics_kvm_register_types(void) +{ + type_register_static(&xics_kvm_info); + type_register_static(&ics_kvm_info); + type_register_static(&icp_kvm_info); +} + +type_init(xics_kvm_register_types) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index f65dadc..c0613e4 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -168,7 +168,26 @@ static XICSState *xics_system_init(int nr_servers, int nr_irqs) { XICSState *icp = NULL; - icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); + if (kvm_enabled()) { + QemuOpts *machine_opts = qemu_get_machine_opts(); + bool irqchip_allowed = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", true); + bool irqchip_required = qemu_opt_get_bool(machine_opts, + "kernel_irqchip", false); + if (irqchip_allowed) { + icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs); + } + + if (irqchip_required && !icp) { + perror("Failed to create in-kernel XICS\n"); + abort(); + } + } + + if (!icp) { + icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs); + } + if (!icp) { perror("Failed to create XICS\n"); abort(); diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 343bba8..0d7673d 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -35,6 +35,9 @@ #define TYPE_XICS "xics" #define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS) +#define TYPE_KVM_XICS "xics-kvm" +#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_KVM_XICS) + #define XICS_COMMON_CLASS(klass) \ OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON) #define XICS_CLASS(klass) \ @@ -82,6 +85,9 @@ struct XICSState { #define TYPE_ICP "icp" #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) +#define TYPE_KVM_ICP "icp-kvm" +#define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) + #define ICP_CLASS(klass) \ OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) #define ICP_GET_CLASS(obj) \ @@ -98,6 +104,7 @@ struct ICPState { /*< private >*/ DeviceState parent_obj; /*< public >*/ + CPUState *cs; uint32_t xirr; uint8_t pending_priority; uint8_t mfrr; @@ -107,6 +114,9 @@ struct ICPState { #define TYPE_ICS "ics" #define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS) +#define TYPE_KVM_ICS "icskvm" +#define KVM_ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_KVM_ICS) + #define ICS_CLASS(klass) \ OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS) #define ICS_GET_CLASS(obj) \ -- cgit v1.1 From 075edbe3bad4c22995ab472c507565b48c4e0985 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 26 Sep 2013 16:18:45 +1000 Subject: xics: Implement H_IPOLL This adds support for the H_IPOLL hypercall which the guest uses to poll for a pending interrupt. This hypercall is mandatory for PAPR+ and there is no way for the guest to detect whether it is supported or not so just add it. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Alexey Kardashevskiy Acked-by: Alexander Graf Signed-off-by: Alexander Graf --- hw/intc/xics.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 1c6e6f5..eb93276 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -689,6 +689,18 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + ICPState *ss = &spapr->icp->ss[cs->cpu_index]; + + args[0] = ss->xirr; + args[1] = ss->mfrr; + + return H_SUCCESS; +} + static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -842,6 +854,7 @@ static void xics_realize(DeviceState *dev, Error **errp) spapr_register_hypercall(H_IPI, h_ipi); spapr_register_hypercall(H_XIRR, h_xirr); spapr_register_hypercall(H_EOI, h_eoi); + spapr_register_hypercall(H_IPOLL, h_ipoll); object_property_set_bool(OBJECT(icp->ics), true, "realized", &error); if (error) { -- cgit v1.1 From 5d87e4b74a0100fbb7970edaa3449eb04b06f782 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Thu, 26 Sep 2013 16:18:46 +1000 Subject: xics: Implement H_XIRR_X This implements H_XIRR_X hypercall in addition to H_XIRR as it is mandatory for PAPR+ and there is no way for the guest to detect whether it is supported or not so just add it. As the Partition Adjunct Option is not supported at the moment, the CPPR parameter of the hypercall is ignored. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/intc/xics.c | 14 ++++++++++++++ include/hw/ppc/spapr.h | 1 + 2 files changed, 15 insertions(+) diff --git a/hw/intc/xics.c b/hw/intc/xics.c index eb93276..a333305 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -27,6 +27,7 @@ #include "hw/hw.h" #include "trace.h" +#include "qemu/timer.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" #include "qemu/error-report.h" @@ -679,6 +680,18 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr, return H_SUCCESS; } +static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPREnvironment *spapr, + target_ulong opcode, target_ulong *args) +{ + CPUState *cs = CPU(cpu); + ICPState *ss = &spapr->icp->ss[cs->cpu_index]; + uint32_t xirr = icp_accept(ss); + + args[0] = xirr; + args[1] = cpu_get_real_ticks(); + return H_SUCCESS; +} + static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr, target_ulong opcode, target_ulong *args) { @@ -853,6 +866,7 @@ static void xics_realize(DeviceState *dev, Error **errp) spapr_register_hypercall(H_CPPR, h_cppr); spapr_register_hypercall(H_IPI, h_ipi); spapr_register_hypercall(H_XIRR, h_xirr); + spapr_register_hypercall(H_XIRR_X, h_xirr_x); spapr_register_hypercall(H_EOI, h_eoi); spapr_register_hypercall(H_IPOLL, h_ipoll); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 6407c8a..5ae0b58 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -283,6 +283,7 @@ typedef struct sPAPREnvironment { #define H_GET_EM_PARMS 0x2B8 #define H_SET_MPP 0x2D0 #define H_GET_MPP 0x2D4 +#define H_XIRR_X 0x2FC #define H_SET_MODE 0x31C #define MAX_HCALL_OPCODE H_SET_MODE -- cgit v1.1 From 9554233c9b8fe7d94dfa53db09ce3d186f2e8b9e Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:47 +1000 Subject: xics-kvm: enable irqfd for MSI This enables IRQFD support for sPAPR. The feature decreases the latency of interrupt handling. To enable IRQFD for MSI, this sets kvm_gsi_direct_mapping to true which enables direct MSI mapping. To enable IRQFD for LSI (level triggered INTx interrupts), a PCI host bus callback is required. The patch for that is coming next. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/intc/xics_kvm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index a2ccafa..c203646 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -441,6 +441,12 @@ static void xics_kvm_realize(DeviceState *dev, Error **errp) goto fail; } } + + kvm_kernel_irqchip = true; + kvm_irqfds_allowed = true; + kvm_msi_via_irqfd_allowed = true; + kvm_gsi_direct_mapping = true; + return; fail: -- cgit v1.1 From 5cc7a967e9de8c7b16c15aee4cb9f5bfcf0c5989 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 26 Sep 2013 16:18:48 +1000 Subject: spapr-pci: enable irqfd for INTx This enables IRQFD for LSI (level triggered INTx interrupts) by adding a spapr_route_intx_pin_to_irq() callback to the sPAPR PCI host bus. This callback is called to know the global interrupt number to link resampling fd with IRQFD's fd in KVM. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Alexander Graf --- hw/ppc/spapr_pci.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 9b6ee32..edb4cb0 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -432,6 +432,17 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); } +static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin) +{ + sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque); + PCIINTxRoute route; + + route.mode = PCI_INTX_ENABLED; + route.irq = sphb->lsi_table[pin].irq; + + return route; +} + /* * MSI/MSIX memory region implementation. * The handler handles both MSI and MSIX. @@ -610,6 +621,8 @@ static int spapr_phb_init(SysBusDevice *s) pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb); + pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq); + QLIST_INSERT_HEAD(&spapr->phbs, sphb, list); /* Initialize the LSI table */ -- cgit v1.1 From 4b4d4a21b988f6d56e0792058aa0b968e19fda6b Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 1 Oct 2013 21:49:28 +0530 Subject: target-ppc: Update slb array with correct index values. Without this, a value of rb=0 and rs=0 results in replacing the 0th index. This can be observed when using gdb remote debugging support. (gdb) x/10i do_fork 0xc000000000085330 : Cannot access memory at address 0xc000000000085330 (gdb) This is because when we do the slb sync via kvm_cpu_synchronize_state, we overwrite the slb entry (0th entry) for 0xc000000000085330 Signed-off-by: Aneesh Kumar K.V Signed-off-by: Alexander Graf --- target-ppc/kvm.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 0b5d391..e2f8b03 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1033,9 +1033,22 @@ int kvm_arch_get_registers(CPUState *cs) /* Sync SLB */ #ifdef TARGET_PPC64 + /* + * The packed SLB array we get from KVM_GET_SREGS only contains + * information about valid entries. So we flush our internal + * copy to get rid of stale ones, then put all valid SLB entries + * back in. + */ + memset(env->slb, 0, sizeof(env->slb)); for (i = 0; i < 64; i++) { - ppc_store_slb(env, sregs.u.s.ppc64.slb[i].slbe, - sregs.u.s.ppc64.slb[i].slbv); + target_ulong rb = sregs.u.s.ppc64.slb[i].slbe; + target_ulong rs = sregs.u.s.ppc64.slb[i].slbv; + /* + * Only restore valid entries + */ + if (rb & SLB_ESID_V) { + ppc_store_slb(env, rb, rs); + } } #endif -- cgit v1.1 From 2f4d0f5990ede025720e41fa473029e9ca85e8b8 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 1 Oct 2013 21:49:30 +0530 Subject: target-ppc: Check for error on address translation in memsave command When we translate the virtual address to physical check for error. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Alexander Graf --- cpus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cpus.c b/cpus.c index 398229e..912938c 100644 --- a/cpus.c +++ b/cpus.c @@ -1403,7 +1403,10 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename, l = sizeof(buf); if (l > size) l = size; - cpu_memory_rw_debug(cpu, addr, buf, l, 0); + if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) { + error_setg(errp, "Invalid addr 0x%016" PRIx64 "specified", addr); + goto exit; + } if (fwrite(buf, 1, l, f) != l) { error_set(errp, QERR_IO_ERROR); goto exit; -- cgit v1.1 From d83af16786ca672bea9a206490f801bec7a057eb Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 1 Oct 2013 21:49:31 +0530 Subject: target-ppc: Use #define for max slb entries Instead of opencoding 64 use MAX_SLB_ENTRIES. We don't update the kernel header here. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Alexander Graf --- target-ppc/cpu.h | 3 ++- target-ppc/kvm.c | 4 ++-- target-ppc/machine.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 422a6bb..26acdba 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -405,6 +405,7 @@ struct ppc_slb_t { uint64_t vsid; }; +#define MAX_SLB_ENTRIES 64 #define SEGMENT_SHIFT_256M 28 #define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1)) @@ -949,7 +950,7 @@ struct CPUPPCState { #if !defined(CONFIG_USER_ONLY) #if defined(TARGET_PPC64) /* PowerPC 64 SLB area */ - ppc_slb_t slb[64]; + ppc_slb_t slb[MAX_SLB_ENTRIES]; int32_t slb_nr; #endif /* segment registers */ diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index e2f8b03..b77ce5e 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -818,7 +818,7 @@ int kvm_arch_put_registers(CPUState *cs, int level) /* Sync SLB */ #ifdef TARGET_PPC64 - for (i = 0; i < 64; i++) { + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid; sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid; } @@ -1040,7 +1040,7 @@ int kvm_arch_get_registers(CPUState *cs) * back in. */ memset(env->slb, 0, sizeof(env->slb)); - for (i = 0; i < 64; i++) { + for (i = 0; i < ARRAY_SIZE(env->slb); i++) { target_ulong rb = sregs.u.s.ppc64.slb[i].slbe; target_ulong rs = sregs.u.s.ppc64.slb[i].slbv; /* diff --git a/target-ppc/machine.c b/target-ppc/machine.c index 12e1512..12c174f 100644 --- a/target-ppc/machine.c +++ b/target-ppc/machine.c @@ -312,7 +312,7 @@ static const VMStateDescription vmstate_slb = { .minimum_version_id_old = 1, .fields = (VMStateField []) { VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU), - VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, 64), + VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES), VMSTATE_END_OF_LIST() } }; -- cgit v1.1 From bb6b684363e83586c90d20127b0d0a79793ab1e2 Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 1 Oct 2013 21:49:32 +0530 Subject: dump-guest-memory: Check for the correct return value We should check for error with s->note_size Signed-off-by: Aneesh Kumar K.V Signed-off-by: Alexander Graf --- dump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dump.c b/dump.c index 846155c..80a9116 100644 --- a/dump.c +++ b/dump.c @@ -66,7 +66,7 @@ typedef struct DumpState { uint32_t sh_info; bool have_section; bool resume; - size_t note_size; + ssize_t note_size; hwaddr memory_offset; int fd; @@ -765,7 +765,7 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter, s->note_size = cpu_get_note_size(s->dump_info.d_class, s->dump_info.d_machine, nr_cpus); - if (ret < 0) { + if (s->note_size < 0) { error_set(errp, QERR_UNSUPPORTED); goto cleanup; } -- cgit v1.1 From e62fbc54d459d4cc8e91dc0938383a7f4c13768c Mon Sep 17 00:00:00 2001 From: "Aneesh Kumar K.V" Date: Tue, 1 Oct 2013 21:49:33 +0530 Subject: target-ppc: dump-guest-memory support This patch add support for dumping guest memory using dump-guest-memory monitor command. Before patch: (qemu) dump-guest-memory testcrash this feature or command is not currently supported (qemu) After patch: (qemu) dump-guest-memory testcrash (qemu) crash was able to read the file crash> bt PID: 0 TASK: c000000000c0d0d0 CPU: 0 COMMAND: "swapper/0" R0: 0000000028000084 R1: c000000000cafa50 R2: c000000000cb05b0 R3: 0000000000000000 R4: c000000000bc4cb0 R5: 0000000000000000 R6: 001efe93b8000000 R7: 0000000000000000 R8: 0000000000000000 R9: b000000000001032 R10: 0000000000000001 R11: 0001eb2117e00d55 .... ... NOTE: Currently crash tools doesn't look at ELF notes in the dump on ppc64. Signed-off-by: Aneesh Kumar K.V Signed-off-by: Alexander Graf --- include/elf.h | 3 + target-ppc/Makefile.objs | 2 +- target-ppc/arch_dump.c | 253 ++++++++++++++++++++++++++++++++++++++++++++ target-ppc/cpu-qom.h | 5 +- target-ppc/translate_init.c | 4 + 5 files changed, 265 insertions(+), 2 deletions(-) create mode 100644 target-ppc/arch_dump.c diff --git a/include/elf.h b/include/elf.h index 58bfbf8..b818091 100644 --- a/include/elf.h +++ b/include/elf.h @@ -1359,6 +1359,9 @@ typedef struct elf64_shdr { #define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ #define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ #define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ /* Note header in a PT_NOTE section */ diff --git a/target-ppc/Makefile.objs b/target-ppc/Makefile.objs index 94d6d0c..3cb23e0 100644 --- a/target-ppc/Makefile.objs +++ b/target-ppc/Makefile.objs @@ -2,7 +2,7 @@ obj-y += cpu-models.o obj-y += translate.o ifeq ($(CONFIG_SOFTMMU),y) obj-y += machine.o mmu_helper.o mmu-hash32.o -obj-$(TARGET_PPC64) += mmu-hash64.o +obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o endif obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o diff --git a/target-ppc/arch_dump.c b/target-ppc/arch_dump.c new file mode 100644 index 0000000..17fd4c6 --- /dev/null +++ b/target-ppc/arch_dump.c @@ -0,0 +1,253 @@ +/* + * writing ELF notes for ppc64 arch + * + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Aneesh Kumar K.V + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "cpu.h" +#include "elf.h" +#include "exec/cpu-all.h" +#include "sysemu/dump.h" +#include "sysemu/kvm.h" + +struct PPC64UserRegStruct { + uint64_t gpr[32]; + uint64_t nip; + uint64_t msr; + uint64_t orig_gpr3; + uint64_t ctr; + uint64_t link; + uint64_t xer; + uint64_t ccr; + uint64_t softe; + uint64_t trap; + uint64_t dar; + uint64_t dsisr; + uint64_t result; +} QEMU_PACKED; + +struct PPC64ElfPrstatus { + char pad1[112]; + struct PPC64UserRegStruct pr_reg; + uint64_t pad2[4]; +} QEMU_PACKED; + + +struct PPC64ElfFpregset { + uint64_t fpr[32]; + uint64_t fpscr; +} QEMU_PACKED; + + +struct PPC64ElfVmxregset { + ppc_avr_t avr[32]; + ppc_avr_t vscr; + union { + ppc_avr_t unused; + uint32_t value; + } vrsave; +} QEMU_PACKED; + +struct PPC64ElfVsxregset { + uint64_t vsr[32]; +} QEMU_PACKED; + +struct PPC64ElfSperegset { + uint32_t evr[32]; + uint64_t spe_acc; + uint32_t spe_fscr; +} QEMU_PACKED; + +typedef struct noteStruct { + Elf64_Nhdr hdr; + char name[5]; + char pad3[3]; + union { + struct PPC64ElfPrstatus prstatus; + struct PPC64ElfFpregset fpregset; + struct PPC64ElfVmxregset vmxregset; + struct PPC64ElfVsxregset vsxregset; + struct PPC64ElfSperegset speregset; + } contents; +} QEMU_PACKED Note; + + +static void ppc64_write_elf64_prstatus(Note *note, PowerPCCPU *cpu) +{ + int i; + uint64_t cr; + struct PPC64ElfPrstatus *prstatus; + struct PPC64UserRegStruct *reg; + + note->hdr.n_type = cpu_to_be32(NT_PRSTATUS); + + prstatus = ¬e->contents.prstatus; + memset(prstatus, 0, sizeof(*prstatus)); + reg = &prstatus->pr_reg; + + for (i = 0; i < 32; i++) { + reg->gpr[i] = cpu_to_be64(cpu->env.gpr[i]); + } + reg->nip = cpu_to_be64(cpu->env.nip); + reg->msr = cpu_to_be64(cpu->env.msr); + reg->ctr = cpu_to_be64(cpu->env.ctr); + reg->link = cpu_to_be64(cpu->env.lr); + reg->xer = cpu_to_be64(cpu_read_xer(&cpu->env)); + + cr = 0; + for (i = 0; i < 8; i++) { + cr |= (cpu->env.crf[i] & 15) << (4 * (7 - i)); + } + reg->ccr = cpu_to_be64(cr); +} + +static void ppc64_write_elf64_fpregset(Note *note, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfFpregset *fpregset; + + note->hdr.n_type = cpu_to_be32(NT_PRFPREG); + + fpregset = ¬e->contents.fpregset; + memset(fpregset, 0, sizeof(*fpregset)); + + for (i = 0; i < 32; i++) { + fpregset->fpr[i] = cpu_to_be64(cpu->env.fpr[i]); + } + fpregset->fpscr = cpu_to_be64(cpu->env.fpscr); +} + +static void ppc64_write_elf64_vmxregset(Note *note, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfVmxregset *vmxregset; + + note->hdr.n_type = cpu_to_be32(NT_PPC_VMX); + vmxregset = ¬e->contents.vmxregset; + memset(vmxregset, 0, sizeof(*vmxregset)); + + for (i = 0; i < 32; i++) { + vmxregset->avr[i].u64[0] = cpu_to_be64(cpu->env.avr[i].u64[0]); + vmxregset->avr[i].u64[1] = cpu_to_be64(cpu->env.avr[i].u64[1]); + } + vmxregset->vscr.u32[3] = cpu_to_be32(cpu->env.vscr); +} +static void ppc64_write_elf64_vsxregset(Note *note, PowerPCCPU *cpu) +{ + int i; + struct PPC64ElfVsxregset *vsxregset; + + note->hdr.n_type = cpu_to_be32(NT_PPC_VSX); + vsxregset = ¬e->contents.vsxregset; + memset(vsxregset, 0, sizeof(*vsxregset)); + + for (i = 0; i < 32; i++) { + vsxregset->vsr[i] = cpu_to_be64(cpu->env.vsr[i]); + } +} +static void ppc64_write_elf64_speregset(Note *note, PowerPCCPU *cpu) +{ + struct PPC64ElfSperegset *speregset; + note->hdr.n_type = cpu_to_be32(NT_PPC_SPE); + speregset = ¬e->contents.speregset; + memset(speregset, 0, sizeof(*speregset)); + + speregset->spe_acc = cpu_to_be64(cpu->env.spe_acc); + speregset->spe_fscr = cpu_to_be32(cpu->env.spe_fscr); +} + +struct NoteFuncDescStruct { + int contents_size; + void (*note_contents_func)(Note *note, PowerPCCPU *cpu); +} note_func[] = { + {sizeof(((Note *)0)->contents.prstatus), ppc64_write_elf64_prstatus}, + {sizeof(((Note *)0)->contents.fpregset), ppc64_write_elf64_fpregset}, + {sizeof(((Note *)0)->contents.vmxregset), ppc64_write_elf64_vmxregset}, + {sizeof(((Note *)0)->contents.vsxregset), ppc64_write_elf64_vsxregset}, + {sizeof(((Note *)0)->contents.speregset), ppc64_write_elf64_speregset}, + { 0, NULL} +}; + +typedef struct NoteFuncDescStruct NoteFuncDesc; + +int cpu_get_dump_info(ArchDumpInfo *info, + const struct GuestPhysBlockList *guest_phys_blocks) +{ + /* + * Currently only handling PPC64 big endian. + */ + info->d_machine = EM_PPC64; + info->d_endian = ELFDATA2MSB; + info->d_class = ELFCLASS64; + + return 0; +} + +ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) +{ + int name_size = 8; /* "CORE" or "QEMU" rounded */ + size_t elf_note_size = 0; + int note_head_size; + NoteFuncDesc *nf; + + if (class != ELFCLASS64) { + return -1; + } + assert(machine == EM_PPC64); + + note_head_size = sizeof(Elf64_Nhdr); + + for (nf = note_func; nf->note_contents_func; nf++) { + elf_note_size = elf_note_size + note_head_size + name_size + + nf->contents_size; + } + + return (elf_note_size) * nr_cpus; +} + +static int ppc64_write_all_elf64_notes(const char *note_name, + WriteCoreDumpFunction f, + PowerPCCPU *cpu, int id, + void *opaque) +{ + Note note; + int ret = -1; + int note_size; + NoteFuncDesc *nf; + + for (nf = note_func; nf->note_contents_func; nf++) { + note.hdr.n_namesz = cpu_to_be32(sizeof(note.name)); + note.hdr.n_descsz = cpu_to_be32(nf->contents_size); + strncpy(note.name, note_name, sizeof(note.name)); + + (*nf->note_contents_func)(¬e, cpu); + + note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size; + ret = f(¬e, note_size, opaque); + if (ret < 0) { + return -1; + } + } + return 0; +} + +int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + return ppc64_write_all_elf64_notes("CORE", f, cpu, cpuid, opaque); +} + +int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, + CPUState *cpu, void *opaque) +{ + return 0; +} diff --git a/target-ppc/cpu-qom.h b/target-ppc/cpu-qom.h index f3c710a..827e5dd 100644 --- a/target-ppc/cpu-qom.h +++ b/target-ppc/cpu-qom.h @@ -108,7 +108,10 @@ void ppc_cpu_dump_statistics(CPUState *cpu, FILE *f, hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); - +int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f, + CPUState *cpu, void *opaque); +int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, + int cpuid, void *opaque); #ifndef CONFIG_USER_ONLY extern const struct VMStateDescription vmstate_ppc_cpu; #endif diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 807dab3..f778eaa 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8572,6 +8572,10 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug; cc->vmsd = &vmstate_ppc_cpu; +#if defined(TARGET_PPC64) + cc->write_elf64_note = ppc64_cpu_write_elf64_note; + cc->write_elf64_qemunote = ppc64_cpu_write_elf64_qemunote; +#endif #endif cc->gdb_num_core_regs = 71; -- cgit v1.1 From 793826cd460828975591f289de78672af4a47ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Tue, 15 Oct 2013 18:33:36 +0200 Subject: target-ppc: Fill in OpenFirmware names for some PowerPCCPU families MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set the expected values for POWER7, POWER7+, POWER8 and POWER5+. Note that POWER5+ and POWER7+ are intentionally lacking the '+', so the lack of a POWER7P family constitutes no problem. Signed-off-by: Andreas Färber Signed-off-by: Alexander Graf --- target-ppc/translate_init.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index f778eaa..9e29caa 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -7108,6 +7108,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->fw_name = "PowerPC,POWER5"; dc->desc = "POWER5+"; pcc->init_proc = init_proc_power5plus; pcc->check_pow = check_pow_970FX; @@ -7218,6 +7219,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->fw_name = "PowerPC,POWER7"; dc->desc = "POWER7"; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; @@ -7252,6 +7254,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); + dc->fw_name = "PowerPC,POWER8"; dc->desc = "POWER8"; pcc->init_proc = init_proc_POWER7; pcc->check_pow = check_pow_nocheck; -- cgit v1.1 From 3bbf37f2692652cc9d48030a9e7f34e2207429f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20F=C3=A4rber?= Date: Tue, 15 Oct 2013 18:33:37 +0200 Subject: spapr: Use DeviceClass::fw_name for device tree CPU node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of relying on cpu_model, obtain the device tree node label per CPU. Use DeviceClass::fw_name as source. Whenever DeviceClass::fw_name is unknown, default to "PowerPC,UNKNOWN". As a consequence, spapr_fixup_cpu_dt() can operate on each CPU's fw_name, obsoleting sPAPREnvironment::cpu_model, and spapr_create_fdt_skel() can drop its cpu_model argument. Signed-off-by: Prerna Saxena Signed-off-by: Andreas Färber Signed-off-by: Alexander Graf --- hw/ppc/spapr.c | 26 ++++++-------------------- include/hw/ppc/spapr.h | 1 - target-ppc/translate_init.c | 2 ++ 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index c0613e4..f76b355 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -204,9 +204,8 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) int smt = kvmppc_smt_threads(); uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; - assert(spapr->cpu_model); - CPU_FOREACH(cpu) { + DeviceClass *dc = DEVICE_GET_CLASS(cpu); uint32_t associativity[] = {cpu_to_be32(0x5), cpu_to_be32(0x0), cpu_to_be32(0x0), @@ -218,7 +217,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr) continue; } - snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model, + snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name, cpu->cpu_index); offset = fdt_path_offset(fdt, cpu_model); @@ -288,8 +287,7 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop, } while (0) -static void *spapr_create_fdt_skel(const char *cpu_model, - hwaddr initrd_base, +static void *spapr_create_fdt_skel(hwaddr initrd_base, hwaddr initrd_size, hwaddr kernel_size, bool little_endian, @@ -306,7 +304,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model, char qemu_hypertas_prop[] = "hcall-memop1"; uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)}; uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)}; - char *modelname; int i, smt = kvmppc_smt_threads(); unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80}; @@ -365,18 +362,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_property_cell(fdt, "#address-cells", 0x1))); _FDT((fdt_property_cell(fdt, "#size-cells", 0x0))); - modelname = g_strdup(cpu_model); - - for (i = 0; i < strlen(modelname); i++) { - modelname[i] = toupper(modelname[i]); - } - - /* This is needed during FDT finalization */ - spapr->cpu_model = g_strdup(modelname); - CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; + DeviceClass *dc = DEVICE_GET_CLASS(cs); PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); int index = cs->cpu_index; uint32_t servers_prop[smp_threads]; @@ -393,7 +382,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model, continue; } - nodename = g_strdup_printf("%s@%x", modelname, index); + nodename = g_strdup_printf("%s@%x", dc->fw_name, index); _FDT((fdt_begin_node(fdt, nodename))); @@ -477,8 +466,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model, _FDT((fdt_end_node(fdt))); } - g_free(modelname); - _FDT((fdt_end_node(fdt))); /* RTAS */ @@ -1363,8 +1350,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args) &savevm_htab_handlers, spapr); /* Prepare the device tree */ - spapr->fdt_skel = spapr_create_fdt_skel(cpu_model, - initrd_base, initrd_size, + spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size, kernel_size, kernel_le, boot_device, kernel_cmdline, spapr->epow_irq); diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 5ae0b58..fdaab2d 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -29,7 +29,6 @@ typedef struct sPAPREnvironment { target_ulong entry_point; uint32_t next_irq; uint64_t rtc_offset; - char *cpu_model; bool has_graphics; uint32_t epow_irq; diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 9e29caa..47825ac 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -8587,6 +8587,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #else cc->gdb_core_xml_file = "power-core.xml"; #endif + + dc->fw_name = "PowerPC,UNKNOWN"; } static const TypeInfo ppc_cpu_type_info = { -- cgit v1.1