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:
Peter Maydell 2017-04-26 13:17:11 +01:00
commit dcaed66cbe
40 changed files with 2810 additions and 497 deletions

View File

@ -646,7 +646,6 @@ F: hw/ppc/ppc440_bamboo.c
e500 e500
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
M: Scott Wood <scottwood@freescale.com>
L: qemu-ppc@nongnu.org L: qemu-ppc@nongnu.org
S: Supported S: Supported
F: hw/ppc/e500.[hc] F: hw/ppc/e500.[hc]
@ -657,7 +656,6 @@ F: pc-bios/u-boot.e500
mpc8544ds mpc8544ds
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
M: Scott Wood <scottwood@freescale.com>
L: qemu-ppc@nongnu.org L: qemu-ppc@nongnu.org
S: Supported S: Supported
F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544ds.c
@ -934,7 +932,6 @@ F: include/hw/ppc/ppc4xx.h
ppce500 ppce500
M: Alexander Graf <agraf@suse.de> M: Alexander Graf <agraf@suse.de>
M: Scott Wood <scottwood@freescale.com>
L: qemu-ppc@nongnu.org L: qemu-ppc@nongnu.org
S: Supported S: Supported
F: hw/ppc/e500* F: hw/ppc/e500*

View File

@ -6,6 +6,10 @@ include usb.mak
CONFIG_VIRTIO_VGA=y CONFIG_VIRTIO_VGA=y
CONFIG_ESCC=y CONFIG_ESCC=y
CONFIG_M48T59=y CONFIG_M48T59=y
CONFIG_IPMI=y
CONFIG_IPMI_LOCAL=y
CONFIG_IPMI_EXTERN=y
CONFIG_ISA_IPMI_BT=y
CONFIG_SERIAL=y CONFIG_SERIAL=y
CONFIG_PARALLEL=y CONFIG_PARALLEL=y
CONFIG_I8254=y CONFIG_I8254=y

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.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_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
obj-$(CONFIG_S390_FLIC) += s390_flic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o

View File

@ -38,21 +38,10 @@
#include "monitor/monitor.h" #include "monitor/monitor.h"
#include "hw/intc/intc.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) void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
{ {
CPUState *cs = CPU(cpu); CPUState *cs = CPU(cpu);
ICPState *icp = xics_icp_get(xi, cs->cpu_index); ICPState *icp = ICP(cpu->intc);
assert(icp); assert(icp);
assert(cs == icp->cs); assert(cs == icp->cs);
@ -61,15 +50,15 @@ void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu)
icp->cs = NULL; 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); CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env; CPUPPCState *env = &cpu->env;
ICPState *icp = xics_icp_get(xi, cs->cpu_index);
ICPStateClass *icpc; ICPStateClass *icpc;
assert(icp); assert(icp);
cpu->intc = OBJECT(icp);
icp->cs = cs; icp->cs = cs;
icpc = ICP_GET_CLASS(icp); icpc = ICP_GET_CLASS(icp);
@ -348,6 +337,7 @@ static void icp_reset(void *dev)
static void icp_realize(DeviceState *dev, Error **errp) static void icp_realize(DeviceState *dev, Error **errp)
{ {
ICPState *icp = ICP(dev); ICPState *icp = ICP(dev);
ICPStateClass *icpc = ICP_GET_CLASS(dev);
Object *obj; Object *obj;
Error *err = NULL; Error *err = NULL;
@ -360,6 +350,10 @@ static void icp_realize(DeviceState *dev, Error **errp)
icp->xics = XICS_FABRIC(obj); icp->xics = XICS_FABRIC(obj);
if (icpc->realize) {
icpc->realize(dev, errp);
}
qemu_register_reset(icp_reset, dev); qemu_register_reset(icp_reset, dev);
} }

192
hw/intc/xics_pnv.c Normal file
View 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)

View File

@ -43,20 +43,17 @@
static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args) 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]; target_ulong cppr = args[0];
icp_set_cppr(icp, cppr); icp_set_cppr(ICP(cpu->intc), cppr);
return H_SUCCESS; return H_SUCCESS;
} }
static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args) target_ulong opcode, target_ulong *args)
{ {
target_ulong server = xics_get_cpu_index_by_dt_id(args[0]);
target_ulong mfrr = args[1]; 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) { if (!icp) {
return H_PARAMETER; 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, static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args) target_ulong opcode, target_ulong *args)
{ {
CPUState *cs = CPU(cpu); uint32_t xirr = icp_accept(ICP(cpu->intc));
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
uint32_t xirr = icp_accept(icp);
args[0] = xirr; args[0] = xirr;
return H_SUCCESS; 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, static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args) target_ulong opcode, target_ulong *args)
{ {
CPUState *cs = CPU(cpu); uint32_t xirr = icp_accept(ICP(cpu->intc));
ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), cs->cpu_index);
uint32_t xirr = icp_accept(icp);
args[0] = xirr; args[0] = xirr;
args[1] = cpu_get_host_ticks(); 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, static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args) 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]; target_ulong xirr = args[0];
icp_eoi(icp, xirr); icp_eoi(ICP(cpu->intc), xirr);
return H_SUCCESS; return H_SUCCESS;
} }
static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr,
target_ulong opcode, target_ulong *args) 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 mfrr;
uint32_t xirr = icp_ipoll(icp, &mfrr); uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr);
args[0] = xirr; args[0] = xirr;
args[1] = mfrr; args[1] = mfrr;
@ -132,7 +121,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr,
} }
nr = rtas_ld(args, 0); 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); priority = rtas_ld(args, 2);
if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server) if (!ics_valid_irq(ics, nr) || !xics_icp_get(XICS_FABRIC(spapr), server)

View File

@ -27,6 +27,7 @@
#include "qemu/timer.h" #include "qemu/timer.h"
#include "hw/ipmi/ipmi.h" #include "hw/ipmi/ipmi.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "hw/loader.h"
#define IPMI_NETFN_CHASSIS 0x00 #define IPMI_NETFN_CHASSIS 0x00
@ -79,6 +80,9 @@
#define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A #define IPMI_CMD_ENTER_SDR_REP_UPD_MODE 0x2A
#define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B #define IPMI_CMD_EXIT_SDR_REP_UPD_MODE 0x2B
#define IPMI_CMD_RUN_INIT_AGENT 0x2C #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_INFO 0x40
#define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41 #define IPMI_CMD_GET_SEL_ALLOC_INFO 0x41
#define IPMI_CMD_RESERVE_SEL 0x42 #define IPMI_CMD_RESERVE_SEL 0x42
@ -121,6 +125,13 @@ typedef struct IPMISdr {
uint8_t overflow; uint8_t overflow;
} IPMISdr; } IPMISdr;
typedef struct IPMIFru {
char *filename;
unsigned int nentries;
uint16_t areasize;
uint8_t *data;
} IPMIFru;
typedef struct IPMISensor { typedef struct IPMISensor {
uint8_t status; uint8_t status;
uint8_t reading; uint8_t reading;
@ -212,7 +223,9 @@ struct IPMIBmcSim {
IPMISel sel; IPMISel sel;
IPMISdr sdr; IPMISdr sdr;
IPMIFru fru;
IPMISensor sensors[MAX_SENSORS]; IPMISensor sensors[MAX_SENSORS];
char *sdr_filename;
/* Odd netfns are for responses, so we only need the even ones. */ /* Odd netfns are for responses, so we only need the even ones. */
const IPMINetfn *netfns[MAX_NETFNS / 2]; const IPMINetfn *netfns[MAX_NETFNS / 2];
@ -403,6 +416,22 @@ static int sdr_find_entry(IPMISdr *sdr, uint16_t recid,
return 1; 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) static void sel_inc_reservation(IPMISel *sel)
{ {
sel->reservation++; sel->reservation++;
@ -444,6 +473,30 @@ static int attn_irq_enabled(IPMIBmcSim *ibs)
IPMI_BMC_MSG_FLAG_EVT_BUF_FULL_SET(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, static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert,
uint8_t evd1, uint8_t evd2, uint8_t evd3) 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); 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, static void reserve_sel(IPMIBmcSim *ibs,
uint8_t *cmd, unsigned int cmd_len, uint8_t *cmd, unsigned int cmd_len,
RspBuffer *rsp) RspBuffer *rsp)
@ -1651,6 +1789,9 @@ static const IPMINetfn app_netfn = {
}; };
static const IPMICmdHandler storage_cmds[] = { 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_GET_SDR_REP_INFO] = { get_sdr_rep_info },
[IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep }, [IPMI_CMD_RESERVE_SDR_REP] = { reserve_sdr_rep },
[IPMI_CMD_GET_SDR] = { get_sdr, 8 }, [IPMI_CMD_GET_SDR] = { get_sdr, 8 },
@ -1696,22 +1837,33 @@ static void ipmi_sdr_init(IPMIBmcSim *ibs)
sdrs_size = sizeof(init_sdrs); sdrs_size = sizeof(init_sdrs);
sdrs = 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) { for (i = 0; i < sdrs_size; i += len) {
struct ipmi_sdr_header *sdrh; struct ipmi_sdr_header *sdrh;
if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) { if (i + IPMI_SDR_HEADER_SIZE > sdrs_size) {
error_report("Problem with recid 0x%4.4x", i); error_report("Problem with recid 0x%4.4x", i);
return; break;
} }
sdrh = (struct ipmi_sdr_header *) &sdrs[i]; sdrh = (struct ipmi_sdr_header *) &sdrs[i];
len = ipmi_sdr_length(sdrh); len = ipmi_sdr_length(sdrh);
if (i + len > sdrs_size) { if (i + len > sdrs_size) {
error_report("Problem with recid 0x%4.4x", i); error_report("Problem with recid 0x%4.4x", i);
return; break;
} }
sdr_add_entry(ibs, sdrh, len, NULL); sdr_add_entry(ibs, sdrh, len, NULL);
} }
if (sdrs != init_sdrs) {
g_free(sdrs);
}
} }
static const VMStateDescription vmstate_ipmi_sim = { 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) static void ipmi_sim_realize(DeviceState *dev, Error **errp)
{ {
IPMIBmc *b = IPMI_BMC(dev); IPMIBmc *b = IPMI_BMC(dev);
@ -1763,6 +1945,8 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp)
ipmi_sdr_init(ibs); ipmi_sdr_init(ibs);
ipmi_fru_init(&ibs->fru);
ibs->acpi_power_state[0] = 0; ibs->acpi_power_state[0] = 0;
ibs->acpi_power_state[1] = 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); 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) static void ipmi_sim_class_init(ObjectClass *oc, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc);
@ -1787,6 +1978,7 @@ static void ipmi_sim_class_init(ObjectClass *oc, void *data)
dc->hotpluggable = false; dc->hotpluggable = false;
dc->realize = ipmi_sim_realize; dc->realize = ipmi_sim_realize;
dc->props = ipmi_sim_properties;
bk->handle_command = ipmi_sim_handle_command; bk->handle_command = ipmi_sim_handle_command;
} }

View File

@ -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_pci.o spapr_rtc.o spapr_drc.o spapr_rng.o
obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o
# IBM PowerNV # 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) ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy)
obj-y += spapr_pci_vfio.o obj-y += spapr_pci_vfio.o
endif endif

View File

@ -33,7 +33,11 @@
#include "exec/address-spaces.h" #include "exec/address-spaces.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#include "qapi/visitor.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/ppc/pnv_xscom.h"
#include "hw/isa/isa.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)))); 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) static void powernv_populate_chip(PnvChip *chip, void *fdt)
{ {
PnvChipClass *pcc = PNV_CHIP_GET_CLASS(chip); 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); 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++) { for (i = 0; i < chip->nr_cores; i++) {
PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize);
powernv_create_core_node(chip, pnv_core, fdt); 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) { if (chip->ram_size) {
@ -237,6 +304,127 @@ static void powernv_populate_chip(PnvChip *chip, void *fdt)
g_free(typename); 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) static void *powernv_create_fdt(MachineState *machine)
{ {
const char plat_compat[] = "qemu,powernv\0ibm,powernv"; const char plat_compat[] = "qemu,powernv\0ibm,powernv";
@ -245,6 +433,7 @@ static void *powernv_create_fdt(MachineState *machine)
char *buf; char *buf;
int off; int off;
int i; int i;
int lpc_offset;
fdt = g_malloc0(FDT_MAX_SIZE); fdt = g_malloc0(FDT_MAX_SIZE);
_FDT((fdt_create_empty_tree(fdt, 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++) { for (i = 0; i < pnv->num_chips; i++) {
powernv_populate_chip(pnv->chips[i], fdt); 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; 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) static void ppc_powernv_reset(void)
{ {
MachineState *machine = MACHINE(qdev_get_machine()); MachineState *machine = MACHINE(qdev_get_machine());
PnvMachineState *pnv = POWERNV_MACHINE(machine);
void *fdt; void *fdt;
Object *obj;
qemu_devices_reset(); 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); fdt = powernv_create_fdt(machine);
/* Pack resulting tree */ /* Pack resulting tree */
@ -302,29 +524,6 @@ static void ppc_powernv_reset(void)
cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(PNV_FDT_ADDR, fdt, fdt_totalsize(fdt));
} }
/* If we don't use the built-in LPC interrupt deserializer, we need
* to provide a set of qirqs for the ISA bus or things will go bad.
*
* Most machines using pre-Naples chips (without said deserializer)
* have a CPLD that will collect the SerIRQ and shoot them as a
* single level interrupt to the P8 chip. So let's setup a hook
* for doing just that.
*
* Note: The actual interrupt input isn't emulated yet, this will
* come with the PSI bridge model.
*/
static void pnv_lpc_isa_irq_handler_cpld(void *opaque, int n, int level)
{
/* We don't yet emulate the PSI bridge which provides the external
* interrupt, so just drop interrupts on the floor
*/
}
static void pnv_lpc_isa_irq_handler(void *opaque, int n, int level)
{
/* XXX TODO */
}
static ISABus *pnv_isa_create(PnvChip *chip) static ISABus *pnv_isa_create(PnvChip *chip)
{ {
PnvLpcController *lpc = &chip->lpc; 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, isa_bus = isa_bus_new(NULL, &lpc->isa_mem, &lpc->isa_io,
&error_fatal); &error_fatal);
/* Not all variants have a working serial irq decoder. If not, irqs = pnv_lpc_isa_irq_create(lpc, pcc->chip_type, ISA_NUM_IRQS);
* handling of LPC interrupts becomes a platform issue (some
* platforms have a CPLD to do it).
*/
if (pcc->chip_type == PNV_CHIP_POWER8NVL) {
irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler, chip, ISA_NUM_IRQS);
} else {
irqs = qemu_allocate_irqs(pnv_lpc_isa_irq_handler_cpld, chip,
ISA_NUM_IRQS);
}
isa_bus_irqs(isa_bus, irqs); isa_bus_irqs(isa_bus, irqs);
return isa_bus; return isa_bus;
@ -457,6 +647,11 @@ static void ppc_powernv_init(MachineState *machine)
/* Create an RTC ISA device too */ /* Create an RTC ISA device too */
rtc_init(pnv->isa_bus, 2000, NULL); 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_initialize(&chip->lpc, sizeof(chip->lpc), TYPE_PNV_LPC);
object_property_add_child(obj, "lpc", OBJECT(&chip->lpc), NULL); 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) 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), object_property_set_int(OBJECT(pnv_core),
pcc->core_pir(chip, core_hwid), pcc->core_pir(chip, core_hwid),
"pir", &error_fatal); "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", object_property_set_bool(OBJECT(pnv_core), true, "realized",
&error_fatal); &error_fatal);
object_unref(OBJECT(pnv_core)); 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", object_property_set_bool(OBJECT(&chip->lpc), true, "realized",
&error_fatal); &error_fatal);
pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip->lpc.xscom_regs); 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[] = { static Property pnv_chip_properties[] = {
@ -723,6 +992,7 @@ static void pnv_chip_class_init(ObjectClass *klass, void *data)
{ {
DeviceClass *dc = DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass);
set_bit(DEVICE_CATEGORY_CPU, dc->categories);
dc->realize = pnv_chip_realize; dc->realize = pnv_chip_realize;
dc->props = pnv_chip_properties; dc->props = pnv_chip_properties;
dc->desc = "PowerNV Chip"; dc->desc = "PowerNV Chip";
@ -737,6 +1007,70 @@ static const TypeInfo pnv_chip_info = {
.abstract = true, .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, static void pnv_get_num_chips(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp) 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) static void powernv_machine_class_init(ObjectClass *oc, void *data)
{ {
MachineClass *mc = MACHINE_CLASS(oc); 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->desc = "IBM PowerNV (Non-Virtualized)";
mc->init = ppc_powernv_init; mc->init = ppc_powernv_init;
@ -797,6 +1133,10 @@ static void powernv_machine_class_init(ObjectClass *oc, void *data)
mc->no_parallel = 1; mc->no_parallel = 1;
mc->default_boot_order = NULL; mc->default_boot_order = NULL;
mc->default_ram_size = 1 * G_BYTE; 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); powernv_machine_class_props_init(oc);
} }
@ -807,6 +1147,11 @@ static const TypeInfo powernv_machine_info = {
.instance_size = sizeof(PnvMachineState), .instance_size = sizeof(PnvMachineState),
.instance_init = powernv_machine_initfn, .instance_init = powernv_machine_initfn,
.class_init = powernv_machine_class_init, .class_init = powernv_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_XICS_FABRIC },
{ TYPE_INTERRUPT_STATS_PROVIDER },
{ },
},
}; };
static void powernv_machine_register_types(void) static void powernv_machine_register_types(void)

122
hw/ppc/pnv_bmc.c Normal file
View 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)));
}
}

View File

@ -25,6 +25,7 @@
#include "hw/ppc/pnv.h" #include "hw/ppc/pnv.h"
#include "hw/ppc/pnv_core.h" #include "hw/ppc/pnv_core.h"
#include "hw/ppc/pnv_xscom.h" #include "hw/ppc/pnv_xscom.h"
#include "hw/ppc/xics.h"
static void powernv_cpu_reset(void *opaque) static void powernv_cpu_reset(void *opaque)
{ {
@ -110,23 +111,37 @@ static const MemoryRegionOps pnv_core_xscom_ops = {
.endianness = DEVICE_BIG_ENDIAN, .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; Error *local_err = NULL;
CPUState *cs = CPU(child); CPUState *cs = CPU(child);
PowerPCCPU *cpu = POWERPC_CPU(cs); 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); object_property_set_bool(child, true, "realized", &local_err);
if (local_err) { if (local_err) {
object_unparent(obj);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
powernv_cpu_init(cpu, &local_err); powernv_cpu_init(cpu, &local_err);
if (local_err) { if (local_err) {
object_unparent(obj);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
xics_cpu_setup(xi, cpu, ICP(obj));
} }
static void pnv_core_realize(DeviceState *dev, Error **errp) static void pnv_core_realize(DeviceState *dev, Error **errp)
@ -140,6 +155,14 @@ static void pnv_core_realize(DeviceState *dev, Error **errp)
void *obj; void *obj;
int i, j; int i, j;
char name[32]; 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); pc->threads = g_malloc0(size * cc->nr_threads);
for (i = 0; i < cc->nr_threads; i++) { 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++) { for (j = 0; j < cc->nr_threads; j++) {
obj = pc->threads + j * size; 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) { if (local_err) {
goto err; goto err;
} }

View File

@ -92,14 +92,6 @@ enum {
#define LPC_HC_REGS_OPB_SIZE 0x00001000 #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) static int pnv_lpc_populate(PnvXScomInterface *dev, void *fdt, int xscom_offset)
{ {
const char compat[] = "ibm,power8-lpc\0ibm,lpc"; 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(fdt, offset, "reg", reg, sizeof(reg))));
_FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2)));
_FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); _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)))); _FDT((fdt_setprop(fdt, offset, "compatible", compat, sizeof(compat))));
return 0; return 0;
} }
@ -250,6 +241,34 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = {
.endianness = DEVICE_BIG_ENDIAN, .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) static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size)
{ {
PnvLpcController *lpc = opaque; PnvLpcController *lpc = opaque;
@ -300,12 +319,15 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val,
break; break;
case LPC_HC_IRQSER_CTRL: case LPC_HC_IRQSER_CTRL:
lpc->lpc_hc_irqser_ctrl = val; lpc->lpc_hc_irqser_ctrl = val;
pnv_lpc_eval_irqs(lpc);
break; break;
case LPC_HC_IRQMASK: case LPC_HC_IRQMASK:
lpc->lpc_hc_irqmask = val; lpc->lpc_hc_irqmask = val;
pnv_lpc_eval_irqs(lpc);
break; break;
case LPC_HC_IRQSTAT: case LPC_HC_IRQSTAT:
lpc->lpc_hc_irqstat &= ~val; lpc->lpc_hc_irqstat &= ~val;
pnv_lpc_eval_irqs(lpc);
break; break;
case LPC_HC_ERROR_ADDRESS: case LPC_HC_ERROR_ADDRESS:
break; break;
@ -363,14 +385,15 @@ static void opb_master_write(void *opaque, hwaddr addr,
switch (addr) { switch (addr) {
case OPB_MASTER_LS_IRQ_STAT: case OPB_MASTER_LS_IRQ_STAT:
lpc->opb_irq_stat &= ~val; lpc->opb_irq_stat &= ~val;
pnv_lpc_eval_irqs(lpc);
break; break;
case OPB_MASTER_LS_IRQ_MASK: case OPB_MASTER_LS_IRQ_MASK:
/* XXX Filter out reserved bits */
lpc->opb_irq_mask = val; lpc->opb_irq_mask = val;
pnv_lpc_eval_irqs(lpc);
break; break;
case OPB_MASTER_LS_IRQ_POL: case OPB_MASTER_LS_IRQ_POL:
/* XXX Filter out reserved bits */
lpc->opb_irq_pol = val; lpc->opb_irq_pol = val;
pnv_lpc_eval_irqs(lpc);
break; break;
case OPB_MASTER_LS_IRQ_INPUT: case OPB_MASTER_LS_IRQ_INPUT:
/* Read only */ /* Read only */
@ -398,6 +421,8 @@ static const MemoryRegionOps opb_master_ops = {
static void pnv_lpc_realize(DeviceState *dev, Error **errp) static void pnv_lpc_realize(DeviceState *dev, Error **errp)
{ {
PnvLpcController *lpc = PNV_LPC(dev); PnvLpcController *lpc = PNV_LPC(dev);
Object *obj;
Error *error = NULL;
/* Reg inits */ /* Reg inits */
lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; 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_xscom_region_init(&lpc->xscom_regs, OBJECT(dev),
&pnv_lpc_xscom_ops, lpc, "xscom-lpc", &pnv_lpc_xscom_ops, lpc, "xscom-lpc",
PNV_XSCOM_LPC_SIZE); 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) 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) 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
View 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
View 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)

View File

@ -40,6 +40,7 @@
#include "kvm_ppc.h" #include "kvm_ppc.h"
#include "migration/migration.h" #include "migration/migration.h"
#include "mmu-hash64.h" #include "mmu-hash64.h"
#include "mmu-book3s-v3.h"
#include "qom/cpu.h" #include "qom/cpu.h"
#include "hw/boards.h" #include "hw/boards.h"
@ -96,66 +97,40 @@
#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift)) #define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
static int try_create_xics(sPAPRMachineState *spapr, const char *type_ics, static ICSState *spapr_ics_create(sPAPRMachineState *spapr,
const char *type_icp, int nr_servers, const char *type_ics,
int nr_irqs, Error **errp) int nr_irqs, Error **errp)
{ {
XICSFabric *xi = XICS_FABRIC(spapr);
Error *err = NULL, *local_err = NULL; Error *err = NULL, *local_err = NULL;
ICSState *ics = NULL; Object *obj;
int i;
ics = ICS_SIMPLE(object_new(type_ics)); obj = object_new(type_ics);
object_property_add_child(OBJECT(spapr), "ics", OBJECT(ics), NULL); object_property_add_child(OBJECT(spapr), "ics", obj, NULL);
object_property_set_int(OBJECT(ics), nr_irqs, "nr-irqs", &err); object_property_add_const_link(obj, "xics", OBJECT(spapr), &error_abort);
object_property_add_const_link(OBJECT(ics), "xics", OBJECT(xi), NULL); object_property_set_int(obj, nr_irqs, "nr-irqs", &err);
object_property_set_bool(OBJECT(ics), true, "realized", &local_err); object_property_set_bool(obj, true, "realized", &local_err);
error_propagate(&err, local_err); error_propagate(&err, local_err);
if (err) { if (err) {
goto error; error_propagate(errp, err);
return NULL;
} }
spapr->icps = g_malloc0(nr_servers * sizeof(ICPState)); return ICS_SIMPLE(obj);
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;
} }
static int xics_system_init(MachineState *machine, static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp)
int nr_servers, int nr_irqs, Error **errp)
{ {
int rc = -1; sPAPRMachineState *spapr = SPAPR_MACHINE(machine);
if (kvm_enabled()) { if (kvm_enabled()) {
Error *err = NULL; Error *err = NULL;
if (machine_kernel_irqchip_allowed(machine) && if (machine_kernel_irqchip_allowed(machine) &&
!xics_kvm_init(SPAPR_MACHINE(machine), errp)) { !xics_kvm_init(spapr, errp)) {
rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_KVM, spapr->icp_type = TYPE_KVM_ICP;
TYPE_KVM_ICP, nr_servers, nr_irqs, &err); 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, error_reportf_err(err,
"kernel_irqchip requested but unavailable: "); "kernel_irqchip requested but unavailable: ");
} else { } else {
@ -163,13 +138,11 @@ static int xics_system_init(MachineState *machine,
} }
} }
if (rc < 0) { if (!spapr->ics) {
xics_spapr_init(SPAPR_MACHINE(machine), errp); xics_spapr_init(spapr, errp);
rc = try_create_xics(SPAPR_MACHINE(machine), TYPE_ICS_SIMPLE, spapr->icp_type = TYPE_ICP;
TYPE_ICP, nr_servers, nr_irqs, errp); 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, 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; 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) static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
{ {
int ret = 0, offset, cpus_offset; int ret = 0, offset, cpus_offset;
@ -236,6 +288,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr)
CPU_FOREACH(cs) { CPU_FOREACH(cs) {
PowerPCCPU *cpu = POWERPC_CPU(cs); PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
DeviceClass *dc = DEVICE_GET_CLASS(cs); DeviceClass *dc = DEVICE_GET_CLASS(cs);
int index = ppc_get_vcpu_dt_id(cpu); int index = ppc_get_vcpu_dt_id(cpu);
int compat_smt = MIN(smp_threads, ppc_compat_max_threads(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) { if (ret < 0) {
return ret; return ret;
} }
spapr_populate_pa_features(env, fdt, offset,
spapr->cas_legacy_guest_workaround);
} }
return ret; return ret;
} }
@ -378,67 +434,6 @@ static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt)
return 0; 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, static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
sPAPRMachineState *spapr) sPAPRMachineState *spapr)
{ {
@ -459,6 +454,8 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset,
sPAPRDRConnector *drc; sPAPRDRConnector *drc;
sPAPRDRConnectorClass *drck; sPAPRDRConnectorClass *drck;
int drc_index; 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); drc = spapr_dr_connector_by_id(SPAPR_DR_CONNECTOR_TYPE_CPU, index);
if (drc) { 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))); 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", _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id",
cs->cpu_index / vcpus_per_socket))); 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_numa_dt(fdt, offset, cs));
_FDT(spapr_fixup_cpu_smt_dt(fdt, offset, cpu, compat_smt)); _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) 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); 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) static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt)
{ {
MachineState *machine = MACHINE(spapr); 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)); _FDT(fdt_setprop_string(fdt, chosen, "linux,stdout-path", stdout_path));
} }
spapr_dt_ov5_platform_support(fdt, chosen);
g_free(stdout_path); g_free(stdout_path);
g_free(bootlist); g_free(bootlist);
} }
@ -933,6 +970,7 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr,
void *fdt; void *fdt;
sPAPRPHBState *phb; sPAPRPHBState *phb;
char *buf; char *buf;
int smt = kvmppc_smt_threads();
fdt = g_malloc0(FDT_MAX_SIZE); fdt = g_malloc0(FDT_MAX_SIZE);
_FDT((fdt_create_empty_tree(fdt, 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)); _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2));
/* /interrupt controller */ /* /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); ret = spapr_populate_memory(spapr, fdt);
if (ret < 0) { if (ret < 0) {
@ -1100,7 +1138,7 @@ static int get_htab_fd(sPAPRMachineState *spapr)
return spapr->htab_fd; return spapr->htab_fd;
} }
static void close_htab_fd(sPAPRMachineState *spapr) void close_htab_fd(sPAPRMachineState *spapr)
{ {
if (spapr->htab_fd >= 0) { if (spapr->htab_fd >= 0) {
close(spapr->htab_fd); 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) static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque)
{ {
bool matched = false; bool matched = false;
@ -1255,17 +1306,14 @@ static void ppc_spapr_reset(void)
/* Check for unknown sysbus devices */ /* Check for unknown sysbus devices */
foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL);
spapr->patb_entry = 0; if (kvm_enabled() && kvmppc_has_cap_mmu_radix()) {
/* If using KVM with radix mode available, VCPUs can be started
/* Allocate and/or reset the hash page table */ * without a HPT because KVM will start them in radix mode.
spapr_reallocate_hpt(spapr, * Set the GR bit in PATB so that we know there is no HPT. */
spapr_hpt_shift_for_ramsize(machine->maxram_size), spapr->patb_entry = PATBE1_GR;
&error_fatal); } else {
spapr->patb_entry = 0;
/* Update the RMA size if necessary */ spapr_setup_hpt_and_vrma(spapr);
if (spapr->vrma_adjust) {
spapr->rma_size = kvmppc_rma_size(spapr_node0_size(),
spapr->htab_shift);
} }
qemu_devices_reset(); qemu_devices_reset();
@ -1333,13 +1381,13 @@ static void spapr_create_nvram(sPAPRMachineState *spapr)
static void spapr_rtc_create(sPAPRMachineState *spapr) static void spapr_rtc_create(sPAPRMachineState *spapr)
{ {
DeviceState *dev = qdev_create(NULL, TYPE_SPAPR_RTC); object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC);
object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc),
qdev_init_nofail(dev); &error_fatal);
spapr->rtc = dev; object_property_set_bool(OBJECT(&spapr->rtc), true, "realized",
&error_fatal);
object_property_add_alias(qdev_get_machine(), "rtc-time", object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc),
OBJECT(spapr->rtc), "date", NULL); "date", &error_fatal);
} }
/* Returns whether we want to use VGA or not */ /* 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; int err = 0;
if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) {
int i; CPUState *cs;
for (i = 0; i < spapr->nr_servers; i++) { CPU_FOREACH(cs) {
icp_resend(&spapr->icps[i]); 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 * So when migrating from those versions, poke the incoming offset
* value into the RTC device */ * value into the RTC device */
if (version_id < 3) { 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; return err;
@ -1990,7 +2039,6 @@ static void ppc_spapr_init(MachineState *machine)
hwaddr node0_size = spapr_node0_size(); hwaddr node0_size = spapr_node0_size();
long load_limit, fw_size; long load_limit, fw_size;
char *filename; char *filename;
int smt = kvmppc_smt_threads();
msi_nonbroken = true; 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; load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD;
/* Set up Interrupt Controller before we create the VCPUs */ /* Set up Interrupt Controller before we create the VCPUs */
xics_system_init(machine, DIV_ROUND_UP(max_cpus * smt, smp_threads), xics_system_init(machine, XICS_IRQS_SPAPR, &error_fatal);
XICS_IRQS_SPAPR, &error_fatal);
/* Set up containers for ibm,client-set-architecture negotiated options */ /* Set up containers for ibm,client-set-architecture negotiated options */
spapr->ov5 = spapr_ovec_new(); spapr->ov5 = spapr_ovec_new();
@ -2054,6 +2101,11 @@ static void ppc_spapr_init(MachineState *machine)
} }
spapr_ovec_set(spapr->ov5, OV5_FORM1_AFFINITY); 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 */ /* advertise support for dedicated HP event source to guests */
if (spapr->use_hotplug_event_source) { 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); qemu_register_boot_set(spapr_boot_set, spapr);
/* to stop and start vmclock */
if (kvm_enabled()) { if (kvm_enabled()) {
/* to stop and start vmclock */
qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change, qemu_add_vm_change_state_handler(cpu_ppc_clock_vm_state_change,
&spapr->tb); &spapr->tb);
kvmppc_spapr_enable_inkernel_multitce();
} }
} }
@ -3030,21 +3084,23 @@ static void spapr_ics_resend(XICSFabric *dev)
ics_resend(spapr->ics); 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, static void spapr_pic_print_info(InterruptStatsProvider *obj,
Monitor *mon) Monitor *mon)
{ {
sPAPRMachineState *spapr = SPAPR_MACHINE(obj); sPAPRMachineState *spapr = SPAPR_MACHINE(obj);
int i; CPUState *cs;
for (i = 0; i < spapr->nr_servers; i++) { CPU_FOREACH(cs) {
icp_pic_print_info(&spapr->icps[i], mon); PowerPCCPU *cpu = POWERPC_CPU(cs);
icp_pic_print_info(ICP(cpu->intc), mon);
} }
ics_pic_print_info(spapr->ics, mon); ics_pic_print_info(spapr->ics, mon);
@ -3158,18 +3214,37 @@ static const TypeInfo spapr_machine_info = {
type_init(spapr_machine_register_##suffix) 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 */ /* 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 * pseries-2.8

View File

@ -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); qemu_register_reset(spapr_cpu_reset, cpu);
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); PowerPCCPU *cpu = POWERPC_CPU(cs);
spapr_cpu_destroy(cpu); spapr_cpu_destroy(cpu);
object_unparent(cpu->intc);
cpu_remove_sync(cs); cpu_remove_sync(cs);
object_unparent(obj); 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()); sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine());
CPUState *cs = CPU(child); CPUState *cs = CPU(child);
PowerPCCPU *cpu = POWERPC_CPU(cs); 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); object_property_set_bool(child, true, "realized", &local_err);
if (local_err) { if (local_err) {
object_unparent(obj);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
spapr_cpu_init(spapr, cpu, &local_err); spapr_cpu_init(spapr, cpu, &local_err);
if (local_err) { if (local_err) {
object_unparent(obj);
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
} }
xics_cpu_setup(XICS_FABRIC(spapr), cpu, ICP(obj));
} }
static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) static void spapr_cpu_core_realize(DeviceState *dev, Error **errp)

View File

@ -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_id = cpu_to_be16(RTAS_LOG_V6_SECTION_ID_MAINA);
maina->hdr.section_length = cpu_to_be16(sizeof(*maina)); maina->hdr.section_length = cpu_to_be16(sizeof(*maina));
/* FIXME: section version, subtype and creator id? */ /* 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; year = tm.tm_year + 1900;
maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24) maina->creation_date = cpu_to_be32((to_bcd(year / 100) << 24)
| (to_bcd(year % 100) << 16) | (to_bcd(year % 100) << 16)

View File

@ -12,6 +12,8 @@
#include "trace.h" #include "trace.h"
#include "kvm_ppc.h" #include "kvm_ppc.h"
#include "hw/ppc/spapr_ovec.h" #include "hw/ppc/spapr_ovec.h"
#include "qemu/error-report.h"
#include "mmu-book3s-v3.h"
struct SPRSyncState { struct SPRSyncState {
int spr; int spr;
@ -878,6 +880,137 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return ret; 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_ALL -1
#define H_SIGNAL_SYS_RESET_ALLBUTSELF -2 #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 max_compat = cpu->max_compat;
uint32_t best_compat = 0; uint32_t best_compat = 0;
int i; 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 * 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 */ /* For the future use: here @ov_table points to the first option vector */
ov_table = list; ov_table = list;
ov1_guest = spapr_ovec_parse_vector(ov_table, 1);
ov5_guest = spapr_ovec_parse_vector(ov_table, 5); 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 /* NOTE: there are actually a number of ov5 bits where input from the
* guest is always zero, and the platform/QEMU enables them independently * 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(); ov5_updates = spapr_ovec_new();
spapr->cas_reboot = spapr_ovec_diff(ov5_updates, spapr->cas_reboot = spapr_ovec_diff(ov5_updates,
ov5_cas_old, spapr->ov5_cas); 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) { if (!spapr->cas_reboot) {
spapr->cas_reboot = spapr->cas_reboot =
(spapr_h_cas_compose_response(spapr, args[1], args[2], (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) { if (spapr->cas_reboot) {
qemu_system_reset_request(); 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; 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_PAGE_INIT, h_page_init);
spapr_register_hypercall(H_SET_MODE, h_set_mode); 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 /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate
* here between the "CI" and the "CACHE" variants, they will use whatever * here between the "CI" and the "CACHE" variants, they will use whatever
* mapping attributes qemu is using. When using KVM, the kernel will * mapping attributes qemu is using. When using KVM, the kernel will

View File

@ -79,15 +79,16 @@ static IOMMUAccessFlags spapr_tce_iommu_access_flags(uint64_t tce)
static uint64_t *spapr_tce_alloc_table(uint32_t liobn, static uint64_t *spapr_tce_alloc_table(uint32_t liobn,
uint32_t page_shift, uint32_t page_shift,
uint64_t bus_offset,
uint32_t nb_table, uint32_t nb_table,
int *fd, int *fd,
bool need_vfio) bool need_vfio)
{ {
uint64_t *table = NULL; uint64_t *table = NULL;
uint64_t window_size = (uint64_t)nb_table << page_shift;
if (kvm_enabled() && !(window_size >> 32)) { if (kvm_enabled()) {
table = kvmppc_create_spapr_tce(liobn, window_size, fd, need_vfio); table = kvmppc_create_spapr_tce(liobn, page_shift, bus_offset, nb_table,
fd, need_vfio);
} }
if (!table) { if (!table) {
@ -342,6 +343,7 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet,
tcet->nb_table = nb_table; tcet->nb_table = nb_table;
tcet->table = spapr_tce_alloc_table(tcet->liobn, tcet->table = spapr_tce_alloc_table(tcet->liobn,
tcet->page_shift, tcet->page_shift,
tcet->bus_offset,
tcet->nb_table, tcet->nb_table,
&tcet->fd, &tcet->fd,
tcet->need_vfio); tcet->need_vfio);

View File

@ -50,8 +50,6 @@
#include "sysemu/hostmem.h" #include "sysemu/hostmem.h"
#include "sysemu/numa.h" #include "sysemu/numa.h"
#include "hw/vfio/vfio.h"
/* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */ /* Copied from the kernel arch/powerpc/platforms/pseries/msi.c */
#define RTAS_QUERY_FN 0 #define RTAS_QUERY_FN 0
#define RTAS_CHANGE_FN 1 #define RTAS_CHANGE_FN 1
@ -1771,6 +1769,12 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp)
} }
/* DMA setup */ /* 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) { for (i = 0; i < windows_supported; ++i) {
tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]); tcet = spapr_tce_new_table(DEVICE(sphb), sphb->dma_liobn[i]);
if (!tcet) { if (!tcet) {

View File

@ -33,19 +33,8 @@
#include "qapi-event.h" #include "qapi-event.h"
#include "qemu/cutils.h" #include "qemu/cutils.h"
#define SPAPR_RTC(obj) \ void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns)
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)
{ {
sPAPRRTCState *rtc = SPAPR_RTC(dev);
int64_t host_ns = qemu_clock_get_ns(rtc_clock); int64_t host_ns = qemu_clock_get_ns(rtc_clock);
int64_t guest_ns; int64_t guest_ns;
time_t guest_s; 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 (!rtc) {
if (!dev) {
return -ENODEV; return -ENODEV;
} }
rtc = SPAPR_RTC(dev);
rtc->ns_offset = legacy_offset * NANOSECONDS_PER_SECOND; rtc->ns_offset = legacy_offset * NANOSECONDS_PER_SECOND;
return 0; return 0;
@ -91,12 +76,7 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return; return;
} }
if (!spapr->rtc) { spapr_rtc_read(&spapr->rtc, &tm, &ns);
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
spapr_rtc_read(spapr->rtc, &tm, &ns);
rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 0, RTAS_OUT_SUCCESS);
rtas_st(rets, 1, tm.tm_year + 1900); 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, target_ulong args,
uint32_t nret, target_ulong rets) uint32_t nret, target_ulong rets)
{ {
sPAPRRTCState *rtc; sPAPRRTCState *rtc = &spapr->rtc;
struct tm tm; struct tm tm;
time_t new_s; time_t new_s;
int64_t host_ns; int64_t host_ns;
@ -123,11 +103,6 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr,
return; return;
} }
if (!spapr->rtc) {
rtas_st(rets, 0, RTAS_OUT_HW_ERROR);
return;
}
tm.tm_year = rtas_ld(args, 0) - 1900; tm.tm_year = rtas_ld(args, 0) - 1900;
tm.tm_mon = rtas_ld(args, 1) - 1; tm.tm_mon = rtas_ld(args, 1) - 1;
tm.tm_mday = rtas_ld(args, 2); 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 */ /* Generate a monitor event for the change */
qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort);
rtc = SPAPR_RTC(spapr->rtc);
host_ns = qemu_clock_get_ns(rtc_clock); host_ns = qemu_clock_get_ns(rtc_clock);
rtc->ns_offset = (new_s * NANOSECONDS_PER_SECOND) - host_ns; 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) 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) 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 = { static const TypeInfo spapr_rtc_info = {
.name = TYPE_SPAPR_RTC, .name = TYPE_SPAPR_RTC,
.parent = TYPE_SYS_BUS_DEVICE, .parent = TYPE_DEVICE,
.instance_size = sizeof(sPAPRRTCState), .instance_size = sizeof(sPAPRRTCState),
.class_init = spapr_rtc_class_init, .class_init = spapr_rtc_class_init,
}; };

View File

@ -259,4 +259,8 @@ struct ipmi_sdr_compact {
typedef uint8_t ipmi_sdr_compact_buffer[sizeof(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 #endif

View File

@ -22,6 +22,8 @@
#include "hw/boards.h" #include "hw/boards.h"
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "hw/ppc/pnv_lpc.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 TYPE_PNV_CHIP "powernv-chip"
#define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP)
@ -54,8 +56,11 @@ typedef struct PnvChip {
MemoryRegion xscom_mmio; MemoryRegion xscom_mmio;
MemoryRegion xscom; MemoryRegion xscom;
AddressSpace xscom_as; AddressSpace xscom_as;
MemoryRegion icp_mmio;
PnvLpcController lpc; PnvLpcController lpc;
PnvPsi psi;
PnvOCC occ;
} PnvChip; } PnvChip;
typedef struct PnvChipClass { typedef struct PnvChipClass {
@ -91,18 +96,30 @@ typedef struct PnvChipClass {
OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP_POWER9) 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 * 0x0, 0x1, 0x10, 0x11
* *
* 4 chips should be the maximum * 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)) #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 TYPE_POWERNV_MACHINE MACHINE_TYPE_NAME("powernv")
#define POWERNV_MACHINE(obj) \ #define POWERNV_MACHINE(obj) \
OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE) OBJECT_CHECK(PnvMachineState, (obj), TYPE_POWERNV_MACHINE)
typedef struct IPMIBmc IPMIBmc;
typedef struct PnvMachineState { typedef struct PnvMachineState {
/*< private >*/ /*< private >*/
MachineState parent_obj; MachineState parent_obj;
@ -114,11 +131,21 @@ typedef struct PnvMachineState {
PnvChip **chips; PnvChip **chips;
ISABus *isa_bus; ISABus *isa_bus;
uint32_t cpld_irqstate;
IPMIBmc *bmc;
Notifier powerdown_notifier;
} PnvMachineState; } PnvMachineState;
#define PNV_FDT_ADDR 0x01000000 #define PNV_FDT_ADDR 0x01000000
#define PNV_TIMEBASE_FREQ 512000000ULL #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 * POWER8 MMIO base addresses
*/ */
@ -126,4 +153,32 @@ typedef struct PnvMachineState {
#define PNV_XSCOM_BASE(chip) \ #define PNV_XSCOM_BASE(chip) \
(chip->xscom_base + ((uint64_t)(chip)->chip_id) * PNV_XSCOM_SIZE) (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 */ #endif /* _PPC_PNV_H */

View File

@ -23,6 +23,8 @@
#define PNV_LPC(obj) \ #define PNV_LPC(obj) \
OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC)
typedef struct PnvPsi PnvPsi;
typedef struct PnvLpcController { typedef struct PnvLpcController {
DeviceState parent; DeviceState parent;
@ -62,6 +64,12 @@ typedef struct PnvLpcController {
/* XSCOM registers */ /* XSCOM registers */
MemoryRegion xscom_regs; MemoryRegion xscom_regs;
/* PSI to generate interrupts */
PnvPsi *psi;
} PnvLpcController; } PnvLpcController;
qemu_irq *pnv_lpc_isa_irq_create(PnvLpcController *lpc, int chip_type,
int nirqs);
#endif /* _PPC_PNV_LPC_H */ #endif /* _PPC_PNV_LPC_H */

38
include/hw/ppc/pnv_occ.h Normal file
View 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
View 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 */

View File

@ -60,6 +60,12 @@ typedef struct PnvXScomInterfaceClass {
#define PNV_XSCOM_LPC_BASE 0xb0020 #define PNV_XSCOM_LPC_BASE 0xb0020
#define PNV_XSCOM_LPC_SIZE 0x4 #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 void pnv_xscom_realize(PnvChip *chip, Error **errp);
extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset); extern int pnv_xscom_populate(PnvChip *chip, void *fdt, int offset);

View File

@ -20,6 +20,18 @@ typedef struct sPAPREventSource sPAPREventSource;
#define SPAPR_TIMEBASE_FREQ 512000000ULL #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; typedef struct sPAPRMachineClass sPAPRMachineClass;
#define TYPE_SPAPR_MACHINE "spapr-machine" #define TYPE_SPAPR_MACHINE "spapr-machine"
@ -58,7 +70,7 @@ struct sPAPRMachineState {
QLIST_HEAD(, sPAPRPHBState) phbs; QLIST_HEAD(, sPAPRPHBState) phbs;
struct sPAPRNVRAM *nvram; struct sPAPRNVRAM *nvram;
ICSState *ics; ICSState *ics;
DeviceState *rtc; sPAPRRTCState rtc;
void *htab; void *htab;
uint32_t htab_shift; uint32_t htab_shift;
@ -77,6 +89,7 @@ struct sPAPRMachineState {
sPAPROptionVector *ov5; /* QEMU-supported option vectors */ sPAPROptionVector *ov5; /* QEMU-supported option vectors */
sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */
bool cas_reboot; bool cas_reboot;
bool cas_legacy_guest_workaround;
Notifier epow_notifier; Notifier epow_notifier;
QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; QTAILQ_HEAD(, sPAPREventLogEntry) pending_events;
@ -95,8 +108,7 @@ struct sPAPRMachineState {
char *kvm_type; char *kvm_type;
MemoryHotplugState hotplug_memory; MemoryHotplugState hotplug_memory;
uint32_t nr_servers; const char *icp_type;
ICPState *icps;
}; };
#define H_SUCCESS 0 #define H_SUCCESS 0
@ -349,6 +361,9 @@ struct sPAPRMachineState {
#define H_XIRR_X 0x2FC #define H_XIRR_X 0x2FC
#define H_RANDOM 0x300 #define H_RANDOM 0x300
#define H_SET_MODE 0x31C #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 H_SIGNAL_SYS_RESET 0x380
#define MAX_HCALL_OPCODE H_SIGNAL_SYS_RESET #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, int spapr_h_cas_compose_response(sPAPRMachineState *sm,
target_ulong addr, target_ulong size, target_ulong addr, target_ulong size,
sPAPROptionVector *ov5_updates); 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); sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn);
void spapr_tce_table_enable(sPAPRTCETable *tcet, void spapr_tce_table_enable(sPAPRTCETable *tcet,
uint32_t page_shift, uint64_t bus_offset, uint32_t page_shift, uint64_t bus_offset,
@ -629,11 +646,10 @@ struct sPAPRConfigureConnectorState {
void spapr_ccs_reset_hook(void *opaque); void spapr_ccs_reset_hook(void *opaque);
#define TYPE_SPAPR_RTC "spapr-rtc" void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns);
#define TYPE_SPAPR_RNG "spapr-rng" int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset);
void spapr_rtc_read(DeviceState *dev, struct tm *tm, uint32_t *ns); #define TYPE_SPAPR_RNG "spapr-rng"
int spapr_rtc_import_offset(DeviceState *dev, int64_t legacy_offset);
int spapr_rng_populate_dt(void *fdt); int spapr_rng_populate_dt(void *fdt);

View File

@ -43,11 +43,19 @@ typedef struct sPAPROptionVector sPAPROptionVector;
#define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) #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 */ /* option vector 5 */
#define OV5_DRCONF_MEMORY OV_BIT(2, 2) #define OV5_DRCONF_MEMORY OV_BIT(2, 2)
#define OV5_FORM1_AFFINITY OV_BIT(5, 0) #define OV5_FORM1_AFFINITY OV_BIT(5, 0)
#define OV5_HP_EVT OV_BIT(6, 5) #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 */ /* interfaces */
sPAPROptionVector *spapr_ovec_new(void); sPAPROptionVector *spapr_ovec_new(void);
sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig);

View File

@ -28,7 +28,7 @@
#ifndef XICS_H #ifndef XICS_H
#define XICS_H #define XICS_H
#include "hw/sysbus.h" #include "hw/qdev.h"
#define XICS_IPI 0x2 #define XICS_IPI 0x2
#define XICS_BUID 0x1 #define XICS_BUID 0x1
@ -41,10 +41,12 @@
*/ */
typedef struct ICPStateClass ICPStateClass; typedef struct ICPStateClass ICPStateClass;
typedef struct ICPState ICPState; typedef struct ICPState ICPState;
typedef struct PnvICPState PnvICPState;
typedef struct ICSStateClass ICSStateClass; typedef struct ICSStateClass ICSStateClass;
typedef struct ICSState ICSState; typedef struct ICSState ICSState;
typedef struct ICSIRQState ICSIRQState; typedef struct ICSIRQState ICSIRQState;
typedef struct XICSFabric XICSFabric; typedef struct XICSFabric XICSFabric;
typedef struct PowerPCCPU PowerPCCPU;
#define TYPE_ICP "icp" #define TYPE_ICP "icp"
#define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_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 TYPE_KVM_ICP "icp-kvm"
#define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) #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) \ #define ICP_CLASS(klass) \
OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP) OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP)
#define ICP_GET_CLASS(obj) \ #define ICP_GET_CLASS(obj) \
@ -60,6 +65,7 @@ typedef struct XICSFabric XICSFabric;
struct ICPStateClass { struct ICPStateClass {
DeviceClass parent_class; DeviceClass parent_class;
void (*realize)(DeviceState *dev, Error **errp);
void (*pre_save)(ICPState *s); void (*pre_save)(ICPState *s);
int (*post_load)(ICPState *s, int version_id); int (*post_load)(ICPState *s, int version_id);
void (*cpu_setup)(ICPState *icp, PowerPCCPU *cpu); void (*cpu_setup)(ICPState *icp, PowerPCCPU *cpu);
@ -80,6 +86,13 @@ struct ICPState {
XICSFabric *xics; XICSFabric *xics;
}; };
struct PnvICPState {
ICPState parent_obj;
MemoryRegion mmio;
uint32_t links[3];
};
#define TYPE_ICS_BASE "ics-base" #define TYPE_ICS_BASE "ics-base"
#define ICS_BASE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_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); qemu_irq xics_get_qirq(XICSFabric *xi, int irq);
ICPState *xics_icp_get(XICSFabric *xi, int server); 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); void xics_cpu_destroy(XICSFabric *xi, PowerPCCPU *cpu);
/* Internal XICS interfaces */ /* 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_cppr(ICPState *icp, uint8_t cppr);
void icp_set_mfrr(ICPState *icp, uint8_t mfrr); void icp_set_mfrr(ICPState *icp, uint8_t mfrr);
uint32_t icp_accept(ICPState *ss); uint32_t icp_accept(ICPState *ss);

View File

@ -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. * Returns: 0 on success, or a negative errno on failure.
*/ */
int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target); 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); int kvm_get_max_memslots(void);
#endif #endif

View File

@ -425,7 +425,7 @@ possible drivers and properties, use @code{-device help} and
@code{-device @var{driver},help}. @code{-device @var{driver},help}.
Some drivers are: 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 Add an IPMI BMC. This is a simulation of a hardware management
interface processor that normally sits on a system. It provides 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 controllers. If you don't know what this means, it is safe to ignore
it. 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}] @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 Add a connection to an external IPMI BMC simulator. Instead of

View File

@ -50,7 +50,7 @@ struct PPCUserRegStruct {
struct PPCElfPrstatus { struct PPCElfPrstatus {
char pad1[112]; char pad1[112];
struct PPCUserRegStruct pr_reg; struct PPCUserRegStruct pr_reg;
reg_t pad2[4]; char pad2[40];
} QEMU_PACKED; } QEMU_PACKED;

View File

@ -197,6 +197,7 @@ typedef struct PowerPCCPUClass {
int bfd_mach; int bfd_mach;
uint32_t l1_dcache_size, l1_icache_size; uint32_t l1_dcache_size, l1_icache_size;
const struct ppc_segment_page_sizes *sps; const struct ppc_segment_page_sizes *sps;
struct ppc_radix_page_info *radix_page_info;
void (*init_proc)(CPUPPCState *env); void (*init_proc)(CPUPPCState *env);
int (*check_pow)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env);
int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx);

View File

@ -943,6 +943,10 @@ struct ppc_segment_page_sizes {
struct ppc_one_seg_page_size sps[PPC_PAGE_SIZES_MAX_SZ]; 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 */ /* The whole PowerPC CPU context */
@ -1196,6 +1200,7 @@ struct PowerPCCPU {
uint32_t max_compat; uint32_t max_compat;
uint32_t compat_pvr; uint32_t compat_pvr;
PPCVirtualHypervisor *vhyp; PPCVirtualHypervisor *vhyp;
Object *intc;
/* Fields related to migration compatibility hacks */ /* Fields related to migration compatibility hacks */
bool pre_2_8_migration; bool pre_2_8_migration;

View File

@ -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) DEF_HELPER_FLAGS_1(load_purr, TCG_CALL_NO_RWG, tl, env)
#endif #endif
DEF_HELPER_2(store_sdr1, void, env, tl) 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_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_tbu, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl)

View File

@ -49,6 +49,8 @@
#if defined(TARGET_PPC64) #if defined(TARGET_PPC64)
#include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/spapr_cpu_core.h"
#endif #endif
#include "elf.h"
#include "sysemu/kvm_int.h"
//#define DEBUG_KVM //#define DEBUG_KVM
@ -73,6 +75,7 @@ static int cap_booke_sregs;
static int cap_ppc_smt; static int cap_ppc_smt;
static int cap_ppc_rma; static int cap_ppc_rma;
static int cap_spapr_tce; static int cap_spapr_tce;
static int cap_spapr_tce_64;
static int cap_spapr_multitce; static int cap_spapr_multitce;
static int cap_spapr_vfio; static int cap_spapr_vfio;
static int cap_hior; static int cap_hior;
@ -83,6 +86,8 @@ static int cap_papr;
static int cap_htab_fd; static int cap_htab_fd;
static int cap_fixup_hcalls; static int cap_fixup_hcalls;
static int cap_htm; /* Hardware transactional memory support */ 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; 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_smt = kvm_check_extension(s, KVM_CAP_PPC_SMT);
cap_ppc_rma = kvm_check_extension(s, KVM_CAP_PPC_RMA); 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 = 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_multitce = kvm_check_extension(s, KVM_CAP_SPAPR_MULTITCE);
cap_spapr_vfio = false; cap_spapr_vfio = false;
cap_one_reg = kvm_check_extension(s, KVM_CAP_ONE_REG); 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_htab_fd = kvm_check_extension(s, KVM_CAP_PPC_HTAB_FD);
cap_fixup_hcalls = kvm_check_extension(s, KVM_CAP_PPC_FIXUP_HCALL); cap_fixup_hcalls = kvm_check_extension(s, KVM_CAP_PPC_FIXUP_HCALL);
cap_htm = kvm_vm_check_extension(s, KVM_CAP_PPC_HTM); 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) { if (!cap_interrupt_level) {
fprintf(stderr, "KVM: Couldn't find level irq capability. Expect the " 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); 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) static bool kvm_valid_page_size(uint32_t flags, long rampgsize, uint32_t shift)
{ {
if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) { if (!(flags & KVM_PPC_PAGE_SIZES_REAL)) {
@ -509,8 +572,11 @@ int kvm_arch_init_vcpu(CPUState *cs)
case POWERPC_MMU_2_07: case POWERPC_MMU_2_07:
if (!cap_htm && !kvmppc_is_pr(cs->kvm_state)) { if (!cap_htm && !kvmppc_is_pr(cs->kvm_state)) {
/* KVM-HV has transactional memory on POWER8 also without the /* KVM-HV has transactional memory on POWER8 also without the
* KVM_CAP_PPC_HTM extension, so enable it here instead. */ * KVM_CAP_PPC_HTM extension, so enable it here instead as
cap_htm = true; * long as it's availble to userspace on the host. */
if (qemu_getauxval(AT_HWCAP2) & PPC_FEATURE2_HAS_HTM) {
cap_htm = true;
}
} }
break; break;
default: default:
@ -2132,13 +2198,24 @@ bool kvmppc_spapr_use_multitce(void)
return cap_spapr_multitce; return cap_spapr_multitce;
} }
void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, int kvmppc_spapr_enable_inkernel_multitce(void)
bool need_vfio) {
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; long len;
int fd; int fd;
void *table; void *table;
@ -2151,14 +2228,41 @@ void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd,
return NULL; return NULL;
} }
fd = kvm_vm_ioctl(kvm_state, KVM_CREATE_SPAPR_TCE, &args); if (cap_spapr_tce_64) {
if (fd < 0) { struct kvm_create_spapr_tce_64 args = {
fprintf(stderr, "KVM: Failed to create TCE table for liobn 0x%x\n", .liobn = 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; 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 */ /* FIXME: round this up to page size */
table = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 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) { if (icache_size != -1) {
pcc->l1_icache_size = icache_size; 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) bool kvmppc_has_cap_epr(void)
@ -2295,6 +2403,16 @@ bool kvmppc_has_cap_htm(void)
return cap_htm; 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) static PowerPCCPUClass *ppc_cpu_get_family_class(PowerPCCPUClass *pcc)
{ {
ObjectClass *oc = OBJECT_CLASS(pcc); ObjectClass *oc = OBJECT_CLASS(pcc);

View File

@ -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_or_tsr_bits(PowerPCCPU *cpu, uint32_t tsr_bits);
int kvmppc_set_tcr(PowerPCCPU *cpu); int kvmppc_set_tcr(PowerPCCPU *cpu);
int kvmppc_booke_watchdog_enable(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 #ifndef CONFIG_USER_ONLY
off_t kvmppc_alloc_rma(void **rma); off_t kvmppc_alloc_rma(void **rma);
bool kvmppc_spapr_use_multitce(void); bool kvmppc_spapr_use_multitce(void);
void *kvmppc_create_spapr_tce(uint32_t liobn, uint32_t window_size, int *pfd, int kvmppc_spapr_enable_inkernel_multitce(void);
bool need_vfio); 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_remove_spapr_tce(void *table, int pfd, uint32_t window_size);
int kvmppc_reset_htab(int shift_hint); int kvmppc_reset_htab(int shift_hint);
uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift); 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); void kvmppc_write_hpte(hwaddr ptex, uint64_t pte0, uint64_t pte1);
bool kvmppc_has_cap_fixup_hcalls(void); bool kvmppc_has_cap_fixup_hcalls(void);
bool kvmppc_has_cap_htm(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_enable_hwrng(void);
int kvmppc_put_books_sregs(PowerPCCPU *cpu); int kvmppc_put_books_sregs(PowerPCCPU *cpu);
PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void);
@ -156,6 +163,13 @@ static inline int kvmppc_booke_watchdog_enable(PowerPCCPU *cpu)
return -1; 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 #ifndef CONFIG_USER_ONLY
static inline off_t kvmppc_alloc_rma(void **rma) static inline off_t kvmppc_alloc_rma(void **rma)
{ {
@ -167,9 +181,15 @@ static inline bool kvmppc_spapr_use_multitce(void)
return false; return false;
} }
static inline void *kvmppc_create_spapr_tce(uint32_t liobn, static inline int kvmppc_spapr_enable_inkernel_multitce(void)
uint32_t window_size, int *fd, {
bool need_vfio) 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; return NULL;
} }
@ -252,6 +272,16 @@ static inline bool kvmppc_has_cap_htm(void)
return false; 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) static inline int kvmppc_enable_hwrng(void)
{ {
return -1; return -1;

View File

@ -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) void helper_store_hid0_601(CPUPPCState *env, target_ulong val)
{ {
target_ulong hid0; target_ulong hid0;

File diff suppressed because it is too large Load Diff