mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
+8
-14
@@ -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)
|
||||
+7
-18
@@ -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)
|
||||
|
||||
+194
-2
@@ -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
|
||||
|
||||
+378
-33
File diff suppressed because it is too large
Load Diff
@@ -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
-2
@@ -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;
|
||||
}
|
||||
|
||||
+95
-11
@@ -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)
|
||||
File diff suppressed because it is too large
Load Diff
+225
-150
File diff suppressed because it is too large
Load Diff
+15
-2
@@ -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)
|
||||
|
||||
+172
-2
@@ -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);
|
||||
|
||||
+6
-2
@@ -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) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user