ppc patch queue 2017-04-26
Here's a respind of my first pull request for qemu-2.10, consisting of
 assorted patches which have accumulated while qemu-2.9 stabilized.
 Highlights are:
     * Rework / cleanup of the XICS interrupt controller
     * Substantial improvement to the 'powernv' machine type
         - Includes an MMIO XICS version
     * POWER9 support improvements
         - POWER9 guests with KVM
         - Partial support for POWER9 guests with TCG
     * IOMMU and VFIO improvements
     * Assorted minor changes
 
 There are several IPMI patches here that aren't usually in my area of
 maintenance, but there isn't a regular maintainer and these patches
 are for the benefit of the powernv machine type.
 
 This pull request supersedes my 2017-04-26 pull request.  This new set
 fixes a bug in one of the aforementioned IPMI patches which caused
 clang sanitizer failures (and may have crashed on some libc / host
 versions).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJZAEUCAAoJEGw4ysog2bOS8O4P/01ruoUftX9JCkvqJjReMCjX
 h52ygdzkoa24ekc95wjNz9uVjzRavx1AVmd3wty3Po9oPiY7Or8CmvnMoCi2g4Vj
 cl2YjneAnaDuv7ud0HObOptfjtJxiNZr1la+gC+z3rIk0CdJ/XmH8Aiw5OhwimnC
 2NLL8vxkvIPgjHGJQ4r2YxX6qjhiwBL39DE1YpIKJ1aonh7tgXbrytR34owEphFp
 BOQLC0Sk0+GzI9LPlHTe54nQLantFkgzdZYIIA6GX8owtX3Nul/bp3YahdgiPLC1
 NOSAyf7CO5+AISWsqrojncd4pTWuCSUfqoRdhSSGrpj3DeFtdiFEtmr8W1NTj+MZ
 J9MP/UGQXgI0uLgvhqA41zzy/4OapIWdMczYRwVH8Fb0pFVklhuSQIE1R6V/6L7Q
 Gajs6SWczCw0zVyflHXryRdaEyx67gL1Nl0NWgUuSJBt0sdOU9Rh89oNPssJcioy
 ZIKCXl5W5uh8xHiFnCnMqbk6YOw15FufiQajideL03QEMztw42ZiejpZObK+yMpA
 TnxUsH2p/naQbh5wn4Z+0IUQ6KubX+XstNy/p45aKujvkGHq/L5vI2JNUujIa8EL
 x5vTY/zfaSh1k2J1HLm7LvwYnZTS8Mc/TKHKWOV1iGrG+4u89SiuyQq20SqXgNmE
 L2SHTJjDxdUDmBWBKCRi
 =Nnid
 -----END PGP SIGNATURE-----
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.10-20170426' into staging
ppc patch queue 2017-04-26
Here's a respind of my first pull request for qemu-2.10, consisting of
assorted patches which have accumulated while qemu-2.9 stabilized.
Highlights are:
    * Rework / cleanup of the XICS interrupt controller
    * Substantial improvement to the 'powernv' machine type
        - Includes an MMIO XICS version
    * POWER9 support improvements
        - POWER9 guests with KVM
        - Partial support for POWER9 guests with TCG
    * IOMMU and VFIO improvements
    * Assorted minor changes
There are several IPMI patches here that aren't usually in my area of
maintenance, but there isn't a regular maintainer and these patches
are for the benefit of the powernv machine type.
This pull request supersedes my 2017-04-26 pull request.  This new set
fixes a bug in one of the aforementioned IPMI patches which caused
clang sanitizer failures (and may have crashed on some libc / host
versions).
# gpg: Signature made Wed 26 Apr 2017 07:58:10 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.10-20170426: (48 commits)
  MAINTAINERS: Remove myself from e500
  target/ppc: Style fixes
  e500,book3s: mfspr 259: Register mapped/aliased SPRG3 user read
  target/ppc: Flush TLB on write to PIDR
  spapr-cpu-core: Release ICPState object during CPU unrealization
  ppc/pnv: generate an OEM SEL event on shutdown
  ppc/pnv: add initial IPMI sensors for the BMC simulator
  ppc/pnv: populate device tree for IPMI BT devices
  ppc/pnv: populate device tree for serial devices
  ppc/pnv: populate device tree for RTC devices
  ppc/pnv: scan ISA bus to populate device tree
  ppc/pnv: enable only one LPC bus
  ppc/pnv: Add support for POWER8+ LPC Controller
  spapr: remove the 'nr_servers' field from the machine
  target/ppc: Fix size of struct PPCElfPrstatus
  ipmi: introduce an ipmi_bmc_gen_event() API
  ipmi: introduce an ipmi_bmc_sdr_find() API
  ipmi: provide support for FRUs
  ipmi: use a file to load SDRs
  ppc: add IPMI support
  ...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
			
			
This commit is contained in:
		
						commit
						dcaed66cbe
					
				| @ -646,7 +646,6 @@ F: hw/ppc/ppc440_bamboo.c | ||||
| 
 | ||||
| e500 | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| M: Scott Wood <scottwood@freescale.com> | ||||
| L: qemu-ppc@nongnu.org | ||||
| S: Supported | ||||
| F: hw/ppc/e500.[hc] | ||||
| @ -657,7 +656,6 @@ F: pc-bios/u-boot.e500 | ||||
| 
 | ||||
| mpc8544ds | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| M: Scott Wood <scottwood@freescale.com> | ||||
| L: qemu-ppc@nongnu.org | ||||
| S: Supported | ||||
| F: hw/ppc/mpc8544ds.c | ||||
| @ -934,7 +932,6 @@ F: include/hw/ppc/ppc4xx.h | ||||
| 
 | ||||
| ppce500 | ||||
| M: Alexander Graf <agraf@suse.de> | ||||
| M: Scott Wood <scottwood@freescale.com> | ||||
| L: qemu-ppc@nongnu.org | ||||
| S: Supported | ||||
| F: hw/ppc/e500* | ||||
|  | ||||
| @ -6,6 +6,10 @@ include usb.mak | ||||
| CONFIG_VIRTIO_VGA=y | ||||
| CONFIG_ESCC=y | ||||
| CONFIG_M48T59=y | ||||
| CONFIG_IPMI=y | ||||
| CONFIG_IPMI_LOCAL=y | ||||
| CONFIG_IPMI_EXTERN=y | ||||
| CONFIG_ISA_IPMI_BT=y | ||||
| CONFIG_SERIAL=y | ||||
| CONFIG_PARALLEL=y | ||||
| CONFIG_I8254=y | ||||
|  | ||||
| @ -35,6 +35,7 @@ obj-$(CONFIG_SH4) += sh_intc.o | ||||
| obj-$(CONFIG_XICS) += xics.o | ||||
| obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o | ||||
| obj-$(CONFIG_XICS_KVM) += xics_kvm.o | ||||
| obj-$(CONFIG_POWERNV) += xics_pnv.o | ||||
| obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o | ||||
| obj-$(CONFIG_S390_FLIC) += s390_flic.o | ||||
| obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o | ||||
|  | ||||
| @ -38,21 +38,10 @@ | ||||
| #include "monitor/monitor.h" | ||||
| #include "hw/intc/intc.h" | ||||
| 
 | ||||
| int xics_get_cpu_index_by_dt_id(int cpu_dt_id) | ||||
| { | ||||
|     PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); | ||||
| 
 | ||||
|     if (cpu) { | ||||
|         return cpu->parent_obj.cpu_index; | ||||
|     } | ||||
| 
 | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     ICPState *icp = xics_icp_get(xi, cs->cpu_index); | ||||
|     ICPState *icp = ICP(cpu->intc); | ||||
| 
 | ||||
|     assert(icp); | ||||
|     assert(cs == icp->cs); | ||||
| @ -61,15 +50,15 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu) | ||||
|     icp->cs = NULL; | ||||
| } | ||||
| 
 | ||||
| void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu) | ||||
| void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     CPUPPCState *env = &cpu->env; | ||||
|     ICPState *icp = xics_icp_get(xi, cs->cpu_index); | ||||
|     ICPStateClass *icpc; | ||||
| 
 | ||||
|     assert(icp); | ||||
| 
 | ||||
|     cpu->intc = OBJECT(icp); | ||||
|     icp->cs = cs; | ||||
| 
 | ||||
|     icpc = ICP_GET_CLASS(icp); | ||||
| @ -348,6 +337,7 @@ static void icp_reset(void *dev) | ||||
| static void icp_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     ICPState *icp = ICP(dev); | ||||
|     ICPStateClass *icpc = ICP_GET_CLASS(dev); | ||||
|     Object *obj; | ||||
|     Error *err = NULL; | ||||
| 
 | ||||
| @ -360,6 +350,10 @@ static void icp_realize(DeviceState *dev, Error **errp) | ||||
| 
 | ||||
|     icp->xics = XICS_FABRIC(obj); | ||||
| 
 | ||||
|     if (icpc->realize) { | ||||
|         icpc->realize(dev, errp); | ||||
|     } | ||||
| 
 | ||||
|     qemu_register_reset(icp_reset, dev); | ||||
| } | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										192
									
								
								hw/intc/xics_pnv.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								hw/intc/xics_pnv.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| /*
 | ||||
|  * QEMU PowerPC PowerNV Interrupt Control Presenter (ICP) model | ||||
|  * | ||||
|  * Copyright (c) 2017, 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 "hw/ppc/xics.h" | ||||
| 
 | ||||
| #define ICP_XIRR_POLL    0 /* 1 byte (CPRR) or 4 bytes */ | ||||
| #define ICP_XIRR         4 /* 1 byte (CPRR) or 4 bytes */ | ||||
| #define ICP_MFRR        12 /* 1 byte access only */ | ||||
| 
 | ||||
| #define ICP_LINKA       16 /* unused */ | ||||
| #define ICP_LINKB       20 /* unused */ | ||||
| #define ICP_LINKC       24 /* unused */ | ||||
| 
 | ||||
| static uint64_t pnv_icp_read(void *opaque, hwaddr addr, unsigned width) | ||||
| { | ||||
|     ICPState *icp = ICP(opaque); | ||||
|     PnvICPState *picp = PNV_ICP(opaque); | ||||
|     bool byte0 = (width == 1 && (addr & 0x3) == 0); | ||||
|     uint64_t val = 0xffffffff; | ||||
| 
 | ||||
|     switch (addr & 0xffc) { | ||||
|     case ICP_XIRR_POLL: | ||||
|         val = icp_ipoll(icp, NULL); | ||||
|         if (byte0) { | ||||
|             val >>= 24; | ||||
|         } else if (width != 4) { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_XIRR: | ||||
|         if (byte0) { | ||||
|             val = icp_ipoll(icp, NULL) >> 24; | ||||
|         } else if (width == 4) { | ||||
|             val = icp_accept(icp); | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_MFRR: | ||||
|         if (byte0) { | ||||
|             val = icp->mfrr; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_LINKA: | ||||
|         if (width == 4) { | ||||
|             val = picp->links[0]; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_LINKB: | ||||
|         if (width == 4) { | ||||
|             val = picp->links[1]; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_LINKC: | ||||
|         if (width == 4) { | ||||
|             val = picp->links[2]; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
| bad_access: | ||||
|         qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" | ||||
|                       HWADDR_PRIx"/%d\n", addr, width); | ||||
|     } | ||||
| 
 | ||||
|     return val; | ||||
| } | ||||
| 
 | ||||
| static void pnv_icp_write(void *opaque, hwaddr addr, uint64_t val, | ||||
|                               unsigned width) | ||||
| { | ||||
|     ICPState *icp = ICP(opaque); | ||||
|     PnvICPState *picp = PNV_ICP(opaque); | ||||
|     bool byte0 = (width == 1 && (addr & 0x3) == 0); | ||||
| 
 | ||||
|     switch (addr & 0xffc) { | ||||
|     case ICP_XIRR: | ||||
|         if (byte0) { | ||||
|             icp_set_cppr(icp, val); | ||||
|         } else if (width == 4) { | ||||
|             icp_eoi(icp, val); | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_MFRR: | ||||
|         if (byte0) { | ||||
|             icp_set_mfrr(icp, val); | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_LINKA: | ||||
|         if (width == 4) { | ||||
|             picp->links[0] = val; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_LINKB: | ||||
|         if (width == 4) { | ||||
|             picp->links[1] = val; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     case ICP_LINKC: | ||||
|         if (width == 4) { | ||||
|             picp->links[2] = val; | ||||
|         } else { | ||||
|             goto bad_access; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
| bad_access: | ||||
|         qemu_log_mask(LOG_GUEST_ERROR, "XICS: Bad ICP access 0x%" | ||||
|                       HWADDR_PRIx"/%d\n", addr, width); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static const MemoryRegionOps pnv_icp_ops = { | ||||
|     .read = pnv_icp_read, | ||||
|     .write = pnv_icp_write, | ||||
|     .endianness = DEVICE_BIG_ENDIAN, | ||||
|     .valid = { | ||||
|         .min_access_size = 1, | ||||
|         .max_access_size = 4, | ||||
|     }, | ||||
|     .impl = { | ||||
|         .min_access_size = 1, | ||||
|         .max_access_size = 4, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| static void pnv_icp_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     PnvICPState *icp = PNV_ICP(dev); | ||||
| 
 | ||||
|     memory_region_init_io(&icp->mmio, OBJECT(dev), &pnv_icp_ops, | ||||
|                           icp, "icp-thread", 0x1000); | ||||
| } | ||||
| 
 | ||||
| static void pnv_icp_class_init(ObjectClass *klass, void *data) | ||||
| { | ||||
|     DeviceClass *dc = DEVICE_CLASS(klass); | ||||
|     ICPStateClass *icpc = ICP_CLASS(klass); | ||||
| 
 | ||||
|     icpc->realize = pnv_icp_realize; | ||||
|     dc->desc = "PowerNV ICP"; | ||||
| } | ||||
| 
 | ||||
| static const TypeInfo pnv_icp_info = { | ||||
|     .name          = TYPE_PNV_ICP, | ||||
|     .parent        = TYPE_ICP, | ||||
|     .instance_size = sizeof(PnvICPState), | ||||
|     .class_init    = pnv_icp_class_init, | ||||
|     .class_size    = sizeof(ICPStateClass), | ||||
| }; | ||||
| 
 | ||||
| static void pnv_icp_register_types(void) | ||||
| { | ||||
|     type_register_static(&pnv_icp_info); | ||||
| } | ||||
| 
 | ||||
| type_init(pnv_icp_register_types) | ||||
| @ -43,20 +43,17 @@ | ||||
| static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                            target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); | ||||
|     target_ulong cppr = args[0]; | ||||
| 
 | ||||
|     icp_set_cppr(icp, cppr); | ||||
|     icp_set_cppr(ICP(cpu->intc), cppr); | ||||
|     return H_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                           target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     target_ulong server = xics_get_cpu_index_by_dt_id(args[0]); | ||||
|     target_ulong mfrr = args[1]; | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), server); | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]); | ||||
| 
 | ||||
|     if (!icp) { | ||||
|         return H_PARAMETER; | ||||
| @ -69,9 +66,7 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
| static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                            target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); | ||||
|     uint32_t xirr = icp_accept(icp); | ||||
|     uint32_t xirr = icp_accept(ICP(cpu->intc)); | ||||
| 
 | ||||
|     args[0] = xirr; | ||||
|     return H_SUCCESS; | ||||
| @ -80,9 +75,7 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
| static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                              target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); | ||||
|     uint32_t xirr = icp_accept(icp); | ||||
|     uint32_t xirr = icp_accept(ICP(cpu->intc)); | ||||
| 
 | ||||
|     args[0] = xirr; | ||||
|     args[1] = cpu_get_host_ticks(); | ||||
| @ -92,21 +85,17 @@ static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
| static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                           target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); | ||||
|     target_ulong xirr = args[0]; | ||||
| 
 | ||||
|     icp_eoi(icp, xirr); | ||||
|     icp_eoi(ICP(cpu->intc), xirr); | ||||
|     return H_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                             target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index); | ||||
|     uint32_t mfrr; | ||||
|     uint32_t xirr = icp_ipoll(icp, &mfrr); | ||||
|     uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr); | ||||
| 
 | ||||
|     args[0] = xirr; | ||||
|     args[1] = mfrr; | ||||
| @ -132,7 +121,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|     } | ||||
| 
 | ||||
|     nr = rtas_ld(args, 0); | ||||
|     server = xics_get_cpu_index_by_dt_id(rtas_ld(args, 1)); | ||||
|     server = rtas_ld(args, 1); | ||||
|     priority = rtas_ld(args, 2); | ||||
| 
 | ||||
|     if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server) | ||||
|  | ||||
| @ -27,6 +27,7 @@ | ||||
| #include "qemu/timer.h" | ||||
| #include "hw/ipmi/ipmi.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "hw/loader.h" | ||||
| 
 | ||||
| #define IPMI_NETFN_CHASSIS            0x00 | ||||
| 
 | ||||
| @ -79,6 +80,9 @@ | ||||
| #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE   0x2A | ||||
| #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE    0x2B | ||||
| #define IPMI_CMD_RUN_INIT_AGENT           0x2C | ||||
| #define IPMI_CMD_GET_FRU_AREA_INFO        0x10 | ||||
| #define IPMI_CMD_READ_FRU_DATA            0x11 | ||||
| #define IPMI_CMD_WRITE_FRU_DATA           0x12 | ||||
| #define IPMI_CMD_GET_SEL_INFO             0x40 | ||||
| #define IPMI_CMD_GET_SEL_ALLOC_INFO       0x41 | ||||
| #define IPMI_CMD_RESERVE_SEL              0x42 | ||||
| @ -121,6 +125,13 @@ typedef struct IPMISdr { | ||||
|     uint8_t overflow; | ||||
| } IPMISdr; | ||||
| 
 | ||||
| typedef struct IPMIFru { | ||||
|     char *filename; | ||||
|     unsigned int nentries; | ||||
|     uint16_t areasize; | ||||
|     uint8_t *data; | ||||
| } IPMIFru; | ||||
| 
 | ||||
| typedef struct IPMISensor { | ||||
|     uint8_t status; | ||||
|     uint8_t reading; | ||||
| @ -212,7 +223,9 @@ struct IPMIBmcSim { | ||||
| 
 | ||||
|     IPMISel sel; | ||||
|     IPMISdr sdr; | ||||
|     IPMIFru fru; | ||||
|     IPMISensor sensors[MAX_SENSORS]; | ||||
|     char *sdr_filename; | ||||
| 
 | ||||
|     /* Odd netfns are for responses, so we only need the even ones. */ | ||||
|     const IPMINetfn *netfns[MAX_NETFNS / 2]; | ||||
| @ -403,6 +416,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid, | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
| int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, | ||||
|                       const struct ipmi_sdr_compact **sdr, uint16_t *nextrec) | ||||
| 
 | ||||
| { | ||||
|     IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); | ||||
|     unsigned int pos; | ||||
| 
 | ||||
|     pos = 0; | ||||
|     if (sdr_find_entry(&ibs->sdr, recid, &pos, nextrec)) { | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     *sdr = (const struct ipmi_sdr_compact *) &ibs->sdr.sdr[pos]; | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void sel_inc_reservation(IPMISel *sel) | ||||
| { | ||||
|     sel->reservation++; | ||||
| @ -444,6 +473,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs) | ||||
|             IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(ibs)); | ||||
| } | ||||
| 
 | ||||
| void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) | ||||
| { | ||||
|     IPMIBmcSim *ibs = IPMI_BMC_SIMULATOR(b); | ||||
|     IPMIInterface *s = ibs->parent.intf; | ||||
|     IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); | ||||
| 
 | ||||
|     if (!IPMI_BMC_EVENT_MSG_BUF_ENABLED(ibs)) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (log && IPMI_BMC_EVENT_LOG_ENABLED(ibs)) { | ||||
|         sel_add_event(ibs, evt); | ||||
|     } | ||||
| 
 | ||||
|     if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     memcpy(ibs->evtbuf, evt, 16); | ||||
|     ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; | ||||
|     k->set_atn(s, 1, attn_irq_enabled(ibs)); | ||||
|  out: | ||||
|     return; | ||||
| } | ||||
| static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, | ||||
|                       uint8_t evd1, uint8_t evd2, uint8_t evd3) | ||||
| { | ||||
| @ -1315,6 +1368,91 @@ static void get_sel_info(IPMIBmcSim *ibs, | ||||
|     rsp_buffer_push(rsp, (ibs->sel.overflow << 7) | 0x02); | ||||
| } | ||||
| 
 | ||||
| static void get_fru_area_info(IPMIBmcSim *ibs, | ||||
|                          uint8_t *cmd, unsigned int cmd_len, | ||||
|                          RspBuffer *rsp) | ||||
| { | ||||
|     uint8_t fruid; | ||||
|     uint16_t fru_entry_size; | ||||
| 
 | ||||
|     fruid = cmd[2]; | ||||
| 
 | ||||
|     if (fruid >= ibs->fru.nentries) { | ||||
|         rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     fru_entry_size = ibs->fru.areasize; | ||||
| 
 | ||||
|     rsp_buffer_push(rsp, fru_entry_size & 0xff); | ||||
|     rsp_buffer_push(rsp, fru_entry_size >> 8 & 0xff); | ||||
|     rsp_buffer_push(rsp, 0x0); | ||||
| } | ||||
| 
 | ||||
| static void read_fru_data(IPMIBmcSim *ibs, | ||||
|                          uint8_t *cmd, unsigned int cmd_len, | ||||
|                          RspBuffer *rsp) | ||||
| { | ||||
|     uint8_t fruid; | ||||
|     uint16_t offset; | ||||
|     int i; | ||||
|     uint8_t *fru_entry; | ||||
|     unsigned int count; | ||||
| 
 | ||||
|     fruid = cmd[2]; | ||||
|     offset = (cmd[3] | cmd[4] << 8); | ||||
| 
 | ||||
|     if (fruid >= ibs->fru.nentries) { | ||||
|         rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (offset >= ibs->fru.areasize - 1) { | ||||
|         rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; | ||||
| 
 | ||||
|     count = MIN(cmd[5], ibs->fru.areasize - offset); | ||||
| 
 | ||||
|     rsp_buffer_push(rsp, count & 0xff); | ||||
|     for (i = 0; i < count; i++) { | ||||
|         rsp_buffer_push(rsp, fru_entry[offset + i]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void write_fru_data(IPMIBmcSim *ibs, | ||||
|                          uint8_t *cmd, unsigned int cmd_len, | ||||
|                          RspBuffer *rsp) | ||||
| { | ||||
|     uint8_t fruid; | ||||
|     uint16_t offset; | ||||
|     uint8_t *fru_entry; | ||||
|     unsigned int count; | ||||
| 
 | ||||
|     fruid = cmd[2]; | ||||
|     offset = (cmd[3] | cmd[4] << 8); | ||||
| 
 | ||||
|     if (fruid >= ibs->fru.nentries) { | ||||
|         rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (offset >= ibs->fru.areasize - 1) { | ||||
|         rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     fru_entry = &ibs->fru.data[fruid * ibs->fru.areasize]; | ||||
| 
 | ||||
|     count = MIN(cmd_len - 5, ibs->fru.areasize - offset); | ||||
| 
 | ||||
|     memcpy(fru_entry + offset, cmd + 5, count); | ||||
| 
 | ||||
|     rsp_buffer_push(rsp, count & 0xff); | ||||
| } | ||||
| 
 | ||||
| static void reserve_sel(IPMIBmcSim *ibs, | ||||
|                         uint8_t *cmd, unsigned int cmd_len, | ||||
|                         RspBuffer *rsp) | ||||
| @ -1651,6 +1789,9 @@ static const IPMINetfn app_netfn = { | ||||
| }; | ||||
| 
 | ||||
| static const IPMICmdHandler storage_cmds[] = { | ||||
|     [IPMI_CMD_GET_FRU_AREA_INFO] = { get_fru_area_info, 3 }, | ||||
|     [IPMI_CMD_READ_FRU_DATA] = { read_fru_data, 5 }, | ||||
|     [IPMI_CMD_WRITE_FRU_DATA] = { write_fru_data, 5 }, | ||||
|     [IPMI_CMD_GET_SDR_REP_INFO] = { get_sdr_rep_info }, | ||||
|     [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep }, | ||||
|     [IPMI_CMD_GET_SDR] = { get_sdr, 8 }, | ||||
| @ -1696,22 +1837,33 @@ static void ipmi_sdr_init(IPMIBmcSim *ibs) | ||||
| 
 | ||||
|     sdrs_size = sizeof(init_sdrs); | ||||
|     sdrs = init_sdrs; | ||||
|     if (ibs->sdr_filename && | ||||
|         !g_file_get_contents(ibs->sdr_filename, (gchar **) &sdrs, &sdrs_size, | ||||
|                              NULL)) { | ||||
|         error_report("failed to load sdr file '%s'", ibs->sdr_filename); | ||||
|         sdrs_size = sizeof(init_sdrs); | ||||
|         sdrs = init_sdrs; | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < sdrs_size; i += len) { | ||||
|         struct ipmi_sdr_header *sdrh; | ||||
| 
 | ||||
|         if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) { | ||||
|             error_report("Problem with recid 0x%4.4x", i); | ||||
|             return; | ||||
|             break; | ||||
|         } | ||||
|         sdrh = (struct ipmi_sdr_header *) &sdrs[i]; | ||||
|         len = ipmi_sdr_length(sdrh); | ||||
|         if (i + len > sdrs_size) { | ||||
|             error_report("Problem with recid 0x%4.4x", i); | ||||
|             return; | ||||
|             break; | ||||
|         } | ||||
|         sdr_add_entry(ibs, sdrh, len, NULL); | ||||
|     } | ||||
| 
 | ||||
|     if (sdrs != init_sdrs) { | ||||
|         g_free(sdrs); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static const VMStateDescription vmstate_ipmi_sim = { | ||||
| @ -1742,6 +1894,36 @@ static const VMStateDescription vmstate_ipmi_sim = { | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static void ipmi_fru_init(IPMIFru *fru) | ||||
| { | ||||
|     int fsize; | ||||
|     int size = 0; | ||||
| 
 | ||||
|     if (!fru->filename) { | ||||
|         goto out; | ||||
|     } | ||||
| 
 | ||||
|     fsize = get_image_size(fru->filename); | ||||
|     if (fsize > 0) { | ||||
|         size = QEMU_ALIGN_UP(fsize, fru->areasize); | ||||
|         fru->data = g_malloc0(size); | ||||
|         if (load_image_size(fru->filename, fru->data, fsize) != fsize) { | ||||
|             error_report("Could not load file '%s'", fru->filename); | ||||
|             g_free(fru->data); | ||||
|             fru->data = NULL; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| out: | ||||
|     if (!fru->data) { | ||||
|         /* give one default FRU */ | ||||
|         size = fru->areasize; | ||||
|         fru->data = g_malloc0(size); | ||||
|     } | ||||
| 
 | ||||
|     fru->nentries = size / fru->areasize; | ||||
| } | ||||
| 
 | ||||
| static void ipmi_sim_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     IPMIBmc *b = IPMI_BMC(dev); | ||||
| @ -1763,6 +1945,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) | ||||
| 
 | ||||
|     ipmi_sdr_init(ibs); | ||||
| 
 | ||||
|     ipmi_fru_init(&ibs->fru); | ||||
| 
 | ||||
|     ibs->acpi_power_state[0] = 0; | ||||
|     ibs->acpi_power_state[1] = 0; | ||||
| 
 | ||||
| @ -1780,6 +1964,13 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) | ||||
|     vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); | ||||
| } | ||||
| 
 | ||||
| static Property ipmi_sim_properties[] = { | ||||
|     DEFINE_PROP_UINT16("fruareasize", IPMIBmcSim, fru.areasize, 1024), | ||||
|     DEFINE_PROP_STRING("frudatafile", IPMIBmcSim, fru.filename), | ||||
|     DEFINE_PROP_STRING("sdrfile", IPMIBmcSim, sdr_filename), | ||||
|     DEFINE_PROP_END_OF_LIST(), | ||||
| }; | ||||
| 
 | ||||
| static void ipmi_sim_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     DeviceClass *dc = DEVICE_CLASS(oc); | ||||
| @ -1787,6 +1978,7 @@ static void ipmi_sim_class_init(ObjectClass *oc, void *data) | ||||
| 
 | ||||
|     dc->hotpluggable = false; | ||||
|     dc->realize = ipmi_sim_realize; | ||||
|     dc->props = ipmi_sim_properties; | ||||
|     bk->handle_command = ipmi_sim_handle_command; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ 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 spapr_ovec.o | ||||
| # IBM PowerNV
 | ||||
| obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o | ||||
| obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o | ||||
| ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) | ||||
| obj-y += spapr_pci_vfio.o | ||||
| endif | ||||
|  | ||||
							
								
								
									
										411
									
								
								hw/ppc/pnv.c
									
									
									
									
									
								
							
							
						
						
									
										411
									
								
								hw/ppc/pnv.c
									
									
									
									
									
								
							| @ -33,7 +33,11 @@ | ||||
| #include "exec/address-spaces.h" | ||||
| #include "qemu/cutils.h" | ||||
| #include "qapi/visitor.h" | ||||
| #include "monitor/monitor.h" | ||||
| #include "hw/intc/intc.h" | ||||
| #include "hw/ipmi/ipmi.h" | ||||
| 
 | ||||
| #include "hw/ppc/xics.h" | ||||
| #include "hw/ppc/pnv_xscom.h" | ||||
| 
 | ||||
| #include "hw/isa/isa.h" | ||||
| @ -215,6 +219,55 @@ static void powernv_create_core_node(PnvChip *chip, PnvCore *pc, void *fdt) | ||||
|                        servers_prop, sizeof(servers_prop)))); | ||||
| } | ||||
| 
 | ||||
| static void powernv_populate_icp(PnvChip *chip, void *fdt, uint32_t pir, | ||||
|                                  uint32_t nr_threads) | ||||
| { | ||||
|     uint64_t addr = PNV_ICP_BASE(chip) | (pir << 12); | ||||
|     char *name; | ||||
|     const char compat[] = "IBM,power8-icp\0IBM,ppc-xicp"; | ||||
|     uint32_t irange[2], i, rsize; | ||||
|     uint64_t *reg; | ||||
|     int offset; | ||||
| 
 | ||||
|     irange[0] = cpu_to_be32(pir); | ||||
|     irange[1] = cpu_to_be32(nr_threads); | ||||
| 
 | ||||
|     rsize = sizeof(uint64_t) * 2 * nr_threads; | ||||
|     reg = g_malloc(rsize); | ||||
|     for (i = 0; i < nr_threads; i++) { | ||||
|         reg[i * 2] = cpu_to_be64(addr | ((pir + i) * 0x1000)); | ||||
|         reg[i * 2 + 1] = cpu_to_be64(0x1000); | ||||
|     } | ||||
| 
 | ||||
|     name = g_strdup_printf("interrupt-controller@%"PRIX64, addr); | ||||
|     offset = fdt_add_subnode(fdt, 0, name); | ||||
|     _FDT(offset); | ||||
|     g_free(name); | ||||
| 
 | ||||
|     _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); | ||||
|     _FDT((fdt_setprop(fdt, offset, "reg", reg, rsize))); | ||||
|     _FDT((fdt_setprop_string(fdt, offset, "device_type", | ||||
|                               "PowerPC-External-Interrupt-Presentation"))); | ||||
|     _FDT((fdt_setprop(fdt, offset, "interrupt-controller", NULL, 0))); | ||||
|     _FDT((fdt_setprop(fdt, offset, "ibm,interrupt-server-ranges", | ||||
|                        irange, sizeof(irange)))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#interrupt-cells", 1))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0))); | ||||
|     g_free(reg); | ||||
| } | ||||
| 
 | ||||
| static int pnv_chip_lpc_offset(PnvChip *chip, void *fdt) | ||||
| { | ||||
|     char *name; | ||||
|     int offset; | ||||
| 
 | ||||
|     name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", | ||||
|                            (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); | ||||
|     offset = fdt_path_offset(fdt, name); | ||||
|     g_free(name); | ||||
|     return offset; | ||||
| } | ||||
| 
 | ||||
| static void powernv_populate_chip(PnvChip *chip, void *fdt) | ||||
| { | ||||
|     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); | ||||
| @ -224,10 +277,24 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt) | ||||
| 
 | ||||
|     pnv_xscom_populate(chip, fdt, 0); | ||||
| 
 | ||||
|     /* The default LPC bus of a multichip system is on chip 0. It's
 | ||||
|      * recognized by the firmware (skiboot) using a "primary" | ||||
|      * property. | ||||
|      */ | ||||
|     if (chip->chip_id == 0x0) { | ||||
|         int lpc_offset = pnv_chip_lpc_offset(chip, fdt); | ||||
| 
 | ||||
|         _FDT((fdt_setprop(fdt, lpc_offset, "primary", NULL, 0))); | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < chip->nr_cores; i++) { | ||||
|         PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); | ||||
| 
 | ||||
|         powernv_create_core_node(chip, pnv_core, fdt); | ||||
| 
 | ||||
|         /* Interrupt Control Presenters (ICP). One per core. */ | ||||
|         powernv_populate_icp(chip, fdt, pnv_core->pir, | ||||
|                              CPU_CORE(pnv_core)->nr_threads); | ||||
|     } | ||||
| 
 | ||||
|     if (chip->ram_size) { | ||||
| @ -237,6 +304,127 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt) | ||||
|     g_free(typename); | ||||
| } | ||||
| 
 | ||||
| static void powernv_populate_rtc(ISADevice *d, void *fdt, int lpc_off) | ||||
| { | ||||
|     uint32_t io_base = d->ioport_id; | ||||
|     uint32_t io_regs[] = { | ||||
|         cpu_to_be32(1), | ||||
|         cpu_to_be32(io_base), | ||||
|         cpu_to_be32(2) | ||||
|     }; | ||||
|     char *name; | ||||
|     int node; | ||||
| 
 | ||||
|     name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); | ||||
|     node = fdt_add_subnode(fdt, lpc_off, name); | ||||
|     _FDT(node); | ||||
|     g_free(name); | ||||
| 
 | ||||
|     _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)))); | ||||
|     _FDT((fdt_setprop_string(fdt, node, "compatible", "pnpPNP,b00"))); | ||||
| } | ||||
| 
 | ||||
| static void powernv_populate_serial(ISADevice *d, void *fdt, int lpc_off) | ||||
| { | ||||
|     const char compatible[] = "ns16550\0pnpPNP,501"; | ||||
|     uint32_t io_base = d->ioport_id; | ||||
|     uint32_t io_regs[] = { | ||||
|         cpu_to_be32(1), | ||||
|         cpu_to_be32(io_base), | ||||
|         cpu_to_be32(8) | ||||
|     }; | ||||
|     char *name; | ||||
|     int node; | ||||
| 
 | ||||
|     name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); | ||||
|     node = fdt_add_subnode(fdt, lpc_off, name); | ||||
|     _FDT(node); | ||||
|     g_free(name); | ||||
| 
 | ||||
|     _FDT((fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)))); | ||||
|     _FDT((fdt_setprop(fdt, node, "compatible", compatible, | ||||
|                       sizeof(compatible)))); | ||||
| 
 | ||||
|     _FDT((fdt_setprop_cell(fdt, node, "clock-frequency", 1843200))); | ||||
|     _FDT((fdt_setprop_cell(fdt, node, "current-speed", 115200))); | ||||
|     _FDT((fdt_setprop_cell(fdt, node, "interrupts", d->isairq[0]))); | ||||
|     _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent", | ||||
|                            fdt_get_phandle(fdt, lpc_off)))); | ||||
| 
 | ||||
|     /* This is needed by Linux */ | ||||
|     _FDT((fdt_setprop_string(fdt, node, "device_type", "serial"))); | ||||
| } | ||||
| 
 | ||||
| static void powernv_populate_ipmi_bt(ISADevice *d, void *fdt, int lpc_off) | ||||
| { | ||||
|     const char compatible[] = "bt\0ipmi-bt"; | ||||
|     uint32_t io_base; | ||||
|     uint32_t io_regs[] = { | ||||
|         cpu_to_be32(1), | ||||
|         0, /* 'io_base' retrieved from the 'ioport' property of 'isa-ipmi-bt' */ | ||||
|         cpu_to_be32(3) | ||||
|     }; | ||||
|     uint32_t irq; | ||||
|     char *name; | ||||
|     int node; | ||||
| 
 | ||||
|     io_base = object_property_get_int(OBJECT(d), "ioport", &error_fatal); | ||||
|     io_regs[1] = cpu_to_be32(io_base); | ||||
| 
 | ||||
|     irq = object_property_get_int(OBJECT(d), "irq", &error_fatal); | ||||
| 
 | ||||
|     name = g_strdup_printf("%s@i%x", qdev_fw_name(DEVICE(d)), io_base); | ||||
|     node = fdt_add_subnode(fdt, lpc_off, name); | ||||
|     _FDT(node); | ||||
|     g_free(name); | ||||
| 
 | ||||
|     fdt_setprop(fdt, node, "reg", io_regs, sizeof(io_regs)); | ||||
|     fdt_setprop(fdt, node, "compatible", compatible, sizeof(compatible)); | ||||
| 
 | ||||
|     /* Mark it as reserved to avoid Linux trying to claim it */ | ||||
|     _FDT((fdt_setprop_string(fdt, node, "status", "reserved"))); | ||||
|     _FDT((fdt_setprop_cell(fdt, node, "interrupts", irq))); | ||||
|     _FDT((fdt_setprop_cell(fdt, node, "interrupt-parent", | ||||
|                            fdt_get_phandle(fdt, lpc_off)))); | ||||
| } | ||||
| 
 | ||||
| typedef struct ForeachPopulateArgs { | ||||
|     void *fdt; | ||||
|     int offset; | ||||
| } ForeachPopulateArgs; | ||||
| 
 | ||||
| static int powernv_populate_isa_device(DeviceState *dev, void *opaque) | ||||
| { | ||||
|     ForeachPopulateArgs *args = opaque; | ||||
|     ISADevice *d = ISA_DEVICE(dev); | ||||
| 
 | ||||
|     if (object_dynamic_cast(OBJECT(dev), TYPE_MC146818_RTC)) { | ||||
|         powernv_populate_rtc(d, args->fdt, args->offset); | ||||
|     } else if (object_dynamic_cast(OBJECT(dev), TYPE_ISA_SERIAL)) { | ||||
|         powernv_populate_serial(d, args->fdt, args->offset); | ||||
|     } else if (object_dynamic_cast(OBJECT(dev), "isa-ipmi-bt")) { | ||||
|         powernv_populate_ipmi_bt(d, args->fdt, args->offset); | ||||
|     } else { | ||||
|         error_report("unknown isa device %s@i%x", qdev_fw_name(dev), | ||||
|                      d->ioport_id); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void powernv_populate_isa(ISABus *bus, void *fdt, int lpc_offset) | ||||
| { | ||||
|     ForeachPopulateArgs args = { | ||||
|         .fdt = fdt, | ||||
|         .offset = lpc_offset, | ||||
|     }; | ||||
| 
 | ||||
|     /* ISA devices are not necessarily parented to the ISA bus so we
 | ||||
|      * can not use object_child_foreach() */ | ||||
|     qbus_walk_children(BUS(bus), powernv_populate_isa_device, | ||||
|                        NULL, NULL, NULL, &args); | ||||
| } | ||||
| 
 | ||||
| static void *powernv_create_fdt(MachineState *machine) | ||||
| { | ||||
|     const char plat_compat[] = "qemu,powernv\0ibm,powernv"; | ||||
| @ -245,6 +433,7 @@ static void *powernv_create_fdt(MachineState *machine) | ||||
|     char *buf; | ||||
|     int off; | ||||
|     int i; | ||||
|     int lpc_offset; | ||||
| 
 | ||||
|     fdt = g_malloc0(FDT_MAX_SIZE); | ||||
|     _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); | ||||
| @ -284,16 +473,49 @@ static void *powernv_create_fdt(MachineState *machine) | ||||
|     for (i = 0; i < pnv->num_chips; i++) { | ||||
|         powernv_populate_chip(pnv->chips[i], fdt); | ||||
|     } | ||||
| 
 | ||||
|     /* Populate ISA devices on chip 0 */ | ||||
|     lpc_offset = pnv_chip_lpc_offset(pnv->chips[0], fdt); | ||||
|     powernv_populate_isa(pnv->isa_bus, fdt, lpc_offset); | ||||
| 
 | ||||
|     if (pnv->bmc) { | ||||
|         pnv_bmc_populate_sensors(pnv->bmc, fdt); | ||||
|     } | ||||
| 
 | ||||
|     return fdt; | ||||
| } | ||||
| 
 | ||||
| static void pnv_powerdown_notify(Notifier *n, void *opaque) | ||||
| { | ||||
|     PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); | ||||
| 
 | ||||
|     if (pnv->bmc) { | ||||
|         pnv_bmc_powerdown(pnv->bmc); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void ppc_powernv_reset(void) | ||||
| { | ||||
|     MachineState *machine = MACHINE(qdev_get_machine()); | ||||
|     PnvMachineState *pnv = POWERNV_MACHINE(machine); | ||||
|     void *fdt; | ||||
|     Object *obj; | ||||
| 
 | ||||
|     qemu_devices_reset(); | ||||
| 
 | ||||
|     /* OpenPOWER systems have a BMC, which can be defined on the
 | ||||
|      * command line with: | ||||
|      * | ||||
|      *   -device ipmi-bmc-sim,id=bmc0 | ||||
|      * | ||||
|      * This is the internal simulator but it could also be an external | ||||
|      * BMC. | ||||
|      */ | ||||
|     obj = object_resolve_path_type("", TYPE_IPMI_BMC, NULL); | ||||
|     if (obj) { | ||||
|         pnv->bmc = IPMI_BMC(obj); | ||||
|     } | ||||
| 
 | ||||
|     fdt = powernv_create_fdt(machine); | ||||
| 
 | ||||
|     /* Pack resulting tree */ | ||||
| @ -302,29 +524,6 @@ static void ppc_powernv_reset(void) | ||||
|     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; | ||||
| @ -339,16 +538,7 @@ static ISABus *pnv_isa_create(PnvChip *chip) | ||||
|     isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io, | ||||
|                           &error_fatal); | ||||
| 
 | ||||
|     /* Not all variants have a working serial irq decoder. If not,
 | ||||
|      * handling of LPC interrupts becomes a platform issue (some | ||||
|      * platforms have a CPLD to do it). | ||||
|      */ | ||||
|     if (pcc->chip_type == PNV_CHIP_POWER8NVL) { | ||||
|         irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS); | ||||
|     } else { | ||||
|         irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip, | ||||
|                                   ISA_NUM_IRQS); | ||||
|     } | ||||
|     irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS); | ||||
| 
 | ||||
|     isa_bus_irqs(isa_bus, irqs); | ||||
|     return isa_bus; | ||||
| @ -457,6 +647,11 @@ static void ppc_powernv_init(MachineState *machine) | ||||
| 
 | ||||
|     /* Create an RTC ISA device too */ | ||||
|     rtc_init(pnv->isa_bus, 2000, NULL); | ||||
| 
 | ||||
|     /* OpenPOWER systems use a IPMI SEL Event message to notify the
 | ||||
|      * host to powerdown */ | ||||
|     pnv->powerdown_notifier.notify = pnv_powerdown_notify; | ||||
|     qemu_register_powerdown_notifier(&pnv->powerdown_notifier); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -638,6 +833,52 @@ static void pnv_chip_init(Object *obj) | ||||
| 
 | ||||
|     object_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC); | ||||
|     object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); | ||||
| 
 | ||||
|     object_initialize(&chip->psi, sizeof(chip->psi), TYPE_PNV_PSI); | ||||
|     object_property_add_child(obj, "psi", OBJECT(&chip->psi), NULL); | ||||
|     object_property_add_const_link(OBJECT(&chip->psi), "xics", | ||||
|                                    OBJECT(qdev_get_machine()), &error_abort); | ||||
| 
 | ||||
|     object_initialize(&chip->occ, sizeof(chip->occ), TYPE_PNV_OCC); | ||||
|     object_property_add_child(obj, "occ", OBJECT(&chip->occ), NULL); | ||||
|     object_property_add_const_link(OBJECT(&chip->occ), "psi", | ||||
|                                    OBJECT(&chip->psi), &error_abort); | ||||
| 
 | ||||
|     /* The LPC controller needs PSI to generate interrupts */ | ||||
|     object_property_add_const_link(OBJECT(&chip->lpc), "psi", | ||||
|                                    OBJECT(&chip->psi), &error_abort); | ||||
| } | ||||
| 
 | ||||
| static void pnv_chip_icp_realize(PnvChip *chip, Error **errp) | ||||
| { | ||||
|     PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); | ||||
|     char *typename = pnv_core_typename(pcc->cpu_model); | ||||
|     size_t typesize = object_type_get_instance_size(typename); | ||||
|     int i, j; | ||||
|     char *name; | ||||
|     XICSFabric *xi = XICS_FABRIC(qdev_get_machine()); | ||||
| 
 | ||||
|     name = g_strdup_printf("icp-%x", chip->chip_id); | ||||
|     memory_region_init(&chip->icp_mmio, OBJECT(chip), name, PNV_ICP_SIZE); | ||||
|     sysbus_init_mmio(SYS_BUS_DEVICE(chip), &chip->icp_mmio); | ||||
|     g_free(name); | ||||
| 
 | ||||
|     sysbus_mmio_map(SYS_BUS_DEVICE(chip), 1, PNV_ICP_BASE(chip)); | ||||
| 
 | ||||
|     /* Map the ICP registers for each thread */ | ||||
|     for (i = 0; i < chip->nr_cores; i++) { | ||||
|         PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); | ||||
|         int core_hwid = CPU_CORE(pnv_core)->core_id; | ||||
| 
 | ||||
|         for (j = 0; j < CPU_CORE(pnv_core)->nr_threads; j++) { | ||||
|             uint32_t pir = pcc->core_pir(chip, core_hwid) + j; | ||||
|             PnvICPState *icp = PNV_ICP(xics_icp_get(xi, pir)); | ||||
| 
 | ||||
|             memory_region_add_subregion(&chip->icp_mmio, pir << 12, &icp->mmio); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     g_free(typename); | ||||
| } | ||||
| 
 | ||||
| static void pnv_chip_realize(DeviceState *dev, Error **errp) | ||||
| @ -691,6 +932,8 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) | ||||
|         object_property_set_int(OBJECT(pnv_core), | ||||
|                                 pcc->core_pir(chip, core_hwid), | ||||
|                                 "pir", &error_fatal); | ||||
|         object_property_add_const_link(OBJECT(pnv_core), "xics", | ||||
|                                        qdev_get_machine(), &error_fatal); | ||||
|         object_property_set_bool(OBJECT(pnv_core), true, "realized", | ||||
|                                  &error_fatal); | ||||
|         object_unref(OBJECT(pnv_core)); | ||||
| @ -708,6 +951,32 @@ static void pnv_chip_realize(DeviceState *dev, Error **errp) | ||||
|     object_property_set_bool(OBJECT(&chip->lpc), true, "realized", | ||||
|                              &error_fatal); | ||||
|     pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); | ||||
| 
 | ||||
|     /* Interrupt Management Area. This is the memory region holding
 | ||||
|      * all the Interrupt Control Presenter (ICP) registers */ | ||||
|     pnv_chip_icp_realize(chip, &error); | ||||
|     if (error) { | ||||
|         error_propagate(errp, error); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /* Processor Service Interface (PSI) Host Bridge */ | ||||
|     object_property_set_int(OBJECT(&chip->psi), PNV_PSIHB_BASE(chip), | ||||
|                             "bar", &error_fatal); | ||||
|     object_property_set_bool(OBJECT(&chip->psi), true, "realized", &error); | ||||
|     if (error) { | ||||
|         error_propagate(errp, error); | ||||
|         return; | ||||
|     } | ||||
|     pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip->psi.xscom_regs); | ||||
| 
 | ||||
|     /* Create the simplified OCC model */ | ||||
|     object_property_set_bool(OBJECT(&chip->occ), true, "realized", &error); | ||||
|     if (error) { | ||||
|         error_propagate(errp, error); | ||||
|         return; | ||||
|     } | ||||
|     pnv_xscom_add_subregion(chip, PNV_XSCOM_OCC_BASE, &chip->occ.xscom_regs); | ||||
| } | ||||
| 
 | ||||
| static Property pnv_chip_properties[] = { | ||||
| @ -723,6 +992,7 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data) | ||||
| { | ||||
|     DeviceClass *dc = DEVICE_CLASS(klass); | ||||
| 
 | ||||
|     set_bit(DEVICE_CATEGORY_CPU, dc->categories); | ||||
|     dc->realize = pnv_chip_realize; | ||||
|     dc->props = pnv_chip_properties; | ||||
|     dc->desc = "PowerNV Chip"; | ||||
| @ -737,6 +1007,70 @@ static const TypeInfo pnv_chip_info = { | ||||
|     .abstract      = true, | ||||
| }; | ||||
| 
 | ||||
| static ICSState *pnv_ics_get(XICSFabric *xi, int irq) | ||||
| { | ||||
|     PnvMachineState *pnv = POWERNV_MACHINE(xi); | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < pnv->num_chips; i++) { | ||||
|         if (ics_valid_irq(&pnv->chips[i]->psi.ics, irq)) { | ||||
|             return &pnv->chips[i]->psi.ics; | ||||
|         } | ||||
|     } | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| static void pnv_ics_resend(XICSFabric *xi) | ||||
| { | ||||
|     PnvMachineState *pnv = POWERNV_MACHINE(xi); | ||||
|     int i; | ||||
| 
 | ||||
|     for (i = 0; i < pnv->num_chips; i++) { | ||||
|         ics_resend(&pnv->chips[i]->psi.ics); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static PowerPCCPU *ppc_get_vcpu_by_pir(int pir) | ||||
| { | ||||
|     CPUState *cs; | ||||
| 
 | ||||
|     CPU_FOREACH(cs) { | ||||
|         PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
|         CPUPPCState *env = &cpu->env; | ||||
| 
 | ||||
|         if (env->spr_cb[SPR_PIR].default_value == pir) { | ||||
|             return cpu; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return NULL; | ||||
| } | ||||
| 
 | ||||
| static ICPState *pnv_icp_get(XICSFabric *xi, int pir) | ||||
| { | ||||
|     PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir); | ||||
| 
 | ||||
|     return cpu ? ICP(cpu->intc) : NULL; | ||||
| } | ||||
| 
 | ||||
| static void pnv_pic_print_info(InterruptStatsProvider *obj, | ||||
|                                Monitor *mon) | ||||
| { | ||||
|     PnvMachineState *pnv = POWERNV_MACHINE(obj); | ||||
|     int i; | ||||
|     CPUState *cs; | ||||
| 
 | ||||
|     CPU_FOREACH(cs) { | ||||
|         PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
| 
 | ||||
|         icp_pic_print_info(ICP(cpu->intc), mon); | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < pnv->num_chips; i++) { | ||||
|         ics_pic_print_info(&pnv->chips[i]->psi.ics, mon); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name, | ||||
|                               void *opaque, Error **errp) | ||||
| { | ||||
| @ -787,6 +1121,8 @@ static void powernv_machine_class_props_init(ObjectClass *oc) | ||||
| static void powernv_machine_class_init(ObjectClass *oc, void *data) | ||||
| { | ||||
|     MachineClass *mc = MACHINE_CLASS(oc); | ||||
|     XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); | ||||
|     InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); | ||||
| 
 | ||||
|     mc->desc = "IBM PowerNV (Non-Virtualized)"; | ||||
|     mc->init = ppc_powernv_init; | ||||
| @ -797,6 +1133,10 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data) | ||||
|     mc->no_parallel = 1; | ||||
|     mc->default_boot_order = NULL; | ||||
|     mc->default_ram_size = 1 * G_BYTE; | ||||
|     xic->icp_get = pnv_icp_get; | ||||
|     xic->ics_get = pnv_ics_get; | ||||
|     xic->ics_resend = pnv_ics_resend; | ||||
|     ispc->print_info = pnv_pic_print_info; | ||||
| 
 | ||||
|     powernv_machine_class_props_init(oc); | ||||
| } | ||||
| @ -807,6 +1147,11 @@ static const TypeInfo powernv_machine_info = { | ||||
|     .instance_size = sizeof(PnvMachineState), | ||||
|     .instance_init = powernv_machine_initfn, | ||||
|     .class_init    = powernv_machine_class_init, | ||||
|     .interfaces = (InterfaceInfo[]) { | ||||
|         { TYPE_XICS_FABRIC }, | ||||
|         { TYPE_INTERRUPT_STATS_PROVIDER }, | ||||
|         { }, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| static void powernv_machine_register_types(void) | ||||
|  | ||||
							
								
								
									
										122
									
								
								hw/ppc/pnv_bmc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								hw/ppc/pnv_bmc.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | ||||
| /*
 | ||||
|  * QEMU PowerNV, BMC related functions | ||||
|  * | ||||
|  * Copyright (c) 2016-2017, IBM Corporation. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License, version 2, as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * 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 "hw/hw.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "target/ppc/cpu.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/log.h" | ||||
| #include "hw/ipmi/ipmi.h" | ||||
| #include "hw/ppc/fdt.h" | ||||
| 
 | ||||
| #include "hw/ppc/pnv.h" | ||||
| 
 | ||||
| #include <libfdt.h> | ||||
| 
 | ||||
| /* TODO: include definition in ipmi.h */ | ||||
| #define IPMI_SDR_FULL_TYPE 1 | ||||
| 
 | ||||
| /*
 | ||||
|  * OEM SEL Event data packet sent by BMC in response of a Read Event | ||||
|  * Message Buffer command | ||||
|  */ | ||||
| typedef struct OemSel { | ||||
|     /* SEL header */ | ||||
|     uint8_t id[2]; | ||||
|     uint8_t type; | ||||
|     uint8_t timestamp[4]; | ||||
|     uint8_t manuf_id[3]; | ||||
| 
 | ||||
|     /* OEM SEL data (6 bytes) follows */ | ||||
|     uint8_t netfun; | ||||
|     uint8_t cmd; | ||||
|     uint8_t data[4]; | ||||
| } OemSel; | ||||
| 
 | ||||
| #define SOFT_OFF        0x00 | ||||
| #define SOFT_REBOOT     0x01 | ||||
| 
 | ||||
| static void pnv_gen_oem_sel(IPMIBmc *bmc, uint8_t reboot) | ||||
| { | ||||
|     /* IPMI SEL Event are 16 bytes long */ | ||||
|     OemSel sel = { | ||||
|         .id        = { 0x55 , 0x55 }, | ||||
|         .type      = 0xC0, /* OEM */ | ||||
|         .manuf_id  = { 0x0, 0x0, 0x0 }, | ||||
|         .timestamp = { 0x0, 0x0, 0x0, 0x0 }, | ||||
|         .netfun    = 0x3A, /* IBM */ | ||||
|         .cmd       = 0x04, /* AMI OEM SEL Power Notification */ | ||||
|         .data      = { reboot, 0xFF, 0xFF, 0xFF }, | ||||
|     }; | ||||
| 
 | ||||
|     ipmi_bmc_gen_event(bmc, (uint8_t *) &sel, 0 /* do not log the event */); | ||||
| } | ||||
| 
 | ||||
| void pnv_bmc_powerdown(IPMIBmc *bmc) | ||||
| { | ||||
|     pnv_gen_oem_sel(bmc, SOFT_OFF); | ||||
| } | ||||
| 
 | ||||
| void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt) | ||||
| { | ||||
|     int offset; | ||||
|     int i; | ||||
|     const struct ipmi_sdr_compact *sdr; | ||||
|     uint16_t nextrec; | ||||
| 
 | ||||
|     offset = fdt_add_subnode(fdt, 0, "/bmc"); | ||||
|     _FDT(offset); | ||||
| 
 | ||||
|     _FDT((fdt_setprop_string(fdt, offset, "name", "bmc"))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); | ||||
| 
 | ||||
|     offset = fdt_add_subnode(fdt, offset, "sensors"); | ||||
|     _FDT(offset); | ||||
| 
 | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 0x1))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0x0))); | ||||
| 
 | ||||
|     for (i = 0; !ipmi_bmc_sdr_find(bmc, i, &sdr, &nextrec); i++) { | ||||
|         int off; | ||||
|         char *name; | ||||
| 
 | ||||
|         if (sdr->header.rec_type != IPMI_SDR_COMPACT_TYPE && | ||||
|             sdr->header.rec_type != IPMI_SDR_FULL_TYPE) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         name = g_strdup_printf("sensor@%x", sdr->sensor_owner_number); | ||||
|         off = fdt_add_subnode(fdt, offset, name); | ||||
|         _FDT(off); | ||||
|         g_free(name); | ||||
| 
 | ||||
|         _FDT((fdt_setprop_cell(fdt, off, "reg", sdr->sensor_owner_number))); | ||||
|         _FDT((fdt_setprop_string(fdt, off, "name", "sensor"))); | ||||
|         _FDT((fdt_setprop_string(fdt, off, "compatible", "ibm,ipmi-sensor"))); | ||||
|         _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-reading-type", | ||||
|                                sdr->reading_type))); | ||||
|         _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-id", | ||||
|                                sdr->entity_id))); | ||||
|         _FDT((fdt_setprop_cell(fdt, off, "ipmi-entity-instance", | ||||
|                                sdr->entity_instance))); | ||||
|         _FDT((fdt_setprop_cell(fdt, off, "ipmi-sensor-type", | ||||
|                                sdr->sensor_type))); | ||||
|     } | ||||
| } | ||||
| @ -25,6 +25,7 @@ | ||||
| #include "hw/ppc/pnv.h" | ||||
| #include "hw/ppc/pnv_core.h" | ||||
| #include "hw/ppc/pnv_xscom.h" | ||||
| #include "hw/ppc/xics.h" | ||||
| 
 | ||||
| static void powernv_cpu_reset(void *opaque) | ||||
| { | ||||
| @ -110,23 +111,37 @@ static const MemoryRegionOps pnv_core_xscom_ops = { | ||||
|     .endianness = DEVICE_BIG_ENDIAN, | ||||
| }; | ||||
| 
 | ||||
| static void pnv_core_realize_child(Object *child, Error **errp) | ||||
| static void pnv_core_realize_child(Object *child, XICSFabric *xi, Error **errp) | ||||
| { | ||||
|     Error *local_err = NULL; | ||||
|     CPUState *cs = CPU(child); | ||||
|     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
|     Object *obj; | ||||
| 
 | ||||
|     obj = object_new(TYPE_PNV_ICP); | ||||
|     object_property_add_child(OBJECT(cpu), "icp", obj, NULL); | ||||
|     object_property_add_const_link(obj, "xics", OBJECT(xi), &error_abort); | ||||
|     object_property_set_bool(obj, true, "realized", &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     object_property_set_bool(child, true, "realized", &local_err); | ||||
|     if (local_err) { | ||||
|         object_unparent(obj); | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     powernv_cpu_init(cpu, &local_err); | ||||
|     if (local_err) { | ||||
|         object_unparent(obj); | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     xics_cpu_setup(xi, cpu, ICP(obj)); | ||||
| } | ||||
| 
 | ||||
| static void pnv_core_realize(DeviceState *dev, Error **errp) | ||||
| @ -140,6 +155,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) | ||||
|     void *obj; | ||||
|     int i, j; | ||||
|     char name[32]; | ||||
|     Object *xi; | ||||
| 
 | ||||
|     xi = object_property_get_link(OBJECT(dev), "xics", &local_err); | ||||
|     if (!xi) { | ||||
|         error_setg(errp, "%s: required link 'xics' not found: %s", | ||||
|                    __func__, error_get_pretty(local_err)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     pc->threads = g_malloc0(size * cc->nr_threads); | ||||
|     for (i = 0; i < cc->nr_threads; i++) { | ||||
| @ -160,7 +183,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) | ||||
|     for (j = 0; j < cc->nr_threads; j++) { | ||||
|         obj = pc->threads + j * size; | ||||
| 
 | ||||
|         pnv_core_realize_child(obj, &local_err); | ||||
|         pnv_core_realize_child(obj, XICS_FABRIC(xi), &local_err); | ||||
|         if (local_err) { | ||||
|             goto err; | ||||
|         } | ||||
|  | ||||
							
								
								
									
										106
									
								
								hw/ppc/pnv_lpc.c
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								hw/ppc/pnv_lpc.c
									
									
									
									
									
								
							| @ -92,14 +92,6 @@ enum { | ||||
| #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"; | ||||
| @ -119,7 +111,6 @@ static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) | ||||
|     _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); | ||||
|     _FDT((fdt_setprop(fdt, offset, "primary", NULL, 0))); | ||||
|     _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat)))); | ||||
|     return 0; | ||||
| } | ||||
| @ -250,6 +241,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = { | ||||
|     .endianness = DEVICE_BIG_ENDIAN, | ||||
| }; | ||||
| 
 | ||||
| static void pnv_lpc_eval_irqs(PnvLpcController *lpc) | ||||
| { | ||||
|     bool lpc_to_opb_irq = false; | ||||
| 
 | ||||
|     /* Update LPC controller to OPB line */ | ||||
|     if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { | ||||
|         uint32_t irqs; | ||||
| 
 | ||||
|         irqs = lpc->lpc_hc_irqstat & lpc->lpc_hc_irqmask; | ||||
|         lpc_to_opb_irq = (irqs != 0); | ||||
|     } | ||||
| 
 | ||||
|     /* We don't honor the polarity register, it's pointless and unused
 | ||||
|      * anyway | ||||
|      */ | ||||
|     if (lpc_to_opb_irq) { | ||||
|         lpc->opb_irq_input |= OPB_MASTER_IRQ_LPC; | ||||
|     } else { | ||||
|         lpc->opb_irq_input &= ~OPB_MASTER_IRQ_LPC; | ||||
|     } | ||||
| 
 | ||||
|     /* Update OPB internal latch */ | ||||
|     lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; | ||||
| 
 | ||||
|     /* Reflect the interrupt */ | ||||
|     pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0); | ||||
| } | ||||
| 
 | ||||
| static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) | ||||
| { | ||||
|     PnvLpcController *lpc = opaque; | ||||
| @ -300,12 +319,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, | ||||
|         break; | ||||
|     case LPC_HC_IRQSER_CTRL: | ||||
|         lpc->lpc_hc_irqser_ctrl = val; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|         break; | ||||
|     case LPC_HC_IRQMASK: | ||||
|         lpc->lpc_hc_irqmask = val; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|         break; | ||||
|     case LPC_HC_IRQSTAT: | ||||
|         lpc->lpc_hc_irqstat &= ~val; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|         break; | ||||
|     case LPC_HC_ERROR_ADDRESS: | ||||
|         break; | ||||
| @ -363,14 +385,15 @@ static void opb_master_write(void *opaque, hwaddr addr, | ||||
|     switch (addr) { | ||||
|     case OPB_MASTER_LS_IRQ_STAT: | ||||
|         lpc->opb_irq_stat &= ~val; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|         break; | ||||
|     case OPB_MASTER_LS_IRQ_MASK: | ||||
|         /* XXX Filter out reserved bits */ | ||||
|         lpc->opb_irq_mask = val; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|         break; | ||||
|     case OPB_MASTER_LS_IRQ_POL: | ||||
|         /* XXX Filter out reserved bits */ | ||||
|         lpc->opb_irq_pol = val; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|         break; | ||||
|     case OPB_MASTER_LS_IRQ_INPUT: | ||||
|         /* Read only */ | ||||
| @ -398,6 +421,8 @@ static const MemoryRegionOps opb_master_ops = { | ||||
| static void pnv_lpc_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     PnvLpcController *lpc = PNV_LPC(dev); | ||||
|     Object *obj; | ||||
|     Error *error = NULL; | ||||
| 
 | ||||
|     /* Reg inits */ | ||||
|     lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; | ||||
| @ -441,6 +466,15 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) | ||||
|     pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), | ||||
|                           &pnv_lpc_xscom_ops, lpc, "xscom-lpc", | ||||
|                           PNV_XSCOM_LPC_SIZE); | ||||
| 
 | ||||
|     /* get PSI object from chip */ | ||||
|     obj = object_property_get_link(OBJECT(dev), "psi", &error); | ||||
|     if (!obj) { | ||||
|         error_setg(errp, "%s: required link 'psi' not found: %s", | ||||
|                    __func__, error_get_pretty(error)); | ||||
|         return; | ||||
|     } | ||||
|     lpc->psi = PNV_PSI(obj); | ||||
| } | ||||
| 
 | ||||
| static void pnv_lpc_class_init(ObjectClass *klass, void *data) | ||||
| @ -470,3 +504,53 @@ static void pnv_lpc_register_types(void) | ||||
| } | ||||
| 
 | ||||
| type_init(pnv_lpc_register_types) | ||||
| 
 | ||||
| /* 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. | ||||
|  */ | ||||
| static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level) | ||||
| { | ||||
|     PnvMachineState *pnv = POWERNV_MACHINE(qdev_get_machine()); | ||||
|     uint32_t old_state = pnv->cpld_irqstate; | ||||
|     PnvLpcController *lpc = PNV_LPC(opaque); | ||||
| 
 | ||||
|     if (level) { | ||||
|         pnv->cpld_irqstate |= 1u << n; | ||||
|     } else { | ||||
|         pnv->cpld_irqstate &= ~(1u << n); | ||||
|     } | ||||
| 
 | ||||
|     if (pnv->cpld_irqstate != old_state) { | ||||
|         pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_EXTERNAL, pnv->cpld_irqstate != 0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level) | ||||
| { | ||||
|     PnvLpcController *lpc = PNV_LPC(opaque); | ||||
| 
 | ||||
|     /* The Naples HW latches the 1 levels, clearing is done by SW */ | ||||
|     if (level) { | ||||
|         lpc->lpc_hc_irqstat |= LPC_HC_IRQ_SERIRQ0 >> n; | ||||
|         pnv_lpc_eval_irqs(lpc); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, | ||||
|                                  int nirqs) | ||||
| { | ||||
|     /* 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 (chip_type == PNV_CHIP_POWER8NVL) { | ||||
|         return qemu_allocate_irqs(pnv_lpc_isa_irq_handler, lpc, nirqs); | ||||
|     } else { | ||||
|         return qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, lpc, nirqs); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										136
									
								
								hw/ppc/pnv_occ.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								hw/ppc/pnv_occ.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,136 @@ | ||||
| /*
 | ||||
|  * QEMU PowerPC PowerNV Emulation of a few OCC related registers | ||||
|  * | ||||
|  * Copyright (c) 2015-2017, IBM Corporation. | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License, version 2, as | ||||
|  * published by the Free Software Foundation. | ||||
|  * | ||||
|  * 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 "hw/hw.h" | ||||
| #include "sysemu/sysemu.h" | ||||
| #include "target/ppc/cpu.h" | ||||
| #include "qapi/error.h" | ||||
| #include "qemu/log.h" | ||||
| 
 | ||||
| #include "hw/ppc/pnv.h" | ||||
| #include "hw/ppc/pnv_xscom.h" | ||||
| #include "hw/ppc/pnv_occ.h" | ||||
| 
 | ||||
| #define OCB_OCI_OCCMISC         0x4020 | ||||
| #define OCB_OCI_OCCMISC_AND     0x4021 | ||||
| #define OCB_OCI_OCCMISC_OR      0x4022 | ||||
| 
 | ||||
| static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) | ||||
| { | ||||
|     bool irq_state; | ||||
| 
 | ||||
|     val &= 0xffff000000000000ull; | ||||
| 
 | ||||
|     occ->occmisc = val; | ||||
|     irq_state = !!(val >> 63); | ||||
|     pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state); | ||||
| } | ||||
| 
 | ||||
| static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size) | ||||
| { | ||||
|     PnvOCC *occ = PNV_OCC(opaque); | ||||
|     uint32_t offset = addr >> 3; | ||||
|     uint64_t val = 0; | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case OCB_OCI_OCCMISC: | ||||
|         val = occ->occmisc; | ||||
|         break; | ||||
|     default: | ||||
|         qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" | ||||
|                       HWADDR_PRIx "\n", addr); | ||||
|     } | ||||
|     return val; | ||||
| } | ||||
| 
 | ||||
| static void pnv_occ_xscom_write(void *opaque, hwaddr addr, | ||||
|                                 uint64_t val, unsigned size) | ||||
| { | ||||
|     PnvOCC *occ = PNV_OCC(opaque); | ||||
|     uint32_t offset = addr >> 3; | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case OCB_OCI_OCCMISC_AND: | ||||
|         pnv_occ_set_misc(occ, occ->occmisc & val); | ||||
|         break; | ||||
|     case OCB_OCI_OCCMISC_OR: | ||||
|         pnv_occ_set_misc(occ, occ->occmisc | val); | ||||
|         break; | ||||
|     case OCB_OCI_OCCMISC: | ||||
|         pnv_occ_set_misc(occ, val); | ||||
|         break; | ||||
|     default: | ||||
|         qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" | ||||
|                       HWADDR_PRIx "\n", addr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static const MemoryRegionOps pnv_occ_xscom_ops = { | ||||
|     .read = pnv_occ_xscom_read, | ||||
|     .write = pnv_occ_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_occ_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     PnvOCC *occ = PNV_OCC(dev); | ||||
|     Object *obj; | ||||
|     Error *error = NULL; | ||||
| 
 | ||||
|     occ->occmisc = 0; | ||||
| 
 | ||||
|     /* get PSI object from chip */ | ||||
|     obj = object_property_get_link(OBJECT(dev), "psi", &error); | ||||
|     if (!obj) { | ||||
|         error_setg(errp, "%s: required link 'psi' not found: %s", | ||||
|                    __func__, error_get_pretty(error)); | ||||
|         return; | ||||
|     } | ||||
|     occ->psi = PNV_PSI(obj); | ||||
| 
 | ||||
|     /* XScom region for OCC registers */ | ||||
|     pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops, | ||||
|                   occ, "xscom-occ", PNV_XSCOM_OCC_SIZE); | ||||
| } | ||||
| 
 | ||||
| static void pnv_occ_class_init(ObjectClass *klass, void *data) | ||||
| { | ||||
|     DeviceClass *dc = DEVICE_CLASS(klass); | ||||
| 
 | ||||
|     dc->realize = pnv_occ_realize; | ||||
| } | ||||
| 
 | ||||
| static const TypeInfo pnv_occ_type_info = { | ||||
|     .name          = TYPE_PNV_OCC, | ||||
|     .parent        = TYPE_DEVICE, | ||||
|     .instance_size = sizeof(PnvOCC), | ||||
|     .class_init    = pnv_occ_class_init, | ||||
| }; | ||||
| 
 | ||||
| static void pnv_occ_register_types(void) | ||||
| { | ||||
|     type_register_static(&pnv_occ_type_info); | ||||
| } | ||||
| 
 | ||||
| type_init(pnv_occ_register_types) | ||||
							
								
								
									
										571
									
								
								hw/ppc/pnv_psi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										571
									
								
								hw/ppc/pnv_psi.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,571 @@ | ||||
| /*
 | ||||
|  * QEMU PowerPC PowerNV Processor Service Interface (PSI) model | ||||
|  * | ||||
|  * Copyright (c) 2015-2017, 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 "hw/hw.h" | ||||
| #include "target/ppc/cpu.h" | ||||
| #include "qemu/log.h" | ||||
| #include "qapi/error.h" | ||||
| 
 | ||||
| #include "exec/address-spaces.h" | ||||
| 
 | ||||
| #include "hw/ppc/fdt.h" | ||||
| #include "hw/ppc/pnv.h" | ||||
| #include "hw/ppc/pnv_xscom.h" | ||||
| #include "hw/ppc/pnv_psi.h" | ||||
| 
 | ||||
| #include <libfdt.h> | ||||
| 
 | ||||
| #define PSIHB_XSCOM_FIR_RW      0x00 | ||||
| #define PSIHB_XSCOM_FIR_AND     0x01 | ||||
| #define PSIHB_XSCOM_FIR_OR      0x02 | ||||
| #define PSIHB_XSCOM_FIRMASK_RW  0x03 | ||||
| #define PSIHB_XSCOM_FIRMASK_AND 0x04 | ||||
| #define PSIHB_XSCOM_FIRMASK_OR  0x05 | ||||
| #define PSIHB_XSCOM_FIRACT0     0x06 | ||||
| #define PSIHB_XSCOM_FIRACT1     0x07 | ||||
| 
 | ||||
| /* Host Bridge Base Address Register */ | ||||
| #define PSIHB_XSCOM_BAR         0x0a | ||||
| #define   PSIHB_BAR_EN                  0x0000000000000001ull | ||||
| 
 | ||||
| /* FSP Base Address Register */ | ||||
| #define PSIHB_XSCOM_FSPBAR      0x0b | ||||
| 
 | ||||
| /* PSI Host Bridge Control/Status Register */ | ||||
| #define PSIHB_XSCOM_CR          0x0e | ||||
| #define   PSIHB_CR_FSP_CMD_ENABLE       0x8000000000000000ull | ||||
| #define   PSIHB_CR_FSP_MMIO_ENABLE      0x4000000000000000ull | ||||
| #define   PSIHB_CR_FSP_IRQ_ENABLE       0x1000000000000000ull | ||||
| #define   PSIHB_CR_FSP_ERR_RSP_ENABLE   0x0800000000000000ull | ||||
| #define   PSIHB_CR_PSI_LINK_ENABLE      0x0400000000000000ull | ||||
| #define   PSIHB_CR_FSP_RESET            0x0200000000000000ull | ||||
| #define   PSIHB_CR_PSIHB_RESET          0x0100000000000000ull | ||||
| #define   PSIHB_CR_PSI_IRQ              0x0000800000000000ull | ||||
| #define   PSIHB_CR_FSP_IRQ              0x0000400000000000ull | ||||
| #define   PSIHB_CR_FSP_LINK_ACTIVE      0x0000200000000000ull | ||||
| #define   PSIHB_CR_IRQ_CMD_EXPECT       0x0000010000000000ull | ||||
|           /* and more ... */ | ||||
| 
 | ||||
| /* PSIHB Status / Error Mask Register */ | ||||
| #define PSIHB_XSCOM_SEMR        0x0f | ||||
| 
 | ||||
| /* XIVR, to signal interrupts to the CEC firmware. more XIVR below. */ | ||||
| #define PSIHB_XSCOM_XIVR_FSP    0x10 | ||||
| #define   PSIHB_XIVR_SERVER_SH          40 | ||||
| #define   PSIHB_XIVR_SERVER_MSK         (0xffffull << PSIHB_XIVR_SERVER_SH) | ||||
| #define   PSIHB_XIVR_PRIO_SH            32 | ||||
| #define   PSIHB_XIVR_PRIO_MSK           (0xffull << PSIHB_XIVR_PRIO_SH) | ||||
| #define   PSIHB_XIVR_SRC_SH             29 | ||||
| #define   PSIHB_XIVR_SRC_MSK            (0x7ull << PSIHB_XIVR_SRC_SH) | ||||
| #define   PSIHB_XIVR_PENDING            0x01000000ull | ||||
| 
 | ||||
| /* PSI Host Bridge Set Control/ Status Register */ | ||||
| #define PSIHB_XSCOM_SCR         0x12 | ||||
| 
 | ||||
| /* PSI Host Bridge Clear Control/ Status Register */ | ||||
| #define PSIHB_XSCOM_CCR         0x13 | ||||
| 
 | ||||
| /* DMA Upper Address Register */ | ||||
| #define PSIHB_XSCOM_DMA_UPADD   0x14 | ||||
| 
 | ||||
| /* Interrupt Status */ | ||||
| #define PSIHB_XSCOM_IRQ_STAT    0x15 | ||||
| #define   PSIHB_IRQ_STAT_OCC            0x0000001000000000ull | ||||
| #define   PSIHB_IRQ_STAT_FSI            0x0000000800000000ull | ||||
| #define   PSIHB_IRQ_STAT_LPCI2C         0x0000000400000000ull | ||||
| #define   PSIHB_IRQ_STAT_LOCERR         0x0000000200000000ull | ||||
| #define   PSIHB_IRQ_STAT_EXT            0x0000000100000000ull | ||||
| 
 | ||||
| /* remaining XIVR */ | ||||
| #define PSIHB_XSCOM_XIVR_OCC    0x16 | ||||
| #define PSIHB_XSCOM_XIVR_FSI    0x17 | ||||
| #define PSIHB_XSCOM_XIVR_LPCI2C 0x18 | ||||
| #define PSIHB_XSCOM_XIVR_LOCERR 0x19 | ||||
| #define PSIHB_XSCOM_XIVR_EXT    0x1a | ||||
| 
 | ||||
| /* Interrupt Requester Source Compare Register */ | ||||
| #define PSIHB_XSCOM_IRSN        0x1b | ||||
| #define   PSIHB_IRSN_COMP_SH            45 | ||||
| #define   PSIHB_IRSN_COMP_MSK           (0x7ffffull << PSIHB_IRSN_COMP_SH) | ||||
| #define   PSIHB_IRSN_IRQ_MUX            0x0000000800000000ull | ||||
| #define   PSIHB_IRSN_IRQ_RESET          0x0000000400000000ull | ||||
| #define   PSIHB_IRSN_DOWNSTREAM_EN      0x0000000200000000ull | ||||
| #define   PSIHB_IRSN_UPSTREAM_EN        0x0000000100000000ull | ||||
| #define   PSIHB_IRSN_COMPMASK_SH        13 | ||||
| #define   PSIHB_IRSN_COMPMASK_MSK       (0x7ffffull << PSIHB_IRSN_COMPMASK_SH) | ||||
| 
 | ||||
| #define PSIHB_BAR_MASK                  0x0003fffffff00000ull | ||||
| #define PSIHB_FSPBAR_MASK               0x0003ffff00000000ull | ||||
| 
 | ||||
| static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) | ||||
| { | ||||
|     MemoryRegion *sysmem = get_system_memory(); | ||||
|     uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; | ||||
| 
 | ||||
|     psi->regs[PSIHB_XSCOM_BAR] = bar & (PSIHB_BAR_MASK | PSIHB_BAR_EN); | ||||
| 
 | ||||
|     /* Update MR, always remove it first */ | ||||
|     if (old & PSIHB_BAR_EN) { | ||||
|         memory_region_del_subregion(sysmem, &psi->regs_mr); | ||||
|     } | ||||
| 
 | ||||
|     /* Then add it back if needed */ | ||||
|     if (bar & PSIHB_BAR_EN) { | ||||
|         uint64_t addr = bar & PSIHB_BAR_MASK; | ||||
|         memory_region_add_subregion(sysmem, addr, &psi->regs_mr); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_update_fsp_mr(PnvPsi *psi) | ||||
| { | ||||
|     /* TODO: Update FSP MR if/when we support FSP BAR */ | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr) | ||||
| { | ||||
|     uint64_t old = psi->regs[PSIHB_XSCOM_CR]; | ||||
| 
 | ||||
|     psi->regs[PSIHB_XSCOM_CR] = cr; | ||||
| 
 | ||||
|     /* Check some bit changes */ | ||||
|     if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) { | ||||
|         pnv_psi_update_fsp_mr(psi); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val) | ||||
| { | ||||
|     ICSState *ics = &psi->ics; | ||||
| 
 | ||||
|     /* In this model we ignore the up/down enable bits for now
 | ||||
|      * as SW doesn't use them (other than setting them at boot). | ||||
|      * We ignore IRQ_MUX, its meaning isn't clear and we don't use | ||||
|      * it and finally we ignore reset (XXX fix that ?) | ||||
|      */ | ||||
|     psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK | | ||||
|                                          PSIHB_IRSN_IRQ_MUX | | ||||
|                                          PSIHB_IRSN_IRQ_RESET | | ||||
|                                          PSIHB_IRSN_DOWNSTREAM_EN | | ||||
|                                          PSIHB_IRSN_UPSTREAM_EN); | ||||
| 
 | ||||
|     /* We ignore the compare mask as well, our ICS emulation is too
 | ||||
|      * simplistic to make any use if it, and we extract the offset | ||||
|      * from the compare value | ||||
|      */ | ||||
|     ics->offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * FSP and PSI interrupts are muxed under the same number. | ||||
|  */ | ||||
| static const uint32_t xivr_regs[] = { | ||||
|     [PSIHB_IRQ_PSI]       = PSIHB_XSCOM_XIVR_FSP, | ||||
|     [PSIHB_IRQ_FSP]       = PSIHB_XSCOM_XIVR_FSP, | ||||
|     [PSIHB_IRQ_OCC]       = PSIHB_XSCOM_XIVR_OCC, | ||||
|     [PSIHB_IRQ_FSI]       = PSIHB_XSCOM_XIVR_FSI, | ||||
|     [PSIHB_IRQ_LPC_I2C]   = PSIHB_XSCOM_XIVR_LPCI2C, | ||||
|     [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_XIVR_LOCERR, | ||||
|     [PSIHB_IRQ_EXTERNAL]  = PSIHB_XSCOM_XIVR_EXT, | ||||
| }; | ||||
| 
 | ||||
| static const uint32_t stat_regs[] = { | ||||
|     [PSIHB_IRQ_PSI]       = PSIHB_XSCOM_CR, | ||||
|     [PSIHB_IRQ_FSP]       = PSIHB_XSCOM_CR, | ||||
|     [PSIHB_IRQ_OCC]       = PSIHB_XSCOM_IRQ_STAT, | ||||
|     [PSIHB_IRQ_FSI]       = PSIHB_XSCOM_IRQ_STAT, | ||||
|     [PSIHB_IRQ_LPC_I2C]   = PSIHB_XSCOM_IRQ_STAT, | ||||
|     [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_IRQ_STAT, | ||||
|     [PSIHB_IRQ_EXTERNAL]  = PSIHB_XSCOM_IRQ_STAT, | ||||
| }; | ||||
| 
 | ||||
| static const uint64_t stat_bits[] = { | ||||
|     [PSIHB_IRQ_PSI]       = PSIHB_CR_PSI_IRQ, | ||||
|     [PSIHB_IRQ_FSP]       = PSIHB_CR_FSP_IRQ, | ||||
|     [PSIHB_IRQ_OCC]       = PSIHB_IRQ_STAT_OCC, | ||||
|     [PSIHB_IRQ_FSI]       = PSIHB_IRQ_STAT_FSI, | ||||
|     [PSIHB_IRQ_LPC_I2C]   = PSIHB_IRQ_STAT_LPCI2C, | ||||
|     [PSIHB_IRQ_LOCAL_ERR] = PSIHB_IRQ_STAT_LOCERR, | ||||
|     [PSIHB_IRQ_EXTERNAL]  = PSIHB_IRQ_STAT_EXT, | ||||
| }; | ||||
| 
 | ||||
| void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) | ||||
| { | ||||
|     ICSState *ics = &psi->ics; | ||||
|     uint32_t xivr_reg; | ||||
|     uint32_t stat_reg; | ||||
|     uint32_t src; | ||||
|     bool masked; | ||||
| 
 | ||||
|     if (irq > PSIHB_IRQ_EXTERNAL) { | ||||
|         qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     xivr_reg = xivr_regs[irq]; | ||||
|     stat_reg = stat_regs[irq]; | ||||
| 
 | ||||
|     src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; | ||||
|     if (state) { | ||||
|         psi->regs[stat_reg] |= stat_bits[irq]; | ||||
|         /* TODO: optimization, check mask here. That means
 | ||||
|          * re-evaluating when unmasking | ||||
|          */ | ||||
|         qemu_irq_raise(ics->qirqs[src]); | ||||
|     } else { | ||||
|         psi->regs[stat_reg] &= ~stat_bits[irq]; | ||||
| 
 | ||||
|         /* FSP and PSI are muxed so don't lower if either is still set */ | ||||
|         if (stat_reg != PSIHB_XSCOM_CR || | ||||
|             !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { | ||||
|             qemu_irq_lower(ics->qirqs[src]); | ||||
|         } else { | ||||
|             state = true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Note about the emulation of the pending bit: This isn't
 | ||||
|      * entirely correct. The pending bit should be cleared when the | ||||
|      * EOI has been received. However, we don't have callbacks on EOI | ||||
|      * (especially not under KVM) so no way to emulate that properly, | ||||
|      * so instead we just set that bit as the logical "output" of the | ||||
|      * XIVR (ie pending & !masked) | ||||
|      * | ||||
|      * CLG: We could define a new ICS object with a custom eoi() | ||||
|      * handler to clear the pending bit. But I am not sure this would | ||||
|      * be useful for the software anyhow. | ||||
|      */ | ||||
|     masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK; | ||||
|     if (state && !masked) { | ||||
|         psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING; | ||||
|     } else { | ||||
|         psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val) | ||||
| { | ||||
|     ICSState *ics = &psi->ics; | ||||
|     uint16_t server; | ||||
|     uint8_t prio; | ||||
|     uint8_t src; | ||||
| 
 | ||||
|     psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) | | ||||
|             (val & (PSIHB_XIVR_SERVER_MSK | | ||||
|                     PSIHB_XIVR_PRIO_MSK | | ||||
|                     PSIHB_XIVR_SRC_MSK)); | ||||
|     val = psi->regs[reg]; | ||||
|     server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH; | ||||
|     prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH; | ||||
|     src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; | ||||
| 
 | ||||
|     if (src >= PSI_NUM_INTERRUPTS) { | ||||
|         qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", src); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /* Remove pending bit if the IRQ is masked */ | ||||
|     if ((psi->regs[reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK) { | ||||
|         psi->regs[reg] &= ~PSIHB_XIVR_PENDING; | ||||
|     } | ||||
| 
 | ||||
|     /* The low order 2 bits are the link pointer (Type II interrupts).
 | ||||
|      * Shift back to get a valid IRQ server. | ||||
|      */ | ||||
|     server >>= 2; | ||||
| 
 | ||||
|     /* Now because of source remapping, weird things can happen
 | ||||
|      * if you change the source number dynamically, our simple ICS | ||||
|      * doesn't deal with remapping. So we just poke a different | ||||
|      * ICS entry based on what source number was written. This will | ||||
|      * do for now but a more accurate implementation would instead | ||||
|      * use a fixed server/prio and a remapper of the generated irq. | ||||
|      */ | ||||
|     ics_simple_write_xive(ics, src, server, prio, prio); | ||||
| } | ||||
| 
 | ||||
| static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio) | ||||
| { | ||||
|     uint64_t val = 0xffffffffffffffffull; | ||||
| 
 | ||||
|     switch (offset) { | ||||
|     case PSIHB_XSCOM_FIR_RW: | ||||
|     case PSIHB_XSCOM_FIRACT0: | ||||
|     case PSIHB_XSCOM_FIRACT1: | ||||
|     case PSIHB_XSCOM_BAR: | ||||
|     case PSIHB_XSCOM_FSPBAR: | ||||
|     case PSIHB_XSCOM_CR: | ||||
|     case PSIHB_XSCOM_XIVR_FSP: | ||||
|     case PSIHB_XSCOM_XIVR_OCC: | ||||
|     case PSIHB_XSCOM_XIVR_FSI: | ||||
|     case PSIHB_XSCOM_XIVR_LPCI2C: | ||||
|     case PSIHB_XSCOM_XIVR_LOCERR: | ||||
|     case PSIHB_XSCOM_XIVR_EXT: | ||||
|     case PSIHB_XSCOM_IRQ_STAT: | ||||
|     case PSIHB_XSCOM_SEMR: | ||||
|     case PSIHB_XSCOM_DMA_UPADD: | ||||
|     case PSIHB_XSCOM_IRSN: | ||||
|         val = psi->regs[offset]; | ||||
|         break; | ||||
|     default: | ||||
|         qemu_log_mask(LOG_UNIMP, "PSI: read at Ox%" PRIx32 "\n", offset); | ||||
|     } | ||||
|     return val; | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val, | ||||
|                               bool mmio) | ||||
| { | ||||
|     switch (offset) { | ||||
|     case PSIHB_XSCOM_FIR_RW: | ||||
|     case PSIHB_XSCOM_FIRACT0: | ||||
|     case PSIHB_XSCOM_FIRACT1: | ||||
|     case PSIHB_XSCOM_SEMR: | ||||
|     case PSIHB_XSCOM_DMA_UPADD: | ||||
|         psi->regs[offset] = val; | ||||
|         break; | ||||
|     case PSIHB_XSCOM_FIR_OR: | ||||
|         psi->regs[PSIHB_XSCOM_FIR_RW] |= val; | ||||
|         break; | ||||
|     case PSIHB_XSCOM_FIR_AND: | ||||
|         psi->regs[PSIHB_XSCOM_FIR_RW] &= val; | ||||
|         break; | ||||
|     case PSIHB_XSCOM_BAR: | ||||
|         /* Only XSCOM can write this one */ | ||||
|         if (!mmio) { | ||||
|             pnv_psi_set_bar(psi, val); | ||||
|         } else { | ||||
|             qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of BAR\n"); | ||||
|         } | ||||
|         break; | ||||
|     case PSIHB_XSCOM_FSPBAR: | ||||
|         psi->regs[PSIHB_XSCOM_FSPBAR] = val & PSIHB_FSPBAR_MASK; | ||||
|         pnv_psi_update_fsp_mr(psi); | ||||
|         break; | ||||
|     case PSIHB_XSCOM_CR: | ||||
|         pnv_psi_set_cr(psi, val); | ||||
|         break; | ||||
|     case PSIHB_XSCOM_SCR: | ||||
|         pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val); | ||||
|         break; | ||||
|     case PSIHB_XSCOM_CCR: | ||||
|         pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val); | ||||
|         break; | ||||
|     case PSIHB_XSCOM_XIVR_FSP: | ||||
|     case PSIHB_XSCOM_XIVR_OCC: | ||||
|     case PSIHB_XSCOM_XIVR_FSI: | ||||
|     case PSIHB_XSCOM_XIVR_LPCI2C: | ||||
|     case PSIHB_XSCOM_XIVR_LOCERR: | ||||
|     case PSIHB_XSCOM_XIVR_EXT: | ||||
|         pnv_psi_set_xivr(psi, offset, val); | ||||
|         break; | ||||
|     case PSIHB_XSCOM_IRQ_STAT: | ||||
|         /* Read only */ | ||||
|         qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of IRQ_STAT\n"); | ||||
|         break; | ||||
|     case PSIHB_XSCOM_IRSN: | ||||
|         pnv_psi_set_irsn(psi, val); | ||||
|         break; | ||||
|     default: | ||||
|         qemu_log_mask(LOG_UNIMP, "PSI: write at Ox%" PRIx32 "\n", offset); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * The values of the registers when accessed through the MMIO region | ||||
|  * follow the relation : xscom = (mmio + 0x50) >> 3 | ||||
|  */ | ||||
| static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) | ||||
| { | ||||
|     return pnv_psi_reg_read(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, true); | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_mmio_write(void *opaque, hwaddr addr, | ||||
|                               uint64_t val, unsigned size) | ||||
| { | ||||
|     pnv_psi_reg_write(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, val, true); | ||||
| } | ||||
| 
 | ||||
| static const MemoryRegionOps psi_mmio_ops = { | ||||
|     .read = pnv_psi_mmio_read, | ||||
|     .write = pnv_psi_mmio_write, | ||||
|     .endianness = DEVICE_BIG_ENDIAN, | ||||
|     .valid = { | ||||
|         .min_access_size = 8, | ||||
|         .max_access_size = 8, | ||||
|     }, | ||||
|     .impl = { | ||||
|         .min_access_size = 8, | ||||
|         .max_access_size = 8, | ||||
|     }, | ||||
| }; | ||||
| 
 | ||||
| static uint64_t pnv_psi_xscom_read(void *opaque, hwaddr addr, unsigned size) | ||||
| { | ||||
|     return pnv_psi_reg_read(opaque, addr >> 3, false); | ||||
| } | ||||
| 
 | ||||
| static void pnv_psi_xscom_write(void *opaque, hwaddr addr, | ||||
|                                 uint64_t val, unsigned size) | ||||
| { | ||||
|     pnv_psi_reg_write(opaque, addr >> 3, val, false); | ||||
| } | ||||
| 
 | ||||
| static const MemoryRegionOps pnv_psi_xscom_ops = { | ||||
|     .read = pnv_psi_xscom_read, | ||||
|     .write = pnv_psi_xscom_write, | ||||
|     .endianness = DEVICE_BIG_ENDIAN, | ||||
|     .valid = { | ||||
|         .min_access_size = 8, | ||||
|         .max_access_size = 8, | ||||
|     }, | ||||
|     .impl = { | ||||
|         .min_access_size = 8, | ||||
|         .max_access_size = 8, | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static void pnv_psi_init(Object *obj) | ||||
| { | ||||
|     PnvPsi *psi = PNV_PSI(obj); | ||||
| 
 | ||||
|     object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE); | ||||
|     object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL); | ||||
| } | ||||
| 
 | ||||
| static const uint8_t irq_to_xivr[] = { | ||||
|     PSIHB_XSCOM_XIVR_FSP, | ||||
|     PSIHB_XSCOM_XIVR_OCC, | ||||
|     PSIHB_XSCOM_XIVR_FSI, | ||||
|     PSIHB_XSCOM_XIVR_LPCI2C, | ||||
|     PSIHB_XSCOM_XIVR_LOCERR, | ||||
|     PSIHB_XSCOM_XIVR_EXT, | ||||
| }; | ||||
| 
 | ||||
| static void pnv_psi_realize(DeviceState *dev, Error **errp) | ||||
| { | ||||
|     PnvPsi *psi = PNV_PSI(dev); | ||||
|     ICSState *ics = &psi->ics; | ||||
|     Object *obj; | ||||
|     Error *err = NULL; | ||||
|     unsigned int i; | ||||
| 
 | ||||
|     obj = object_property_get_link(OBJECT(dev), "xics", &err); | ||||
|     if (!obj) { | ||||
|         error_setg(errp, "%s: required link 'xics' not found: %s", | ||||
|                    __func__, error_get_pretty(err)); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     /* Create PSI interrupt control source */ | ||||
|     object_property_add_const_link(OBJECT(ics), "xics", obj,  &error_abort); | ||||
|     object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); | ||||
|     if (err) { | ||||
|         error_propagate(errp, err); | ||||
|         return; | ||||
|     } | ||||
|     object_property_set_bool(OBJECT(ics), true, "realized",  &err); | ||||
|     if (err) { | ||||
|         error_propagate(errp, err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < ics->nr_irqs; i++) { | ||||
|         ics_set_irq_type(ics, i, true); | ||||
|     } | ||||
| 
 | ||||
|     /* XSCOM region for PSI registers */ | ||||
|     pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops, | ||||
|                 psi, "xscom-psi", PNV_XSCOM_PSIHB_SIZE); | ||||
| 
 | ||||
|     /* Initialize MMIO region */ | ||||
|     memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, | ||||
|                           "psihb", PNV_PSIHB_SIZE); | ||||
| 
 | ||||
|     /* Default BAR for MMIO region */ | ||||
|     pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); | ||||
| 
 | ||||
|     /* Default sources in XIVR */ | ||||
|     for (i = 0; i < PSI_NUM_INTERRUPTS; i++) { | ||||
|         uint8_t xivr = irq_to_xivr[i]; | ||||
|         psi->regs[xivr] = PSIHB_XIVR_PRIO_MSK | | ||||
|             ((uint64_t) i << PSIHB_XIVR_SRC_SH); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static int pnv_psi_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset) | ||||
| { | ||||
|     const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; | ||||
|     char *name; | ||||
|     int offset; | ||||
|     uint32_t lpc_pcba = PNV_XSCOM_PSIHB_BASE; | ||||
|     uint32_t reg[] = { | ||||
|         cpu_to_be32(lpc_pcba), | ||||
|         cpu_to_be32(PNV_XSCOM_PSIHB_SIZE) | ||||
|     }; | ||||
| 
 | ||||
|     name = g_strdup_printf("psihb@%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, "compatible", compat, | ||||
|                       sizeof(compat)))); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static Property pnv_psi_properties[] = { | ||||
|     DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), | ||||
|     DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), | ||||
|     DEFINE_PROP_END_OF_LIST(), | ||||
| }; | ||||
| 
 | ||||
| static void pnv_psi_class_init(ObjectClass *klass, void *data) | ||||
| { | ||||
|     DeviceClass *dc = DEVICE_CLASS(klass); | ||||
|     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); | ||||
| 
 | ||||
|     xdc->populate = pnv_psi_populate; | ||||
| 
 | ||||
|     dc->realize = pnv_psi_realize; | ||||
|     dc->props = pnv_psi_properties; | ||||
| } | ||||
| 
 | ||||
| static const TypeInfo pnv_psi_info = { | ||||
|     .name          = TYPE_PNV_PSI, | ||||
|     .parent        = TYPE_SYS_BUS_DEVICE, | ||||
|     .instance_size = sizeof(PnvPsi), | ||||
|     .instance_init = pnv_psi_init, | ||||
|     .class_init    = pnv_psi_class_init, | ||||
|     .interfaces    = (InterfaceInfo[]) { | ||||
|         { TYPE_PNV_XSCOM_INTERFACE }, | ||||
|         { } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| static void pnv_psi_register_types(void) | ||||
| { | ||||
|     type_register_static(&pnv_psi_info); | ||||
| } | ||||
| 
 | ||||
| type_init(pnv_psi_register_types) | ||||
							
								
								
									
										375
									
								
								hw/ppc/spapr.c
									
									
									
									
									
								
							
							
						
						
									
										375
									
								
								hw/ppc/spapr.c
									
									
									
									
									
								
							| @ -40,6 +40,7 @@ | ||||
| #include "kvm_ppc.h" | ||||
| #include "migration/migration.h" | ||||
| #include "mmu-hash64.h" | ||||
| #include "mmu-book3s-v3.h" | ||||
| #include "qom/cpu.h" | ||||
| 
 | ||||
| #include "hw/boards.h" | ||||
| @ -96,66 +97,40 @@ | ||||
| 
 | ||||
| #define HTAB_SIZE(spapr)        (1ULL << ((spapr)->htab_shift)) | ||||
| 
 | ||||
| static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics, | ||||
|                            const char *type_icp, int nr_servers, | ||||
|                            int nr_irqs, Error **errp) | ||||
| static ICSState *spapr_ics_create(sPAPRMachineState *spapr, | ||||
|                                   const char *type_ics, | ||||
|                                   int nr_irqs, Error **errp) | ||||
| { | ||||
|     XICSFabric *xi = XICS_FABRIC(spapr); | ||||
|     Error *err = NULL, *local_err = NULL; | ||||
|     ICSState *ics = NULL; | ||||
|     int i; | ||||
|     Object *obj; | ||||
| 
 | ||||
|     ics = ICS_SIMPLE(object_new(type_ics)); | ||||
|     object_property_add_child(OBJECT(spapr), "ics", OBJECT(ics), NULL); | ||||
|     object_property_set_int(OBJECT(ics), nr_irqs, "nr-irqs", &err); | ||||
|     object_property_add_const_link(OBJECT(ics), "xics", OBJECT(xi), NULL); | ||||
|     object_property_set_bool(OBJECT(ics), true, "realized", &local_err); | ||||
|     obj = object_new(type_ics); | ||||
|     object_property_add_child(OBJECT(spapr), "ics", obj, NULL); | ||||
|     object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); | ||||
|     object_property_set_int(obj, nr_irqs, "nr-irqs", &err); | ||||
|     object_property_set_bool(obj, true, "realized", &local_err); | ||||
|     error_propagate(&err, local_err); | ||||
|     if (err) { | ||||
|         goto error; | ||||
|         error_propagate(errp, err); | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     spapr->icps = g_malloc0(nr_servers * sizeof(ICPState)); | ||||
|     spapr->nr_servers = nr_servers; | ||||
| 
 | ||||
|     for (i = 0; i < nr_servers; i++) { | ||||
|         ICPState *icp = &spapr->icps[i]; | ||||
| 
 | ||||
|         object_initialize(icp, sizeof(*icp), type_icp); | ||||
|         object_property_add_child(OBJECT(spapr), "icp[*]", OBJECT(icp), NULL); | ||||
|         object_property_add_const_link(OBJECT(icp), "xics", OBJECT(xi), NULL); | ||||
|         object_property_set_bool(OBJECT(icp), true, "realized", &err); | ||||
|         if (err) { | ||||
|             goto error; | ||||
|         } | ||||
|         object_unref(OBJECT(icp)); | ||||
|     } | ||||
| 
 | ||||
|     spapr->ics = ics; | ||||
|     return 0; | ||||
| 
 | ||||
| error: | ||||
|     error_propagate(errp, err); | ||||
|     if (ics) { | ||||
|         object_unparent(OBJECT(ics)); | ||||
|     } | ||||
|     return -1; | ||||
|     return ICS_SIMPLE(obj); | ||||
| } | ||||
| 
 | ||||
| static int xics_system_init(MachineState *machine, | ||||
|                             int nr_servers, int nr_irqs, Error **errp) | ||||
| static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) | ||||
| { | ||||
|     int rc = -1; | ||||
|     sPAPRMachineState *spapr = SPAPR_MACHINE(machine); | ||||
| 
 | ||||
|     if (kvm_enabled()) { | ||||
|         Error *err = NULL; | ||||
| 
 | ||||
|         if (machine_kernel_irqchip_allowed(machine) && | ||||
|             !xics_kvm_init(SPAPR_MACHINE(machine), errp)) { | ||||
|             rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_KVM, | ||||
|                                  TYPE_KVM_ICP, nr_servers, nr_irqs, &err); | ||||
|             !xics_kvm_init(spapr, errp)) { | ||||
|             spapr->icp_type = TYPE_KVM_ICP; | ||||
|             spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, &err); | ||||
|         } | ||||
|         if (machine_kernel_irqchip_required(machine) && rc < 0) { | ||||
|         if (machine_kernel_irqchip_required(machine) && !spapr->ics) { | ||||
|             error_reportf_err(err, | ||||
|                               "kernel_irqchip requested but unavailable: "); | ||||
|         } else { | ||||
| @ -163,13 +138,11 @@ static int xics_system_init(MachineState *machine, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (rc < 0) { | ||||
|         xics_spapr_init(SPAPR_MACHINE(machine), errp); | ||||
|         rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_SIMPLE, | ||||
|                                TYPE_ICP, nr_servers, nr_irqs, errp); | ||||
|     if (!spapr->ics) { | ||||
|         xics_spapr_init(spapr, errp); | ||||
|         spapr->icp_type = TYPE_ICP; | ||||
|         spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, errp); | ||||
|     } | ||||
| 
 | ||||
|     return rc; | ||||
| } | ||||
| 
 | ||||
| static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, | ||||
| @ -226,6 +199,85 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, CPUState *cs) | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| /* Populate the "ibm,pa-features" property */ | ||||
| static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset, | ||||
|                                       bool legacy_guest) | ||||
| { | ||||
|     uint8_t pa_features_206[] = { 6, 0, | ||||
|         0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; | ||||
|     uint8_t pa_features_207[] = { 24, 0, | ||||
|         0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, | ||||
|         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|         0x00, 0x00, 0x00, 0x00, 0x80, 0x00, | ||||
|         0x80, 0x00, 0x80, 0x00, 0x00, 0x00 }; | ||||
|     uint8_t pa_features_300[] = { 66, 0, | ||||
|         /* 0: MMU|FPU|SLB|RUN|DABR|NX, 1: fri[nzpm]|DABRX|SPRG3|SLB0|PP110 */ | ||||
|         /* 2: VPM|DS205|PPR|DS202|DS206, 3: LSD|URG, SSO, 5: LE|CFAR|EB|LSQ */ | ||||
|         0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /* 0 - 5 */ | ||||
|         /* 6: DS207 */ | ||||
|         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /* 6 - 11 */ | ||||
|         /* 16: Vector */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ | ||||
|         /* 18: Vec. Scalar, 20: Vec. XOR, 22: HTM */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 18 - 23 */ | ||||
|         /* 24: Ext. Dec, 26: 64 bit ftrs, 28: PM ftrs */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 24 - 29 */ | ||||
|         /* 30: MMR, 32: LE atomic, 34: EBB + ext EBB */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0xC0, 0x00, /* 30 - 35 */ | ||||
|         /* 36: SPR SO, 38: Copy/Paste, 40: Radix MMU */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 36 - 41 */ | ||||
|         /* 42: PM, 44: PC RA, 46: SC vec'd */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 42 - 47 */ | ||||
|         /* 48: SIMD, 50: QP BFP, 52: String */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 48 - 53 */ | ||||
|         /* 54: DecFP, 56: DecI, 58: SHA */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x80, 0x00, /* 54 - 59 */ | ||||
|         /* 60: NM atomic, 62: RNG */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 60 - 65 */ | ||||
|     }; | ||||
|     uint8_t *pa_features; | ||||
|     size_t pa_size; | ||||
| 
 | ||||
|     switch (POWERPC_MMU_VER(env->mmu_model)) { | ||||
|     case POWERPC_MMU_VER_2_06: | ||||
|         pa_features = pa_features_206; | ||||
|         pa_size = sizeof(pa_features_206); | ||||
|         break; | ||||
|     case POWERPC_MMU_VER_2_07: | ||||
|         pa_features = pa_features_207; | ||||
|         pa_size = sizeof(pa_features_207); | ||||
|         break; | ||||
|     case POWERPC_MMU_VER_3_00: | ||||
|         pa_features = pa_features_300; | ||||
|         pa_size = sizeof(pa_features_300); | ||||
|         break; | ||||
|     default: | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (env->ci_large_pages) { | ||||
|         /*
 | ||||
|          * Note: we keep CI large pages off by default because a 64K capable | ||||
|          * guest provisioned with large pages might otherwise try to map a qemu | ||||
|          * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages | ||||
|          * even if that qemu runs on a 4k host. | ||||
|          * We dd this bit back here if we are confident this is not an issue | ||||
|          */ | ||||
|         pa_features[3] |= 0x20; | ||||
|     } | ||||
|     if (kvmppc_has_cap_htm() && pa_size > 24) { | ||||
|         pa_features[24] |= 0x80;    /* Transactional memory support */ | ||||
|     } | ||||
|     if (legacy_guest && pa_size > 40) { | ||||
|         /* Workaround for broken kernels that attempt (guest) radix
 | ||||
|          * mode when they can't handle it, if they see the radix bit set | ||||
|          * in pa-features. So hide it from them. */ | ||||
|         pa_features[40 + 2] &= ~0x80; /* Radix MMU */ | ||||
|     } | ||||
| 
 | ||||
|     _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); | ||||
| } | ||||
| 
 | ||||
| static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) | ||||
| { | ||||
|     int ret = 0, offset, cpus_offset; | ||||
| @ -236,6 +288,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) | ||||
| 
 | ||||
|     CPU_FOREACH(cs) { | ||||
|         PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
|         CPUPPCState *env = &cpu->env; | ||||
|         DeviceClass *dc = DEVICE_GET_CLASS(cs); | ||||
|         int index = ppc_get_vcpu_dt_id(cpu); | ||||
|         int compat_smt = MIN(smp_threads, ppc_compat_max_threads(cpu)); | ||||
| @ -277,6 +330,9 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) | ||||
|         if (ret < 0) { | ||||
|             return ret; | ||||
|         } | ||||
| 
 | ||||
|         spapr_populate_pa_features(env, fdt, offset, | ||||
|                                          spapr->cas_legacy_guest_workaround); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| @ -378,67 +434,6 @@ static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt) | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /* Populate the "ibm,pa-features" property */ | ||||
| static void spapr_populate_pa_features(CPUPPCState *env, void *fdt, int offset) | ||||
| { | ||||
|     uint8_t pa_features_206[] = { 6, 0, | ||||
|         0xf6, 0x1f, 0xc7, 0x00, 0x80, 0xc0 }; | ||||
|     uint8_t pa_features_207[] = { 24, 0, | ||||
|         0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, | ||||
|         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|         0x00, 0x00, 0x00, 0x00, 0x80, 0x00, | ||||
|         0x80, 0x00, 0x80, 0x00, 0x00, 0x00 }; | ||||
|     /* Currently we don't advertise any of the "new" ISAv3.00 functionality */ | ||||
|     uint8_t pa_features_300[] = { 64, 0, | ||||
|         0xf6, 0x1f, 0xc7, 0xc0, 0x80, 0xf0, /*  0 -  5 */ | ||||
|         0x80, 0x00, 0x00, 0x00, 0x00, 0x00, /*  6 - 11 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x80, 0x00, /* 12 - 17 */ | ||||
|         0x80, 0x00, 0x80, 0x00, 0x00, 0x00, /* 18 - 23 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24 - 29 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 - 35 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 36 - 41 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 - 47 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 48 - 53 */ | ||||
|         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 54 - 59 */ | ||||
|         0x00, 0x00, 0x00, 0x00           }; /* 60 - 63 */ | ||||
| 
 | ||||
|     uint8_t *pa_features; | ||||
|     size_t pa_size; | ||||
| 
 | ||||
|     switch (POWERPC_MMU_VER(env->mmu_model)) { | ||||
|     case POWERPC_MMU_VER_2_06: | ||||
|         pa_features = pa_features_206; | ||||
|         pa_size = sizeof(pa_features_206); | ||||
|         break; | ||||
|     case POWERPC_MMU_VER_2_07: | ||||
|         pa_features = pa_features_207; | ||||
|         pa_size = sizeof(pa_features_207); | ||||
|         break; | ||||
|     case POWERPC_MMU_VER_3_00: | ||||
|         pa_features = pa_features_300; | ||||
|         pa_size = sizeof(pa_features_300); | ||||
|         break; | ||||
|     default: | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (env->ci_large_pages) { | ||||
|         /*
 | ||||
|          * Note: we keep CI large pages off by default because a 64K capable | ||||
|          * guest provisioned with large pages might otherwise try to map a qemu | ||||
|          * framebuffer (or other kind of memory mapped PCI BAR) using 64K pages | ||||
|          * even if that qemu runs on a 4k host. | ||||
|          * We dd this bit back here if we are confident this is not an issue | ||||
|          */ | ||||
|         pa_features[3] |= 0x20; | ||||
|     } | ||||
|     if (kvmppc_has_cap_htm() && pa_size > 24) { | ||||
|         pa_features[24] |= 0x80;    /* Transactional memory support */ | ||||
|     } | ||||
| 
 | ||||
|     _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); | ||||
| } | ||||
| 
 | ||||
| static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, | ||||
|                                   sPAPRMachineState *spapr) | ||||
| { | ||||
| @ -459,6 +454,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, | ||||
|     sPAPRDRConnector *drc; | ||||
|     sPAPRDRConnectorClass *drck; | ||||
|     int drc_index; | ||||
|     uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; | ||||
|     int i; | ||||
| 
 | ||||
|     drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index); | ||||
|     if (drc) { | ||||
| @ -533,7 +530,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, | ||||
|                           page_sizes_prop, page_sizes_prop_size))); | ||||
|     } | ||||
| 
 | ||||
|     spapr_populate_pa_features(env, fdt, offset); | ||||
|     spapr_populate_pa_features(env, fdt, offset, false); | ||||
| 
 | ||||
|     _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", | ||||
|                            cs->cpu_index / vcpus_per_socket))); | ||||
| @ -544,6 +541,17 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, | ||||
|     _FDT(spapr_fixup_cpu_numa_dt(fdt, offset, cs)); | ||||
| 
 | ||||
|     _FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); | ||||
| 
 | ||||
|     if (pcc->radix_page_info) { | ||||
|         for (i = 0; i < pcc->radix_page_info->count; i++) { | ||||
|             radix_AP_encodings[i] = | ||||
|                 cpu_to_be32(pcc->radix_page_info->entries[i]); | ||||
|         } | ||||
|         _FDT((fdt_setprop(fdt, offset, "ibm,processor-radix-AP-encodings", | ||||
|                           radix_AP_encodings, | ||||
|                           pcc->radix_page_info->count * | ||||
|                           sizeof(radix_AP_encodings[0])))); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) | ||||
| @ -842,6 +850,33 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) | ||||
|     spapr_dt_rtas_tokens(fdt, rtas); | ||||
| } | ||||
| 
 | ||||
| /* Prepare ibm,arch-vec-5-platform-support, which indicates the MMU features
 | ||||
|  * that the guest may request and thus the valid values for bytes 24..26 of | ||||
|  * option vector 5: */ | ||||
| static void spapr_dt_ov5_platform_support(void *fdt, int chosen) | ||||
| { | ||||
|     char val[2 * 3] = { | ||||
|         24, 0x00, /* Hash/Radix, filled in below. */ | ||||
|         25, 0x00, /* Hash options: Segment Tables == no, GTSE == no. */ | ||||
|         26, 0x40, /* Radix options: GTSE == yes. */ | ||||
|     }; | ||||
| 
 | ||||
|     if (kvm_enabled()) { | ||||
|         if (kvmppc_has_cap_mmu_radix() && kvmppc_has_cap_mmu_hash_v3()) { | ||||
|             val[1] = 0x80; /* OV5_MMU_BOTH */ | ||||
|         } else if (kvmppc_has_cap_mmu_radix()) { | ||||
|             val[1] = 0x40; /* OV5_MMU_RADIX_300 */ | ||||
|         } else { | ||||
|             val[1] = 0x00; /* Hash */ | ||||
|         } | ||||
|     } else { | ||||
|         /* TODO: TCG case, hash */ | ||||
|         val[1] = 0x00; | ||||
|     } | ||||
|     _FDT(fdt_setprop(fdt, chosen, "ibm,arch-vec-5-platform-support", | ||||
|                      val, sizeof(val))); | ||||
| } | ||||
| 
 | ||||
| static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) | ||||
| { | ||||
|     MachineState *machine = MACHINE(spapr); | ||||
| @ -895,6 +930,8 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) | ||||
|         _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path)); | ||||
|     } | ||||
| 
 | ||||
|     spapr_dt_ov5_platform_support(fdt, chosen); | ||||
| 
 | ||||
|     g_free(stdout_path); | ||||
|     g_free(bootlist); | ||||
| } | ||||
| @ -933,6 +970,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, | ||||
|     void *fdt; | ||||
|     sPAPRPHBState *phb; | ||||
|     char *buf; | ||||
|     int smt = kvmppc_smt_threads(); | ||||
| 
 | ||||
|     fdt = g_malloc0(FDT_MAX_SIZE); | ||||
|     _FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); | ||||
| @ -972,7 +1010,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, | ||||
|     _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); | ||||
| 
 | ||||
|     /* /interrupt controller */ | ||||
|     spapr_dt_xics(spapr->nr_servers, fdt, PHANDLE_XICP); | ||||
|     spapr_dt_xics(DIV_ROUND_UP(max_cpus * smt, smp_threads), fdt, PHANDLE_XICP); | ||||
| 
 | ||||
|     ret = spapr_populate_memory(spapr, fdt); | ||||
|     if (ret < 0) { | ||||
| @ -1100,7 +1138,7 @@ static int get_htab_fd(sPAPRMachineState *spapr) | ||||
|     return spapr->htab_fd; | ||||
| } | ||||
| 
 | ||||
| static void close_htab_fd(sPAPRMachineState *spapr) | ||||
| void close_htab_fd(sPAPRMachineState *spapr) | ||||
| { | ||||
|     if (spapr->htab_fd >= 0) { | ||||
|         close(spapr->htab_fd); | ||||
| @ -1227,6 +1265,19 @@ static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) | ||||
| { | ||||
|     spapr_reallocate_hpt(spapr, | ||||
|                      spapr_hpt_shift_for_ramsize(MACHINE(spapr)->maxram_size), | ||||
|                      &error_fatal); | ||||
|     if (spapr->vrma_adjust) { | ||||
|         spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), | ||||
|                                           spapr->htab_shift); | ||||
|     } | ||||
|     /* We're setting up a hash table, so that means we're not radix */ | ||||
|     spapr->patb_entry = 0; | ||||
| } | ||||
| 
 | ||||
| static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) | ||||
| { | ||||
|     bool matched = false; | ||||
| @ -1255,17 +1306,14 @@ static void ppc_spapr_reset(void) | ||||
|     /* Check for unknown sysbus devices */ | ||||
|     foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); | ||||
| 
 | ||||
|     spapr->patb_entry = 0; | ||||
| 
 | ||||
|     /* Allocate and/or reset the hash page table */ | ||||
|     spapr_reallocate_hpt(spapr, | ||||
|                          spapr_hpt_shift_for_ramsize(machine->maxram_size), | ||||
|                          &error_fatal); | ||||
| 
 | ||||
|     /* Update the RMA size if necessary */ | ||||
|     if (spapr->vrma_adjust) { | ||||
|         spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), | ||||
|                                           spapr->htab_shift); | ||||
|     if (kvm_enabled() && kvmppc_has_cap_mmu_radix()) { | ||||
|         /* If using KVM with radix mode available, VCPUs can be started
 | ||||
|          * without a HPT because KVM will start them in radix mode. | ||||
|          * Set the GR bit in PATB so that we know there is no HPT. */ | ||||
|         spapr->patb_entry = PATBE1_GR; | ||||
|     } else { | ||||
|         spapr->patb_entry = 0; | ||||
|         spapr_setup_hpt_and_vrma(spapr); | ||||
|     } | ||||
| 
 | ||||
|     qemu_devices_reset(); | ||||
| @ -1333,13 +1381,13 @@ static void spapr_create_nvram(sPAPRMachineState *spapr) | ||||
| 
 | ||||
| static void spapr_rtc_create(sPAPRMachineState *spapr) | ||||
| { | ||||
|     DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC); | ||||
| 
 | ||||
|     qdev_init_nofail(dev); | ||||
|     spapr->rtc = dev; | ||||
| 
 | ||||
|     object_property_add_alias(qdev_get_machine(), "rtc-time", | ||||
|                               OBJECT(spapr->rtc), "date", NULL); | ||||
|     object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC); | ||||
|     object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc), | ||||
|                               &error_fatal); | ||||
|     object_property_set_bool(OBJECT(&spapr->rtc), true, "realized", | ||||
|                               &error_fatal); | ||||
|     object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc), | ||||
|                               "date", &error_fatal); | ||||
| } | ||||
| 
 | ||||
| /* Returns whether we want to use VGA or not */ | ||||
| @ -1366,9 +1414,10 @@ static int spapr_post_load(void *opaque, int version_id) | ||||
|     int err = 0; | ||||
| 
 | ||||
|     if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { | ||||
|         int i; | ||||
|         for (i = 0; i < spapr->nr_servers; i++) { | ||||
|             icp_resend(&spapr->icps[i]); | ||||
|         CPUState *cs; | ||||
|         CPU_FOREACH(cs) { | ||||
|             PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
|             icp_resend(ICP(cpu->intc)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| @ -1377,7 +1426,7 @@ static int spapr_post_load(void *opaque, int version_id) | ||||
|      * So when migrating from those versions, poke the incoming offset | ||||
|      * value into the RTC device */ | ||||
|     if (version_id < 3) { | ||||
|         err = spapr_rtc_import_offset(spapr->rtc, spapr->rtc_offset); | ||||
|         err = spapr_rtc_import_offset(&spapr->rtc, spapr->rtc_offset); | ||||
|     } | ||||
| 
 | ||||
|     return err; | ||||
| @ -1990,7 +2039,6 @@ static void ppc_spapr_init(MachineState *machine) | ||||
|     hwaddr node0_size = spapr_node0_size(); | ||||
|     long load_limit, fw_size; | ||||
|     char *filename; | ||||
|     int smt = kvmppc_smt_threads(); | ||||
| 
 | ||||
|     msi_nonbroken = true; | ||||
| 
 | ||||
| @ -2041,8 +2089,7 @@ static void ppc_spapr_init(MachineState *machine) | ||||
|     load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; | ||||
| 
 | ||||
|     /* Set up Interrupt Controller before we create the VCPUs */ | ||||
|     xics_system_init(machine, DIV_ROUND_UP(max_cpus * smt, smp_threads), | ||||
|                      XICS_IRQS_SPAPR, &error_fatal); | ||||
|     xics_system_init(machine, XICS_IRQS_SPAPR, &error_fatal); | ||||
| 
 | ||||
|     /* Set up containers for ibm,client-set-architecture negotiated options */ | ||||
|     spapr->ov5 = spapr_ovec_new(); | ||||
| @ -2054,6 +2101,11 @@ static void ppc_spapr_init(MachineState *machine) | ||||
|     } | ||||
| 
 | ||||
|     spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); | ||||
|     if (kvmppc_has_cap_mmu_radix()) { | ||||
|         /* KVM always allows GTSE with radix... */ | ||||
|         spapr_ovec_set(spapr->ov5, OV5_MMU_RADIX_GTSE); | ||||
|     } | ||||
|     /* ... but not with hash (currently). */ | ||||
| 
 | ||||
|     /* advertise support for dedicated HP event source to guests */ | ||||
|     if (spapr->use_hotplug_event_source) { | ||||
| @ -2281,10 +2333,12 @@ static void ppc_spapr_init(MachineState *machine) | ||||
| 
 | ||||
|     qemu_register_boot_set(spapr_boot_set, spapr); | ||||
| 
 | ||||
|     /* to stop and start vmclock */ | ||||
|     if (kvm_enabled()) { | ||||
|         /* to stop and start vmclock */ | ||||
|         qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change, | ||||
|                                          &spapr->tb); | ||||
| 
 | ||||
|         kvmppc_spapr_enable_inkernel_multitce(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -3030,21 +3084,23 @@ static void spapr_ics_resend(XICSFabric *dev) | ||||
|     ics_resend(spapr->ics); | ||||
| } | ||||
| 
 | ||||
| static ICPState *spapr_icp_get(XICSFabric *xi, int server) | ||||
| static ICPState *spapr_icp_get(XICSFabric *xi, int cpu_dt_id) | ||||
| { | ||||
|     sPAPRMachineState *spapr = SPAPR_MACHINE(xi); | ||||
|     PowerPCCPU *cpu = ppc_get_vcpu_by_dt_id(cpu_dt_id); | ||||
| 
 | ||||
|     return (server < spapr->nr_servers) ? &spapr->icps[server] : NULL; | ||||
|     return cpu ? ICP(cpu->intc) : NULL; | ||||
| } | ||||
| 
 | ||||
| static void spapr_pic_print_info(InterruptStatsProvider *obj, | ||||
|                                  Monitor *mon) | ||||
| { | ||||
|     sPAPRMachineState *spapr = SPAPR_MACHINE(obj); | ||||
|     int i; | ||||
|     CPUState *cs; | ||||
| 
 | ||||
|     for (i = 0; i < spapr->nr_servers; i++) { | ||||
|         icp_pic_print_info(&spapr->icps[i], mon); | ||||
|     CPU_FOREACH(cs) { | ||||
|         PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
| 
 | ||||
|         icp_pic_print_info(ICP(cpu->intc), mon); | ||||
|     } | ||||
| 
 | ||||
|     ics_pic_print_info(spapr->ics, mon); | ||||
| @ -3158,18 +3214,37 @@ static const TypeInfo spapr_machine_info = { | ||||
|     type_init(spapr_machine_register_##suffix) | ||||
| 
 | ||||
| /*
 | ||||
|  * pseries-2.9 | ||||
|  * pseries-2.10 | ||||
|  */ | ||||
| static void spapr_machine_2_9_instance_options(MachineState *machine) | ||||
| static void spapr_machine_2_10_instance_options(MachineState *machine) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| static void spapr_machine_2_9_class_options(MachineClass *mc) | ||||
| static void spapr_machine_2_10_class_options(MachineClass *mc) | ||||
| { | ||||
|     /* Defaults for the latest behaviour inherited from the base class */ | ||||
| } | ||||
| 
 | ||||
| DEFINE_SPAPR_MACHINE(2_9, "2.9", true); | ||||
| DEFINE_SPAPR_MACHINE(2_10, "2.10", true); | ||||
| 
 | ||||
| /*
 | ||||
|  * pseries-2.9 | ||||
|  */ | ||||
| #define SPAPR_COMPAT_2_9                                               \ | ||||
|     HW_COMPAT_2_9 | ||||
| 
 | ||||
| static void spapr_machine_2_9_instance_options(MachineState *machine) | ||||
| { | ||||
|     spapr_machine_2_10_instance_options(machine); | ||||
| } | ||||
| 
 | ||||
| static void spapr_machine_2_9_class_options(MachineClass *mc) | ||||
| { | ||||
|     spapr_machine_2_10_class_options(mc); | ||||
|     SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_9); | ||||
| } | ||||
| 
 | ||||
| DEFINE_SPAPR_MACHINE(2_9, "2.9", false); | ||||
| 
 | ||||
| /*
 | ||||
|  * pseries-2.8 | ||||
|  | ||||
| @ -80,8 +80,6 @@ static void spapr_cpu_init(sPAPRMachineState *spapr, PowerPCCPU *cpu, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     xics_cpu_setup(XICS_FABRIC(spapr), cpu); | ||||
| 
 | ||||
|     qemu_register_reset(spapr_cpu_reset, cpu); | ||||
|     spapr_cpu_reset(cpu); | ||||
| } | ||||
| @ -129,6 +127,7 @@ static void spapr_cpu_core_unrealizefn(DeviceState *dev, Error **errp) | ||||
|         PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
| 
 | ||||
|         spapr_cpu_destroy(cpu); | ||||
|         object_unparent(cpu->intc); | ||||
|         cpu_remove_sync(cs); | ||||
|         object_unparent(obj); | ||||
|     } | ||||
| @ -141,18 +140,32 @@ static void spapr_cpu_core_realize_child(Object *child, Error **errp) | ||||
|     sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); | ||||
|     CPUState *cs = CPU(child); | ||||
|     PowerPCCPU *cpu = POWERPC_CPU(cs); | ||||
|     Object *obj; | ||||
| 
 | ||||
|     obj = object_new(spapr->icp_type); | ||||
|     object_property_add_child(OBJECT(cpu), "icp", obj, NULL); | ||||
|     object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort); | ||||
|     object_property_set_bool(obj, true, "realized", &local_err); | ||||
|     if (local_err) { | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     object_property_set_bool(child, true, "realized", &local_err); | ||||
|     if (local_err) { | ||||
|         object_unparent(obj); | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     spapr_cpu_init(spapr, cpu, &local_err); | ||||
|     if (local_err) { | ||||
|         object_unparent(obj); | ||||
|         error_propagate(errp, local_err); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     xics_cpu_setup(XICS_FABRIC(spapr), cpu, ICP(obj)); | ||||
| } | ||||
| 
 | ||||
| static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) | ||||
|  | ||||
| @ -422,7 +422,7 @@ static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, | ||||
|     maina->hdr.section_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA); | ||||
|     maina->hdr.section_length = cpu_to_be16(sizeof(*maina)); | ||||
|     /* FIXME: section version, subtype and creator id? */ | ||||
|     spapr_rtc_read(spapr->rtc, &tm, NULL); | ||||
|     spapr_rtc_read(&spapr->rtc, &tm, NULL); | ||||
|     year = tm.tm_year + 1900; | ||||
|     maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24) | ||||
|                                        | (to_bcd(year % 100) << 16) | ||||
|  | ||||
| @ -12,6 +12,8 @@ | ||||
| #include "trace.h" | ||||
| #include "kvm_ppc.h" | ||||
| #include "hw/ppc/spapr_ovec.h" | ||||
| #include "qemu/error-report.h" | ||||
| #include "mmu-book3s-v3.h" | ||||
| 
 | ||||
| struct SPRSyncState { | ||||
|     int spr; | ||||
| @ -878,6 +880,137 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static target_ulong h_clean_slb(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                                 target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", | ||||
|                   opcode, " (H_CLEAN_SLB)"); | ||||
|     return H_FUNCTION; | ||||
| } | ||||
| 
 | ||||
| static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                                      target_ulong opcode, target_ulong *args) | ||||
| { | ||||
|     qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", | ||||
|                   opcode, " (H_INVALIDATE_PID)"); | ||||
|     return H_FUNCTION; | ||||
| } | ||||
| 
 | ||||
| static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, | ||||
|                                        uint64_t patbe_old, uint64_t patbe_new) | ||||
| { | ||||
|     /*
 | ||||
|      * We have 4 Options: | ||||
|      * HASH->HASH || RADIX->RADIX || NOTHING->RADIX : Do Nothing | ||||
|      * HASH->RADIX                                  : Free HPT | ||||
|      * RADIX->HASH                                  : Allocate HPT | ||||
|      * NOTHING->HASH                                : Allocate HPT | ||||
|      * Note: NOTHING implies the case where we said the guest could choose | ||||
|      *       later and so assumed radix and now it's called H_REG_PROC_TBL | ||||
|      */ | ||||
| 
 | ||||
|     if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) { | ||||
|         /* We assume RADIX, so this catches all the "Do Nothing" cases */ | ||||
|     } else if (!(patbe_old & PATBE1_GR)) { | ||||
|         /* HASH->RADIX : Free HPT */ | ||||
|         g_free(spapr->htab); | ||||
|         spapr->htab = NULL; | ||||
|         spapr->htab_shift = 0; | ||||
|         close_htab_fd(spapr); | ||||
|     } else if (!(patbe_new & PATBE1_GR)) { | ||||
|         /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ | ||||
|         spapr_setup_hpt_and_vrma(spapr); | ||||
|     } | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| #define FLAGS_MASK              0x01FULL | ||||
| #define FLAG_MODIFY             0x10 | ||||
| #define FLAG_REGISTER           0x08 | ||||
| #define FLAG_RADIX              0x04 | ||||
| #define FLAG_HASH_PROC_TBL      0x02 | ||||
| #define FLAG_GTSE               0x01 | ||||
| 
 | ||||
| static target_ulong h_register_process_table(PowerPCCPU *cpu, | ||||
|                                              sPAPRMachineState *spapr, | ||||
|                                              target_ulong opcode, | ||||
|                                              target_ulong *args) | ||||
| { | ||||
|     CPUPPCState *env = &cpu->env; | ||||
|     target_ulong flags = args[0]; | ||||
|     target_ulong proc_tbl = args[1]; | ||||
|     target_ulong page_size = args[2]; | ||||
|     target_ulong table_size = args[3]; | ||||
|     uint64_t cproc; | ||||
| 
 | ||||
|     if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */ | ||||
|         return H_PARAMETER; | ||||
|     } | ||||
|     if (flags & FLAG_MODIFY) { | ||||
|         if (flags & FLAG_REGISTER) { | ||||
|             if (flags & FLAG_RADIX) { /* Register new RADIX process table */ | ||||
|                 if (proc_tbl & 0xfff || proc_tbl >> 60) { | ||||
|                     return H_P2; | ||||
|                 } else if (page_size) { | ||||
|                     return H_P3; | ||||
|                 } else if (table_size > 24) { | ||||
|                     return H_P4; | ||||
|                 } | ||||
|                 cproc = PATBE1_GR | proc_tbl | table_size; | ||||
|             } else { /* Register new HPT process table */ | ||||
|                 if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */ | ||||
|                     /* TODO - Not Supported */ | ||||
|                     /* Technically caused by flag bits => H_PARAMETER */ | ||||
|                     return H_PARAMETER; | ||||
|                 } else { /* Hash with SLB */ | ||||
|                     if (proc_tbl >> 38) { | ||||
|                         return H_P2; | ||||
|                     } else if (page_size & ~0x7) { | ||||
|                         return H_P3; | ||||
|                     } else if (table_size > 24) { | ||||
|                         return H_P4; | ||||
|                     } | ||||
|                 } | ||||
|                 cproc = (proc_tbl << 25) | page_size << 5 | table_size; | ||||
|             } | ||||
| 
 | ||||
|         } else { /* Deregister current process table */ | ||||
|             /* Set to benign value: (current GR) | 0. This allows
 | ||||
|              * deregistration in KVM to succeed even if the radix bit in flags | ||||
|              * doesn't match the radix bit in the old PATB. */ | ||||
|             cproc = spapr->patb_entry & PATBE1_GR; | ||||
|         } | ||||
|     } else { /* Maintain current registration */ | ||||
|         if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) { | ||||
|             /* Technically caused by flag bits => H_PARAMETER */ | ||||
|             return H_PARAMETER; /* Existing Process Table Mismatch */ | ||||
|         } | ||||
|         cproc = spapr->patb_entry; | ||||
|     } | ||||
| 
 | ||||
|     /* Check if we need to setup OR free the hpt */ | ||||
|     spapr_check_setup_free_hpt(spapr, spapr->patb_entry, cproc); | ||||
| 
 | ||||
|     spapr->patb_entry = cproc; /* Save new process table */ | ||||
|     if ((flags & FLAG_RADIX) || (flags & FLAG_HASH_PROC_TBL)) { | ||||
|         /* Use Process TBL */ | ||||
|         env->spr[SPR_LPCR] |= LPCR_UPRT; | ||||
|     } else { | ||||
|         env->spr[SPR_LPCR] &= ~LPCR_UPRT; | ||||
|     } | ||||
|     if (flags & FLAG_GTSE) { /* Partition Uses Guest Translation Shootdwn */ | ||||
|         env->spr[SPR_LPCR] |= LPCR_GTSE; | ||||
|     } else { | ||||
|         env->spr[SPR_LPCR] &= ~LPCR_GTSE; | ||||
|     } | ||||
| 
 | ||||
|     if (kvm_enabled()) { | ||||
|         return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, | ||||
|                                        flags & FLAG_GTSE, cproc); | ||||
|     } | ||||
|     return H_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| #define H_SIGNAL_SYS_RESET_ALL         -1 | ||||
| #define H_SIGNAL_SYS_RESET_ALLBUTSELF  -2 | ||||
| 
 | ||||
| @ -929,7 +1062,8 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, | ||||
|     uint32_t max_compat = cpu->max_compat; | ||||
|     uint32_t best_compat = 0; | ||||
|     int i; | ||||
|     sPAPROptionVector *ov5_guest, *ov5_cas_old, *ov5_updates; | ||||
|     sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; | ||||
|     bool guest_radix; | ||||
| 
 | ||||
|     /*
 | ||||
|      * We scan the supplied table of PVRs looking for two things | ||||
| @ -980,7 +1114,15 @@ 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; | ||||
| 
 | ||||
|     ov1_guest = spapr_ovec_parse_vector(ov_table, 1); | ||||
|     ov5_guest = spapr_ovec_parse_vector(ov_table, 5); | ||||
|     if (spapr_ovec_test(ov5_guest, OV5_MMU_BOTH)) { | ||||
|         error_report("guest requested hash and radix MMU, which is invalid."); | ||||
|         exit(EXIT_FAILURE); | ||||
|     } | ||||
|     /* The radix/hash bit in byte 24 requires special handling: */ | ||||
|     guest_radix = spapr_ovec_test(ov5_guest, OV5_MMU_RADIX_300); | ||||
|     spapr_ovec_clear(ov5_guest, OV5_MMU_RADIX_300); | ||||
| 
 | ||||
|     /* NOTE: there are actually a number of ov5 bits where input from the
 | ||||
|      * guest is always zero, and the platform/QEMU enables them independently | ||||
| @ -999,7 +1141,23 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, | ||||
|     ov5_updates = spapr_ovec_new(); | ||||
|     spapr->cas_reboot = spapr_ovec_diff(ov5_updates, | ||||
|                                         ov5_cas_old, spapr->ov5_cas); | ||||
| 
 | ||||
|     /* Now that processing is finished, set the radix/hash bit for the
 | ||||
|      * guest if it requested a valid mode; otherwise terminate the boot. */ | ||||
|     if (guest_radix) { | ||||
|         if (kvm_enabled() && !kvmppc_has_cap_mmu_radix()) { | ||||
|             error_report("Guest requested unavailable MMU mode (radix)."); | ||||
|             exit(EXIT_FAILURE); | ||||
|         } | ||||
|         spapr_ovec_set(spapr->ov5_cas, OV5_MMU_RADIX_300); | ||||
|     } else { | ||||
|         if (kvm_enabled() && kvmppc_has_cap_mmu_radix() | ||||
|             && !kvmppc_has_cap_mmu_hash_v3()) { | ||||
|             error_report("Guest requested unavailable MMU mode (hash)."); | ||||
|             exit(EXIT_FAILURE); | ||||
|         } | ||||
|     } | ||||
|     spapr->cas_legacy_guest_workaround = !spapr_ovec_test(ov1_guest, | ||||
|                                                           OV1_PPC_3_00); | ||||
|     if (!spapr->cas_reboot) { | ||||
|         spapr->cas_reboot = | ||||
|             (spapr_h_cas_compose_response(spapr, args[1], args[2], | ||||
| @ -1009,6 +1167,13 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, | ||||
| 
 | ||||
|     if (spapr->cas_reboot) { | ||||
|         qemu_system_reset_request(); | ||||
|     } else { | ||||
|         /* If ppc_spapr_reset() did not set up a HPT but one is necessary
 | ||||
|          * (because the guest isn't going to use radix) then set it up here. */ | ||||
|         if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) { | ||||
|             /* legacy hash or new hash: */ | ||||
|             spapr_setup_hpt_and_vrma(spapr); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return H_SUCCESS; | ||||
| @ -1084,6 +1249,11 @@ static void hypercall_register_types(void) | ||||
|     spapr_register_hypercall(H_PAGE_INIT, h_page_init); | ||||
|     spapr_register_hypercall(H_SET_MODE, h_set_mode); | ||||
| 
 | ||||
|     /* In Memory Table MMU h-calls */ | ||||
|     spapr_register_hypercall(H_CLEAN_SLB, h_clean_slb); | ||||
|     spapr_register_hypercall(H_INVALIDATE_PID, h_invalidate_pid); | ||||
|     spapr_register_hypercall(H_REGISTER_PROC_TBL, h_register_process_table); | ||||
| 
 | ||||
|     /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate
 | ||||
|      * here between the "CI" and the "CACHE" variants, they will use whatever | ||||
|      * mapping attributes qemu is using. When using KVM, the kernel will | ||||
|  | ||||
| @ -79,15 +79,16 @@ static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce) | ||||
| 
 | ||||
| static uint64_t *spapr_tce_alloc_table(uint32_t liobn, | ||||
|                                        uint32_t page_shift, | ||||
|                                        uint64_t bus_offset, | ||||
|                                        uint32_t nb_table, | ||||
|                                        int *fd, | ||||
|                                        bool need_vfio) | ||||
| { | ||||
|     uint64_t *table = NULL; | ||||
|     uint64_t window_size = (uint64_t)nb_table << page_shift; | ||||
| 
 | ||||
|     if (kvm_enabled() && !(window_size >> 32)) { | ||||
|         table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio); | ||||
|     if (kvm_enabled()) { | ||||
|         table = kvmppc_create_spapr_tce(liobn, page_shift, bus_offset, nb_table, | ||||
|                                         fd, need_vfio); | ||||
|     } | ||||
| 
 | ||||
|     if (!table) { | ||||
| @ -342,6 +343,7 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, | ||||
|     tcet->nb_table = nb_table; | ||||
|     tcet->table = spapr_tce_alloc_table(tcet->liobn, | ||||
|                                         tcet->page_shift, | ||||
|                                         tcet->bus_offset, | ||||
|                                         tcet->nb_table, | ||||
|                                         &tcet->fd, | ||||
|                                         tcet->need_vfio); | ||||
|  | ||||
| @ -50,8 +50,6 @@ | ||||
| #include "sysemu/hostmem.h" | ||||
| #include "sysemu/numa.h" | ||||
| 
 | ||||
| #include "hw/vfio/vfio.h" | ||||
| 
 | ||||
| /* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ | ||||
| #define RTAS_QUERY_FN           0 | ||||
| #define RTAS_CHANGE_FN          1 | ||||
| @ -1771,6 +1769,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) | ||||
|     } | ||||
| 
 | ||||
|     /* DMA setup */ | ||||
|     if ((sphb->page_size_mask & qemu_getrampagesize()) == 0) { | ||||
|         error_report("System page size 0x%lx is not enabled in page_size_mask " | ||||
|                      "(0x%"PRIx64"). Performance may be slow", | ||||
|                      qemu_getrampagesize(), sphb->page_size_mask); | ||||
|     } | ||||
| 
 | ||||
|     for (i = 0; i < windows_supported; ++i) { | ||||
|         tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); | ||||
|         if (!tcet) { | ||||
|  | ||||
| @ -33,19 +33,8 @@ | ||||
| #include "qapi-event.h" | ||||
| #include "qemu/cutils.h" | ||||
| 
 | ||||
| #define SPAPR_RTC(obj) \ | ||||
|     OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) | ||||
| 
 | ||||
| typedef struct sPAPRRTCState sPAPRRTCState; | ||||
| struct sPAPRRTCState { | ||||
|     /*< private >*/ | ||||
|     SysBusDevice parent_obj; | ||||
|     int64_t ns_offset; | ||||
| }; | ||||
| 
 | ||||
| void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) | ||||
| void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns) | ||||
| { | ||||
|     sPAPRRTCState *rtc = SPAPR_RTC(dev); | ||||
|     int64_t host_ns = qemu_clock_get_ns(rtc_clock); | ||||
|     int64_t guest_ns; | ||||
|     time_t guest_s; | ||||
| @ -63,16 +52,12 @@ void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset) | ||||
| int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset) | ||||
| { | ||||
|     sPAPRRTCState *rtc; | ||||
| 
 | ||||
|     if (!dev) { | ||||
|     if (!rtc) { | ||||
|         return -ENODEV; | ||||
|     } | ||||
| 
 | ||||
|     rtc = SPAPR_RTC(dev); | ||||
| 
 | ||||
|     rtc->ns_offset = legacy_offset * NANOSECONDS_PER_SECOND; | ||||
| 
 | ||||
|     return 0; | ||||
| @ -91,12 +76,7 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!spapr->rtc) { | ||||
|         rtas_st(rets, 0, RTAS_OUT_HW_ERROR); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     spapr_rtc_read(spapr->rtc, &tm, &ns); | ||||
|     spapr_rtc_read(&spapr->rtc, &tm, &ns); | ||||
| 
 | ||||
|     rtas_st(rets, 0, RTAS_OUT_SUCCESS); | ||||
|     rtas_st(rets, 1, tm.tm_year + 1900); | ||||
| @ -113,7 +93,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|                                  target_ulong args, | ||||
|                                  uint32_t nret, target_ulong rets) | ||||
| { | ||||
|     sPAPRRTCState *rtc; | ||||
|     sPAPRRTCState *rtc = &spapr->rtc; | ||||
|     struct tm tm; | ||||
|     time_t new_s; | ||||
|     int64_t host_ns; | ||||
| @ -123,11 +103,6 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (!spapr->rtc) { | ||||
|         rtas_st(rets, 0, RTAS_OUT_HW_ERROR); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     tm.tm_year = rtas_ld(args, 0) - 1900; | ||||
|     tm.tm_mon = rtas_ld(args, 1) - 1; | ||||
|     tm.tm_mday = rtas_ld(args, 2); | ||||
| @ -144,8 +119,6 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
|     /* Generate a monitor event for the change */ | ||||
|     qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); | ||||
| 
 | ||||
|     rtc = SPAPR_RTC(spapr->rtc); | ||||
| 
 | ||||
|     host_ns = qemu_clock_get_ns(rtc_clock); | ||||
| 
 | ||||
|     rtc->ns_offset = (new_s * NANOSECONDS_PER_SECOND) - host_ns; | ||||
| @ -155,7 +128,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, | ||||
| 
 | ||||
| static void spapr_rtc_qom_date(Object *obj, struct tm *current_tm, Error **errp) | ||||
| { | ||||
|     spapr_rtc_read(DEVICE(obj), current_tm, NULL); | ||||
|     spapr_rtc_read(SPAPR_RTC(obj), current_tm, NULL); | ||||
| } | ||||
| 
 | ||||
| static void spapr_rtc_realize(DeviceState *dev, Error **errp) | ||||
| @ -200,7 +173,7 @@ static void spapr_rtc_class_init(ObjectClass *oc, void *data) | ||||
| 
 | ||||
| static const TypeInfo spapr_rtc_info = { | ||||
|     .name          = TYPE_SPAPR_RTC, | ||||
|     .parent        = TYPE_SYS_BUS_DEVICE, | ||||
|     .parent        = TYPE_DEVICE, | ||||
|     .instance_size = sizeof(sPAPRRTCState), | ||||
|     .class_init    = spapr_rtc_class_init, | ||||
| }; | ||||
|  | ||||
| @ -259,4 +259,8 @@ struct ipmi_sdr_compact { | ||||
| 
 | ||||
| typedef uint8_t ipmi_sdr_compact_buffer[sizeof(struct ipmi_sdr_compact)]; | ||||
| 
 | ||||
| int ipmi_bmc_sdr_find(IPMIBmc *b, uint16_t recid, | ||||
|                       const struct ipmi_sdr_compact **sdr, uint16_t *nextrec); | ||||
| void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log); | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -22,6 +22,8 @@ | ||||
| #include "hw/boards.h" | ||||
| #include "hw/sysbus.h" | ||||
| #include "hw/ppc/pnv_lpc.h" | ||||
| #include "hw/ppc/pnv_psi.h" | ||||
| #include "hw/ppc/pnv_occ.h" | ||||
| 
 | ||||
| #define TYPE_PNV_CHIP "powernv-chip" | ||||
| #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) | ||||
| @ -54,8 +56,11 @@ typedef struct PnvChip { | ||||
|     MemoryRegion xscom_mmio; | ||||
|     MemoryRegion xscom; | ||||
|     AddressSpace xscom_as; | ||||
|     MemoryRegion icp_mmio; | ||||
| 
 | ||||
|     PnvLpcController lpc; | ||||
|     PnvPsi       psi; | ||||
|     PnvOCC       occ; | ||||
| } PnvChip; | ||||
| 
 | ||||
| typedef struct PnvChipClass { | ||||
| @ -91,18 +96,30 @@ typedef struct PnvChipClass { | ||||
|     OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) | ||||
| 
 | ||||
| /*
 | ||||
|  * This generates a HW chip id depending on an index: | ||||
|  * This generates a HW chip id depending on an index, as found on a | ||||
|  * two socket system with dual chip modules : | ||||
|  * | ||||
|  *    0x0, 0x1, 0x10, 0x11 | ||||
|  * | ||||
|  * 4 chips should be the maximum | ||||
|  * | ||||
|  * TODO: use a machine property to define the chip ids | ||||
|  */ | ||||
| #define PNV_CHIP_HWID(i) ((((i) & 0x3e) << 3) | ((i) & 0x1)) | ||||
| 
 | ||||
| /*
 | ||||
|  * Converts back a HW chip id to an index. This is useful to calculate | ||||
|  * the MMIO addresses of some controllers which depend on the chip id. | ||||
|  */ | ||||
| #define PNV_CHIP_INDEX(chip)                                    \ | ||||
|     (((chip)->chip_id >> 2) * 2 + ((chip)->chip_id & 0x3)) | ||||
| 
 | ||||
| #define TYPE_POWERNV_MACHINE       MACHINE_TYPE_NAME("powernv") | ||||
| #define POWERNV_MACHINE(obj) \ | ||||
|     OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) | ||||
| 
 | ||||
| typedef struct IPMIBmc IPMIBmc; | ||||
| 
 | ||||
| typedef struct PnvMachineState { | ||||
|     /*< private >*/ | ||||
|     MachineState parent_obj; | ||||
| @ -114,11 +131,21 @@ typedef struct PnvMachineState { | ||||
|     PnvChip      **chips; | ||||
| 
 | ||||
|     ISABus       *isa_bus; | ||||
|     uint32_t     cpld_irqstate; | ||||
| 
 | ||||
|     IPMIBmc      *bmc; | ||||
|     Notifier     powerdown_notifier; | ||||
| } PnvMachineState; | ||||
| 
 | ||||
| #define PNV_FDT_ADDR          0x01000000 | ||||
| #define PNV_TIMEBASE_FREQ     512000000ULL | ||||
| 
 | ||||
| /*
 | ||||
|  * BMC helpers | ||||
|  */ | ||||
| void pnv_bmc_populate_sensors(IPMIBmc *bmc, void *fdt); | ||||
| void pnv_bmc_powerdown(IPMIBmc *bmc); | ||||
| 
 | ||||
| /*
 | ||||
|  * POWER8 MMIO base addresses | ||||
|  */ | ||||
| @ -126,4 +153,32 @@ typedef struct PnvMachineState { | ||||
| #define PNV_XSCOM_BASE(chip)                                            \ | ||||
|     (chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) | ||||
| 
 | ||||
| /*
 | ||||
|  * XSCOM 0x20109CA defines the ICP BAR: | ||||
|  * | ||||
|  * 0:29   : bits 14 to 43 of address to define 1 MB region. | ||||
|  * 30     : 1 to enable ICP to receive loads/stores against its BAR region | ||||
|  * 31:63  : Constant 0 | ||||
|  * | ||||
|  * Usually defined as : | ||||
|  * | ||||
|  *      0xffffe00200000000 -> 0x0003ffff80000000 | ||||
|  *      0xffffe00600000000 -> 0x0003ffff80100000 | ||||
|  *      0xffffe02200000000 -> 0x0003ffff80800000 | ||||
|  *      0xffffe02600000000 -> 0x0003ffff80900000 | ||||
|  */ | ||||
| #define PNV_ICP_SIZE         0x0000000000100000ull | ||||
| #define PNV_ICP_BASE(chip)                                              \ | ||||
|     (0x0003ffff80000000ull + (uint64_t) PNV_CHIP_INDEX(chip) * PNV_ICP_SIZE) | ||||
| 
 | ||||
| 
 | ||||
| #define PNV_PSIHB_SIZE       0x0000000000100000ull | ||||
| #define PNV_PSIHB_BASE(chip) \ | ||||
|     (0x0003fffe80000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * PNV_PSIHB_SIZE) | ||||
| 
 | ||||
| #define PNV_PSIHB_FSP_SIZE   0x0000000100000000ull | ||||
| #define PNV_PSIHB_FSP_BASE(chip) \ | ||||
|     (0x0003ffe000000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * \ | ||||
|      PNV_PSIHB_FSP_SIZE) | ||||
| 
 | ||||
| #endif /* _PPC_PNV_H */ | ||||
|  | ||||
| @ -23,6 +23,8 @@ | ||||
| #define PNV_LPC(obj) \ | ||||
|      OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) | ||||
| 
 | ||||
| typedef struct PnvPsi PnvPsi; | ||||
| 
 | ||||
| typedef struct PnvLpcController { | ||||
|     DeviceState parent; | ||||
| 
 | ||||
| @ -62,6 +64,12 @@ typedef struct PnvLpcController { | ||||
| 
 | ||||
|     /* XSCOM registers */ | ||||
|     MemoryRegion xscom_regs; | ||||
| 
 | ||||
|     /* PSI to generate interrupts */ | ||||
|     PnvPsi *psi; | ||||
| } PnvLpcController; | ||||
| 
 | ||||
| qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type, | ||||
|                                  int nirqs); | ||||
| 
 | ||||
| #endif /* _PPC_PNV_LPC_H */ | ||||
|  | ||||
							
								
								
									
										38
									
								
								include/hw/ppc/pnv_occ.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								include/hw/ppc/pnv_occ.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| /*
 | ||||
|  * QEMU PowerPC PowerNV Emulation of a few OCC related registers | ||||
|  * | ||||
|  * Copyright (c) 2015-2017, 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_OCC_H | ||||
| #define _PPC_PNV_OCC_H | ||||
| 
 | ||||
| #define TYPE_PNV_OCC "pnv-occ" | ||||
| #define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC) | ||||
| 
 | ||||
| typedef struct PnvPsi PnvPsi; | ||||
| 
 | ||||
| typedef struct PnvOCC { | ||||
|     DeviceState xd; | ||||
| 
 | ||||
|     /* OCC Misc interrupt */ | ||||
|     uint64_t occmisc; | ||||
| 
 | ||||
|     PnvPsi *psi; | ||||
| 
 | ||||
|     MemoryRegion xscom_regs; | ||||
| } PnvOCC; | ||||
| 
 | ||||
| #endif /* _PPC_PNV_OCC_H */ | ||||
							
								
								
									
										67
									
								
								include/hw/ppc/pnv_psi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								include/hw/ppc/pnv_psi.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,67 @@ | ||||
| /*
 | ||||
|  * QEMU PowerPC PowerNV Processor Service Interface (PSI) model | ||||
|  * | ||||
|  * Copyright (c) 2015-2017, 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_PSI_H | ||||
| #define _PPC_PNV_PSI_H | ||||
| 
 | ||||
| #include "hw/sysbus.h" | ||||
| #include "hw/ppc/xics.h" | ||||
| 
 | ||||
| #define TYPE_PNV_PSI "pnv-psi" | ||||
| #define PNV_PSI(obj) \ | ||||
|      OBJECT_CHECK(PnvPsi, (obj), TYPE_PNV_PSI) | ||||
| 
 | ||||
| #define PSIHB_XSCOM_MAX         0x20 | ||||
| 
 | ||||
| typedef struct XICSState XICSState; | ||||
| 
 | ||||
| typedef struct PnvPsi { | ||||
|     SysBusDevice parent; | ||||
| 
 | ||||
|     MemoryRegion regs_mr; | ||||
|     uint64_t bar; | ||||
| 
 | ||||
|     /* FSP region not supported */ | ||||
|     /* MemoryRegion fsp_mr; */ | ||||
|     uint64_t fsp_bar; | ||||
| 
 | ||||
|     /* Interrupt generation */ | ||||
|     ICSState ics; | ||||
| 
 | ||||
|     /* Registers */ | ||||
|     uint64_t regs[PSIHB_XSCOM_MAX]; | ||||
| 
 | ||||
|     MemoryRegion xscom_regs; | ||||
| } PnvPsi; | ||||
| 
 | ||||
| /* The PSI and FSP interrupts are muxed on the same IRQ number */ | ||||
| typedef enum PnvPsiIrq { | ||||
|     PSIHB_IRQ_PSI, /* internal use only */ | ||||
|     PSIHB_IRQ_FSP, /* internal use only */ | ||||
|     PSIHB_IRQ_OCC, | ||||
|     PSIHB_IRQ_FSI, | ||||
|     PSIHB_IRQ_LPC_I2C, | ||||
|     PSIHB_IRQ_LOCAL_ERR, | ||||
|     PSIHB_IRQ_EXTERNAL, | ||||
| } PnvPsiIrq; | ||||
| 
 | ||||
| #define PSI_NUM_INTERRUPTS 6 | ||||
| 
 | ||||
| extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state); | ||||
| 
 | ||||
| #endif /* _PPC_PNV_PSI_H */ | ||||
| @ -60,6 +60,12 @@ typedef struct PnvXScomInterfaceClass { | ||||
| #define PNV_XSCOM_LPC_BASE        0xb0020 | ||||
| #define PNV_XSCOM_LPC_SIZE        0x4 | ||||
| 
 | ||||
| #define PNV_XSCOM_PSIHB_BASE      0x2010900 | ||||
| #define PNV_XSCOM_PSIHB_SIZE      0x20 | ||||
| 
 | ||||
| #define PNV_XSCOM_OCC_BASE        0x0066000 | ||||
| #define PNV_XSCOM_OCC_SIZE        0x6000 | ||||
| 
 | ||||
| extern void pnv_xscom_realize(PnvChip *chip, Error **errp); | ||||
| extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); | ||||
| 
 | ||||
|  | ||||
| @ -20,6 +20,18 @@ typedef struct sPAPREventSource sPAPREventSource; | ||||
| 
 | ||||
| #define SPAPR_TIMEBASE_FREQ     512000000ULL | ||||
| 
 | ||||
| #define TYPE_SPAPR_RTC "spapr-rtc" | ||||
| 
 | ||||
| #define SPAPR_RTC(obj)                                  \ | ||||
|     OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) | ||||
| 
 | ||||
| typedef struct sPAPRRTCState sPAPRRTCState; | ||||
| struct sPAPRRTCState { | ||||
|     /*< private >*/ | ||||
|     DeviceState parent_obj; | ||||
|     int64_t ns_offset; | ||||
| }; | ||||
| 
 | ||||
| typedef struct sPAPRMachineClass sPAPRMachineClass; | ||||
| 
 | ||||
| #define TYPE_SPAPR_MACHINE      "spapr-machine" | ||||
| @ -58,7 +70,7 @@ struct sPAPRMachineState { | ||||
|     QLIST_HEAD(, sPAPRPHBState) phbs; | ||||
|     struct sPAPRNVRAM *nvram; | ||||
|     ICSState *ics; | ||||
|     DeviceState *rtc; | ||||
|     sPAPRRTCState rtc; | ||||
| 
 | ||||
|     void *htab; | ||||
|     uint32_t htab_shift; | ||||
| @ -77,6 +89,7 @@ struct sPAPRMachineState { | ||||
|     sPAPROptionVector *ov5;         /* QEMU-supported option vectors */ | ||||
|     sPAPROptionVector *ov5_cas;     /* negotiated (via CAS) option vectors */ | ||||
|     bool cas_reboot; | ||||
|     bool cas_legacy_guest_workaround; | ||||
| 
 | ||||
|     Notifier epow_notifier; | ||||
|     QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; | ||||
| @ -95,8 +108,7 @@ struct sPAPRMachineState { | ||||
|     char *kvm_type; | ||||
|     MemoryHotplugState hotplug_memory; | ||||
| 
 | ||||
|     uint32_t nr_servers; | ||||
|     ICPState *icps; | ||||
|     const char *icp_type; | ||||
| }; | ||||
| 
 | ||||
| #define H_SUCCESS         0 | ||||
| @ -349,6 +361,9 @@ struct sPAPRMachineState { | ||||
| #define H_XIRR_X                0x2FC | ||||
| #define H_RANDOM                0x300 | ||||
| #define H_SET_MODE              0x31C | ||||
| #define H_CLEAN_SLB             0x374 | ||||
| #define H_INVALIDATE_PID        0x378 | ||||
| #define H_REGISTER_PROC_TBL     0x37C | ||||
| #define H_SIGNAL_SYS_RESET      0x380 | ||||
| #define MAX_HCALL_OPCODE        H_SIGNAL_SYS_RESET | ||||
| 
 | ||||
| @ -593,6 +608,8 @@ void spapr_dt_events(sPAPRMachineState *sm, void *fdt); | ||||
| int spapr_h_cas_compose_response(sPAPRMachineState *sm, | ||||
|                                  target_ulong addr, target_ulong size, | ||||
|                                  sPAPROptionVector *ov5_updates); | ||||
| void close_htab_fd(sPAPRMachineState *spapr); | ||||
| void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr); | ||||
| 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, | ||||
| @ -629,11 +646,10 @@ struct sPAPRConfigureConnectorState { | ||||
| 
 | ||||
| void spapr_ccs_reset_hook(void *opaque); | ||||
| 
 | ||||
| #define TYPE_SPAPR_RTC "spapr-rtc" | ||||
| #define TYPE_SPAPR_RNG "spapr-rng" | ||||
| void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns); | ||||
| int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset); | ||||
| 
 | ||||
| void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); | ||||
| int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset); | ||||
| #define TYPE_SPAPR_RNG "spapr-rng" | ||||
| 
 | ||||
| int spapr_rng_populate_dt(void *fdt); | ||||
| 
 | ||||
|  | ||||
| @ -43,11 +43,19 @@ typedef struct sPAPROptionVector sPAPROptionVector; | ||||
| 
 | ||||
| #define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) | ||||
| 
 | ||||
| /* option vector 1 */ | ||||
| #define OV1_PPC_3_00            OV_BIT(3, 0) /* guest supports PowerPC 3.00? */ | ||||
| 
 | ||||
| /* 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) | ||||
| 
 | ||||
| /* ISA 3.00 MMU features: */ | ||||
| #define OV5_MMU_BOTH            OV_BIT(24, 0) /* Radix and hash */ | ||||
| #define OV5_MMU_RADIX_300       OV_BIT(24, 1) /* 1=Radix only, 0=Hash only */ | ||||
| #define OV5_MMU_RADIX_GTSE      OV_BIT(26, 1) /* Radix GTSE */ | ||||
| 
 | ||||
| /* interfaces */ | ||||
| sPAPROptionVector *spapr_ovec_new(void); | ||||
| sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); | ||||
|  | ||||
| @ -28,7 +28,7 @@ | ||||
| #ifndef XICS_H | ||||
| #define XICS_H | ||||
| 
 | ||||
| #include "hw/sysbus.h" | ||||
| #include "hw/qdev.h" | ||||
| 
 | ||||
| #define XICS_IPI        0x2 | ||||
| #define XICS_BUID       0x1 | ||||
| @ -41,10 +41,12 @@ | ||||
|  */ | ||||
| typedef struct ICPStateClass ICPStateClass; | ||||
| typedef struct ICPState ICPState; | ||||
| typedef struct PnvICPState PnvICPState; | ||||
| typedef struct ICSStateClass ICSStateClass; | ||||
| typedef struct ICSState ICSState; | ||||
| typedef struct ICSIRQState ICSIRQState; | ||||
| typedef struct XICSFabric XICSFabric; | ||||
| typedef struct PowerPCCPU PowerPCCPU; | ||||
| 
 | ||||
| #define TYPE_ICP "icp" | ||||
| #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) | ||||
| @ -52,6 +54,9 @@ typedef struct XICSFabric XICSFabric; | ||||
| #define TYPE_KVM_ICP "icp-kvm" | ||||
| #define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) | ||||
| 
 | ||||
| #define TYPE_PNV_ICP "pnv-icp" | ||||
| #define PNV_ICP(obj) OBJECT_CHECK(PnvICPState, (obj), TYPE_PNV_ICP) | ||||
| 
 | ||||
| #define ICP_CLASS(klass) \ | ||||
|      OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) | ||||
| #define ICP_GET_CLASS(obj) \ | ||||
| @ -60,6 +65,7 @@ typedef struct XICSFabric XICSFabric; | ||||
| struct ICPStateClass { | ||||
|     DeviceClass parent_class; | ||||
| 
 | ||||
|     void (*realize)(DeviceState *dev, Error **errp); | ||||
|     void (*pre_save)(ICPState *s); | ||||
|     int (*post_load)(ICPState *s, int version_id); | ||||
|     void (*cpu_setup)(ICPState *icp, PowerPCCPU *cpu); | ||||
| @ -80,6 +86,13 @@ struct ICPState { | ||||
|     XICSFabric *xics; | ||||
| }; | ||||
| 
 | ||||
| struct PnvICPState { | ||||
|     ICPState parent_obj; | ||||
| 
 | ||||
|     MemoryRegion mmio; | ||||
|     uint32_t links[3]; | ||||
| }; | ||||
| 
 | ||||
| #define TYPE_ICS_BASE "ics-base" | ||||
| #define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_BASE) | ||||
| 
 | ||||
| @ -168,12 +181,10 @@ void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle); | ||||
| 
 | ||||
| qemu_irq xics_get_qirq(XICSFabric *xi, int irq); | ||||
| ICPState *xics_icp_get(XICSFabric *xi, int server); | ||||
| void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu); | ||||
| void xics_cpu_setup(XICSFabric *xi, PowerPCCPU *cpu, ICPState *icp); | ||||
| void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu); | ||||
| 
 | ||||
| /* Internal XICS interfaces */ | ||||
| int xics_get_cpu_index_by_dt_id(int cpu_dt_id); | ||||
| 
 | ||||
| void icp_set_cppr(ICPState *icp, uint8_t cppr); | ||||
| void icp_set_mfrr(ICPState *icp, uint8_t mfrr); | ||||
| uint32_t icp_accept(ICPState *ss); | ||||
|  | ||||
| @ -527,5 +527,6 @@ int kvm_set_one_reg(CPUState *cs, uint64_t id, void *source); | ||||
|  * Returns: 0 on success, or a negative errno on failure. | ||||
|  */ | ||||
| int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); | ||||
| struct ppc_radix_page_info *kvm_get_radix_page_info(void); | ||||
| int kvm_get_max_memslots(void); | ||||
| #endif | ||||
|  | ||||
| @ -425,7 +425,7 @@ possible drivers and properties, use @code{-device help} and | ||||
| @code{-device @var{driver},help}. | ||||
| 
 | ||||
| Some drivers are: | ||||
| @item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}] | ||||
| @item -device ipmi-bmc-sim,id=@var{id}[,slave_addr=@var{val}][,sdrfile=@var{file}][,furareasize=@var{val}][,furdatafile=@var{file}] | ||||
| 
 | ||||
| Add an IPMI BMC.  This is a simulation of a hardware management | ||||
| interface processor that normally sits on a system.  It provides | ||||
| @ -437,6 +437,19 @@ This address is the BMC's address on the I2C network of management | ||||
| controllers.  If you don't know what this means, it is safe to ignore | ||||
| it. | ||||
| 
 | ||||
| @table @option | ||||
| @item bmc=@var{id} | ||||
| The BMC to connect to, one of ipmi-bmc-sim or ipmi-bmc-extern above. | ||||
| @item slave_addr=@var{val} | ||||
| Define slave address to use for the BMC.  The default is 0x20. | ||||
| @item sdrfile=@var{file} | ||||
| file containing raw Sensor Data Records (SDR) data. The default is none. | ||||
| @item fruareasize=@var{val} | ||||
| size of a Field Replaceable Unit (FRU) area.  The default is 1024. | ||||
| @item frudatafile=@var{file} | ||||
| file containing raw Field Replaceable Unit (FRU) inventory data. The default is none. | ||||
| @end table | ||||
| 
 | ||||
| @item -device ipmi-bmc-extern,id=@var{id},chardev=@var{id}[,slave_addr=@var{val}] | ||||
| 
 | ||||
| Add a connection to an external IPMI BMC simulator.  Instead of | ||||
|  | ||||
| @ -50,7 +50,7 @@ struct PPCUserRegStruct { | ||||
| struct PPCElfPrstatus { | ||||
|     char pad1[112]; | ||||
|     struct PPCUserRegStruct pr_reg; | ||||
|     reg_t pad2[4]; | ||||
|     char pad2[40]; | ||||
| } QEMU_PACKED; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -197,6 +197,7 @@ typedef struct PowerPCCPUClass { | ||||
|     int bfd_mach; | ||||
|     uint32_t l1_dcache_size, l1_icache_size; | ||||
|     const struct ppc_segment_page_sizes *sps; | ||||
|     struct ppc_radix_page_info *radix_page_info; | ||||
|     void (*init_proc)(CPUPPCState *env); | ||||
|     int  (*check_pow)(CPUPPCState *env); | ||||
|     int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); | ||||
|  | ||||
| @ -943,6 +943,10 @@ struct ppc_segment_page_sizes { | ||||
|     struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; | ||||
| }; | ||||
| 
 | ||||
| struct ppc_radix_page_info { | ||||
|     uint32_t count; | ||||
|     uint32_t entries[PPC_PAGE_SIZES_MAX_SZ]; | ||||
| }; | ||||
| 
 | ||||
| /*****************************************************************************/ | ||||
| /* The whole PowerPC CPU context */ | ||||
| @ -1196,6 +1200,7 @@ struct PowerPCCPU { | ||||
|     uint32_t max_compat; | ||||
|     uint32_t compat_pvr; | ||||
|     PPCVirtualHypervisor *vhyp; | ||||
|     Object *intc; | ||||
| 
 | ||||
|     /* Fields related to migration compatibility hacks */ | ||||
|     bool pre_2_8_migration; | ||||
|  | ||||
| @ -709,6 +709,7 @@ DEF_HELPER_FLAGS_1(load_601_rtcu, TCG_CALL_NO_RWG, tl, env) | ||||
| DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env) | ||||
| #endif | ||||
| DEF_HELPER_2(store_sdr1, void, env, tl) | ||||
| DEF_HELPER_2(store_pidr, void, env, tl) | ||||
| DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl) | ||||
| DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl) | ||||
| DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl) | ||||
|  | ||||
							
								
								
									
										144
									
								
								target/ppc/kvm.c
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								target/ppc/kvm.c
									
									
									
									
									
								
							| @ -49,6 +49,8 @@ | ||||
| #if defined(TARGET_PPC64) | ||||
| #include "hw/ppc/spapr_cpu_core.h" | ||||
| #endif | ||||
| #include "elf.h" | ||||
| #include "sysemu/kvm_int.h" | ||||
| 
 | ||||
| //#define DEBUG_KVM
 | ||||
| 
 | ||||
| @ -73,6 +75,7 @@ static int cap_booke_sregs; | ||||
| static int cap_ppc_smt; | ||||
| static int cap_ppc_rma; | ||||
| static int cap_spapr_tce; | ||||
| static int cap_spapr_tce_64; | ||||
| static int cap_spapr_multitce; | ||||
| static int cap_spapr_vfio; | ||||
| static int cap_hior; | ||||
| @ -83,6 +86,8 @@ static int cap_papr; | ||||
| static int cap_htab_fd; | ||||
| static int cap_fixup_hcalls; | ||||
| static int cap_htm;             /* Hardware transactional memory support */ | ||||
| static int cap_mmu_radix; | ||||
| static int cap_mmu_hash_v3; | ||||
| 
 | ||||
| static uint32_t debug_inst_opcode; | ||||
| 
 | ||||
| @ -125,6 +130,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) | ||||
|     cap_ppc_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT); | ||||
|     cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); | ||||
|     cap_spapr_tce = kvm_check_extension(s, KVM_CAP_SPAPR_TCE); | ||||
|     cap_spapr_tce_64 = kvm_check_extension(s, KVM_CAP_SPAPR_TCE_64); | ||||
|     cap_spapr_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE); | ||||
|     cap_spapr_vfio = false; | ||||
|     cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG); | ||||
| @ -136,6 +142,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) | ||||
|     cap_htab_fd = kvm_check_extension(s, KVM_CAP_PPC_HTAB_FD); | ||||
|     cap_fixup_hcalls = kvm_check_extension(s, KVM_CAP_PPC_FIXUP_HCALL); | ||||
|     cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM); | ||||
|     cap_mmu_radix = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_RADIX); | ||||
|     cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3); | ||||
| 
 | ||||
|     if (!cap_interrupt_level) { | ||||
|         fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " | ||||
| @ -330,6 +338,61 @@ static void kvm_get_smmu_info(PowerPCCPU *cpu, struct kvm_ppc_smmu_info *info) | ||||
|     kvm_get_fallback_smmu_info(cpu, info); | ||||
| } | ||||
| 
 | ||||
| struct ppc_radix_page_info *kvm_get_radix_page_info(void) | ||||
| { | ||||
|     KVMState *s = KVM_STATE(current_machine->accelerator); | ||||
|     struct ppc_radix_page_info *radix_page_info; | ||||
|     struct kvm_ppc_rmmu_info rmmu_info; | ||||
|     int i; | ||||
| 
 | ||||
|     if (!kvm_check_extension(s, KVM_CAP_PPC_MMU_RADIX)) { | ||||
|         return NULL; | ||||
|     } | ||||
|     if (kvm_vm_ioctl(s, KVM_PPC_GET_RMMU_INFO, &rmmu_info)) { | ||||
|         return NULL; | ||||
|     } | ||||
|     radix_page_info = g_malloc0(sizeof(*radix_page_info)); | ||||
|     radix_page_info->count = 0; | ||||
|     for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) { | ||||
|         if (rmmu_info.ap_encodings[i]) { | ||||
|             radix_page_info->entries[i] = rmmu_info.ap_encodings[i]; | ||||
|             radix_page_info->count++; | ||||
|         } | ||||
|     } | ||||
|     return radix_page_info; | ||||
| } | ||||
| 
 | ||||
| target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, | ||||
|                                      bool radix, bool gtse, | ||||
|                                      uint64_t proc_tbl) | ||||
| { | ||||
|     CPUState *cs = CPU(cpu); | ||||
|     int ret; | ||||
|     uint64_t flags = 0; | ||||
|     struct kvm_ppc_mmuv3_cfg cfg = { | ||||
|         .process_table = proc_tbl, | ||||
|     }; | ||||
| 
 | ||||
|     if (radix) { | ||||
|         flags |= KVM_PPC_MMUV3_RADIX; | ||||
|     } | ||||
|     if (gtse) { | ||||
|         flags |= KVM_PPC_MMUV3_GTSE; | ||||
|     } | ||||
|     cfg.flags = flags; | ||||
|     ret = kvm_vm_ioctl(cs->kvm_state, KVM_PPC_CONFIGURE_V3_MMU, &cfg); | ||||
|     switch (ret) { | ||||
|     case 0: | ||||
|         return H_SUCCESS; | ||||
|     case -EINVAL: | ||||
|         return H_PARAMETER; | ||||
|     case -ENODEV: | ||||
|         return H_NOT_AVAILABLE; | ||||
|     default: | ||||
|         return H_HARDWARE; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift) | ||||
| { | ||||
|     if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { | ||||
| @ -509,8 +572,11 @@ int kvm_arch_init_vcpu(CPUState *cs) | ||||
|     case POWERPC_MMU_2_07: | ||||
|         if (!cap_htm && !kvmppc_is_pr(cs->kvm_state)) { | ||||
|             /* KVM-HV has transactional memory on POWER8 also without the
 | ||||
|              * KVM_CAP_PPC_HTM extension, so enable it here instead. */ | ||||
|             cap_htm = true; | ||||
|              * KVM_CAP_PPC_HTM extension, so enable it here instead as | ||||
|              * long as it's availble to userspace on the host. */ | ||||
|             if (qemu_getauxval(AT_HWCAP2) & PPC_FEATURE2_HAS_HTM) { | ||||
|                 cap_htm = true; | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
| @ -2132,13 +2198,24 @@ bool kvmppc_spapr_use_multitce(void) | ||||
|     return cap_spapr_multitce; | ||||
| } | ||||
| 
 | ||||
| void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, | ||||
|                               bool need_vfio) | ||||
| int kvmppc_spapr_enable_inkernel_multitce(void) | ||||
| { | ||||
|     int ret; | ||||
| 
 | ||||
|     ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_ENABLE_HCALL, 0, | ||||
|                             H_PUT_TCE_INDIRECT, 1); | ||||
|     if (!ret) { | ||||
|         ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_ENABLE_HCALL, 0, | ||||
|                                 H_STUFF_TCE, 1); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, | ||||
|                               uint64_t bus_offset, uint32_t nb_table, | ||||
|                               int *pfd, bool need_vfio) | ||||
| { | ||||
|     struct kvm_create_spapr_tce args = { | ||||
|         .liobn = liobn, | ||||
|         .window_size = window_size, | ||||
|     }; | ||||
|     long len; | ||||
|     int fd; | ||||
|     void *table; | ||||
| @ -2151,14 +2228,41 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args); | ||||
|     if (fd < 0) { | ||||
|         fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n", | ||||
|                 liobn); | ||||
|     if (cap_spapr_tce_64) { | ||||
|         struct kvm_create_spapr_tce_64 args = { | ||||
|             .liobn = liobn, | ||||
|             .page_shift = page_shift, | ||||
|             .offset = bus_offset >> page_shift, | ||||
|             .size = nb_table, | ||||
|             .flags = 0 | ||||
|         }; | ||||
|         fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE_64, &args); | ||||
|         if (fd < 0) { | ||||
|             fprintf(stderr, | ||||
|                     "KVM: Failed to create TCE64 table for liobn 0x%x\n", | ||||
|                     liobn); | ||||
|             return NULL; | ||||
|         } | ||||
|     } else if (cap_spapr_tce) { | ||||
|         uint64_t window_size = (uint64_t) nb_table << page_shift; | ||||
|         struct kvm_create_spapr_tce args = { | ||||
|             .liobn = liobn, | ||||
|             .window_size = window_size, | ||||
|         }; | ||||
|         if ((window_size != args.window_size) || bus_offset) { | ||||
|             return NULL; | ||||
|         } | ||||
|         fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args); | ||||
|         if (fd < 0) { | ||||
|             fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n", | ||||
|                     liobn); | ||||
|             return NULL; | ||||
|         } | ||||
|     } else { | ||||
|         return NULL; | ||||
|     } | ||||
| 
 | ||||
|     len = (window_size / SPAPR_TCE_PAGE_SIZE) * sizeof(uint64_t); | ||||
|     len = nb_table * sizeof(uint64_t); | ||||
|     /* FIXME: round this up to page size */ | ||||
| 
 | ||||
|     table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); | ||||
| @ -2273,6 +2377,10 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) | ||||
|     if (icache_size != -1) { | ||||
|         pcc->l1_icache_size = icache_size; | ||||
|     } | ||||
| 
 | ||||
| #if defined(TARGET_PPC64) | ||||
|     pcc->radix_page_info = kvm_get_radix_page_info(); | ||||
| #endif /* defined(TARGET_PPC64) */ | ||||
| } | ||||
| 
 | ||||
| bool kvmppc_has_cap_epr(void) | ||||
| @ -2295,6 +2403,16 @@ bool kvmppc_has_cap_htm(void) | ||||
|     return cap_htm; | ||||
| } | ||||
| 
 | ||||
| bool kvmppc_has_cap_mmu_radix(void) | ||||
| { | ||||
|     return cap_mmu_radix; | ||||
| } | ||||
| 
 | ||||
| bool kvmppc_has_cap_mmu_hash_v3(void) | ||||
| { | ||||
|     return cap_mmu_hash_v3; | ||||
| } | ||||
| 
 | ||||
| static PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc) | ||||
| { | ||||
|     ObjectClass *oc = OBJECT_CLASS(pcc); | ||||
|  | ||||
| @ -33,11 +33,16 @@ int kvmppc_clear_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); | ||||
| int kvmppc_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits); | ||||
| int kvmppc_set_tcr(PowerPCCPU *cpu); | ||||
| int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu); | ||||
| target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, | ||||
|                                      bool radix, bool gtse, | ||||
|                                      uint64_t proc_tbl); | ||||
| #ifndef CONFIG_USER_ONLY | ||||
| off_t kvmppc_alloc_rma(void **rma); | ||||
| bool kvmppc_spapr_use_multitce(void); | ||||
| void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, | ||||
|                               bool need_vfio); | ||||
| int kvmppc_spapr_enable_inkernel_multitce(void); | ||||
| void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, | ||||
|                               uint64_t bus_offset, uint32_t nb_table, | ||||
|                               int *pfd, bool need_vfio); | ||||
| int kvmppc_remove_spapr_tce(void *table, int pfd, uint32_t window_size); | ||||
| int kvmppc_reset_htab(int shift_hint); | ||||
| uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); | ||||
| @ -53,6 +58,8 @@ void kvmppc_read_hptes(ppc_hash_pte64_t *hptes, hwaddr ptex, int n); | ||||
| void kvmppc_write_hpte(hwaddr ptex, uint64_t pte0, uint64_t pte1); | ||||
| bool kvmppc_has_cap_fixup_hcalls(void); | ||||
| bool kvmppc_has_cap_htm(void); | ||||
| bool kvmppc_has_cap_mmu_radix(void); | ||||
| bool kvmppc_has_cap_mmu_hash_v3(void); | ||||
| int kvmppc_enable_hwrng(void); | ||||
| int kvmppc_put_books_sregs(PowerPCCPU *cpu); | ||||
| PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); | ||||
| @ -156,6 +163,13 @@ static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu) | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, | ||||
|                                      bool radix, bool gtse, | ||||
|                                      uint64_t proc_tbl) | ||||
| { | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| #ifndef CONFIG_USER_ONLY | ||||
| static inline off_t kvmppc_alloc_rma(void **rma) | ||||
| { | ||||
| @ -167,9 +181,15 @@ static inline bool kvmppc_spapr_use_multitce(void) | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static inline void *kvmppc_create_spapr_tce(uint32_t liobn, | ||||
|                                             uint32_t window_size, int *fd, | ||||
|                                             bool need_vfio) | ||||
| static inline int kvmppc_spapr_enable_inkernel_multitce(void) | ||||
| { | ||||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| static inline void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t page_shift, | ||||
|                                             uint64_t bus_offset, | ||||
|                                             uint32_t nb_table, | ||||
|                                             int *pfd, bool need_vfio) | ||||
| { | ||||
|     return NULL; | ||||
| } | ||||
| @ -252,6 +272,16 @@ static inline bool kvmppc_has_cap_htm(void) | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static inline bool kvmppc_has_cap_mmu_radix(void) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static inline bool kvmppc_has_cap_mmu_hash_v3(void) | ||||
| { | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static inline int kvmppc_enable_hwrng(void) | ||||
| { | ||||
|     return -1; | ||||
|  | ||||
| @ -88,6 +88,14 @@ void helper_store_sdr1(CPUPPCState *env, target_ulong val) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void helper_store_pidr(CPUPPCState *env, target_ulong val) | ||||
| { | ||||
|     PowerPCCPU *cpu = ppc_env_get_cpu(env); | ||||
| 
 | ||||
|     env->spr[SPR_BOOKS_PID] = val; | ||||
|     tlb_flush(CPU(cpu)); | ||||
| } | ||||
| 
 | ||||
| void helper_store_hid0_601(CPUPPCState *env, target_ulong val) | ||||
| { | ||||
|     target_ulong hid0; | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Peter Maydell
						Peter Maydell