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