Merge remote-tracking branch 'agraf/ppc-for-upstream' into staging

* agraf/ppc-for-upstream: (29 commits)
  spapr: Use DeviceClass::fw_name for device tree CPU node
  target-ppc: Fill in OpenFirmware names for some PowerPCCPU families
  target-ppc: dump-guest-memory support
  dump-guest-memory: Check for the correct return value
  target-ppc: Use #define for max slb entries
  target-ppc: Check for error on address translation in memsave command
  target-ppc: Update slb array with correct index values.
  spapr-pci: enable irqfd for INTx
  xics-kvm: enable irqfd for MSI
  xics: Implement H_XIRR_X
  xics: Implement H_IPOLL
  xics-kvm: Support for in-kernel XICS interrupt controller
  xics: add cpu_setup callback
  xics: split to xics and xics-common
  xics: add missing const specifiers to TypeInfo
  xics: convert init() to realize()
  xics: add pre_save/post_load dispatchers
  xics: replace fprintf with error_report
  spapr: move cpu_setup after kvmppc_set_papr
  xics: move reset and cpu_setup
  ...

Message-id: 1382736474-32128-1-git-send-email-agraf@suse.de
Signed-off-by: Anthony Liguori <anthony@codemonkey.ws>
This commit is contained in:
Anthony Liguori 2013-10-31 17:01:12 +01:00
commit 1ba1905abd
25 changed files with 1235 additions and 113 deletions

5
cpus.c
View file

@ -1403,7 +1403,10 @@ void qmp_memsave(int64_t addr, int64_t size, const char *filename,
l = sizeof(buf);
if (l > size)
l = size;
cpu_memory_rw_debug(cpu, addr, buf, l, 0);
if (cpu_memory_rw_debug(cpu, addr, buf, l, 0) != 0) {
error_setg(errp, "Invalid addr 0x%016" PRIx64 "specified", addr);
goto exit;
}
if (fwrite(buf, 1, l, f) != l) {
error_set(errp, QERR_IO_ERROR);
goto exit;

View file

@ -46,6 +46,7 @@ CONFIG_E500=y
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
# For pSeries
CONFIG_XICS=$(CONFIG_PSERIES)
CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
# For PReP
CONFIG_I82378=y
CONFIG_I8259=y

4
dump.c
View file

@ -66,7 +66,7 @@ typedef struct DumpState {
uint32_t sh_info;
bool have_section;
bool resume;
size_t note_size;
ssize_t note_size;
hwaddr memory_offset;
int fd;
@ -765,7 +765,7 @@ static int dump_init(DumpState *s, int fd, bool paging, bool has_filter,
s->note_size = cpu_get_note_size(s->dump_info.d_class,
s->dump_info.d_machine, nr_cpus);
if (ret < 0) {
if (s->note_size < 0) {
error_set(errp, QERR_UNSUPPORTED);
goto cleanup;
}

View file

@ -23,3 +23,4 @@ obj-$(CONFIG_OMAP) += omap_intc.o
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
obj-$(CONFIG_SH4) += sh_intc.o
obj-$(CONFIG_XICS) += xics.o
obj-$(CONFIG_XICS_KVM) += xics_kvm.o

View file

@ -27,8 +27,148 @@
#include "hw/hw.h"
#include "trace.h"
#include "qemu/timer.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
#include "qemu/error-report.h"
#include "qapi/visitor.h"
void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
ICPState *ss = &icp->ss[cs->cpu_index];
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
assert(cs->cpu_index < icp->nr_servers);
if (info->cpu_setup) {
info->cpu_setup(icp, cpu);
}
switch (PPC_INPUT(env)) {
case PPC_FLAGS_INPUT_POWER7:
ss->output = env->irq_inputs[POWER7_INPUT_INT];
break;
case PPC_FLAGS_INPUT_970:
ss->output = env->irq_inputs[PPC970_INPUT_INT];
break;
default:
error_report("XICS interrupt controller does not support this CPU "
"bus model");
abort();
}
}
/*
* XICS Common class - parent for emulated XICS and KVM-XICS
*/
static void xics_common_reset(DeviceState *d)
{
XICSState *icp = XICS_COMMON(d);
int i;
for (i = 0; i < icp->nr_servers; i++) {
device_reset(DEVICE(&icp->ss[i]));
}
device_reset(DEVICE(icp->ics));
}
static void xics_prop_get_nr_irqs(Object *obj, Visitor *v,
void *opaque, const char *name, Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
int64_t value = icp->nr_irqs;
visit_type_int(v, &value, name, errp);
}
static void xics_prop_set_nr_irqs(Object *obj, Visitor *v,
void *opaque, const char *name, Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
Error *error = NULL;
int64_t value;
visit_type_int(v, &value, name, &error);
if (error) {
error_propagate(errp, error);
return;
}
if (icp->nr_irqs) {
error_setg(errp, "Number of interrupts is already set to %u",
icp->nr_irqs);
return;
}
assert(info->set_nr_irqs);
assert(icp->ics);
info->set_nr_irqs(icp, value, errp);
}
static void xics_prop_get_nr_servers(Object *obj, Visitor *v,
void *opaque, const char *name,
Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
int64_t value = icp->nr_servers;
visit_type_int(v, &value, name, errp);
}
static void xics_prop_set_nr_servers(Object *obj, Visitor *v,
void *opaque, const char *name,
Error **errp)
{
XICSState *icp = XICS_COMMON(obj);
XICSStateClass *info = XICS_COMMON_GET_CLASS(icp);
Error *error = NULL;
int64_t value;
visit_type_int(v, &value, name, &error);
if (error) {
error_propagate(errp, error);
return;
}
if (icp->nr_servers) {
error_setg(errp, "Number of servers is already set to %u",
icp->nr_servers);
return;
}
assert(info->set_nr_servers);
info->set_nr_servers(icp, value, errp);
}
static void xics_common_initfn(Object *obj)
{
object_property_add(obj, "nr_irqs", "int",
xics_prop_get_nr_irqs, xics_prop_set_nr_irqs,
NULL, NULL, NULL);
object_property_add(obj, "nr_servers", "int",
xics_prop_get_nr_servers, xics_prop_set_nr_servers,
NULL, NULL, NULL);
}
static void xics_common_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
dc->reset = xics_common_reset;
}
static const TypeInfo xics_common_info = {
.name = TYPE_XICS_COMMON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(XICSState),
.class_size = sizeof(XICSStateClass),
.instance_init = xics_common_initfn,
.class_init = xics_common_class_init,
};
/*
* ICP: Presentation layer
@ -153,11 +293,35 @@ static void icp_irq(XICSState *icp, int server, int nr, uint8_t priority)
}
}
static void icp_dispatch_pre_save(void *opaque)
{
ICPState *ss = opaque;
ICPStateClass *info = ICP_GET_CLASS(ss);
if (info->pre_save) {
info->pre_save(ss);
}
}
static int icp_dispatch_post_load(void *opaque, int version_id)
{
ICPState *ss = opaque;
ICPStateClass *info = ICP_GET_CLASS(ss);
if (info->post_load) {
return info->post_load(ss, version_id);
}
return 0;
}
static const VMStateDescription vmstate_icp_server = {
.name = "icp/server",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.pre_save = icp_dispatch_pre_save,
.post_load = icp_dispatch_post_load,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32(xirr, ICPState),
@ -187,11 +351,12 @@ static void icp_class_init(ObjectClass *klass, void *data)
dc->vmsd = &vmstate_icp_server;
}
static TypeInfo icp_info = {
static const TypeInfo icp_info = {
.name = TYPE_ICP,
.parent = TYPE_DEVICE,
.instance_size = sizeof(ICPState),
.class_init = icp_class_init,
.class_size = sizeof(ICPStateClass),
};
/*
@ -353,10 +518,9 @@ static void ics_reset(DeviceState *dev)
}
}
static int ics_post_load(void *opaque, int version_id)
static int ics_post_load(ICSState *ics, int version_id)
{
int i;
ICSState *ics = opaque;
for (i = 0; i < ics->icp->nr_servers; i++) {
icp_resend(ics->icp, i);
@ -365,6 +529,28 @@ static int ics_post_load(void *opaque, int version_id)
return 0;
}
static void ics_dispatch_pre_save(void *opaque)
{
ICSState *ics = opaque;
ICSStateClass *info = ICS_GET_CLASS(ics);
if (info->pre_save) {
info->pre_save(ics);
}
}
static int ics_dispatch_post_load(void *opaque, int version_id)
{
ICSState *ics = opaque;
ICSStateClass *info = ICS_GET_CLASS(ics);
if (info->post_load) {
return info->post_load(ics, version_id);
}
return 0;
}
static const VMStateDescription vmstate_ics_irq = {
.name = "ics/irq",
.version_id = 1,
@ -384,7 +570,8 @@ static const VMStateDescription vmstate_ics = {
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.post_load = ics_post_load,
.pre_save = ics_dispatch_pre_save,
.post_load = ics_dispatch_post_load,
.fields = (VMStateField []) {
/* Sanity check */
VMSTATE_UINT32_EQUAL(nr_irqs, ICSState),
@ -395,31 +582,44 @@ static const VMStateDescription vmstate_ics = {
},
};
static int ics_realize(DeviceState *dev)
static void ics_initfn(Object *obj)
{
ICSState *ics = ICS(obj);
ics->offset = XICS_IRQ_BASE;
}
static void ics_realize(DeviceState *dev, Error **errp)
{
ICSState *ics = ICS(dev);
if (!ics->nr_irqs) {
error_setg(errp, "Number of interrupts needs to be greater 0");
return;
}
ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool));
ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs);
return 0;
}
static void ics_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ICSStateClass *isc = ICS_CLASS(klass);
dc->init = ics_realize;
dc->realize = ics_realize;
dc->vmsd = &vmstate_ics;
dc->reset = ics_reset;
isc->post_load = ics_post_load;
}
static TypeInfo ics_info = {
static const TypeInfo ics_info = {
.name = TYPE_ICS,
.parent = TYPE_DEVICE,
.instance_size = sizeof(ICSState),
.class_init = ics_class_init,
.class_size = sizeof(ICSStateClass),
.instance_init = ics_initfn,
};
/*
@ -480,6 +680,18 @@ static target_ulong h_xirr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
ICPState *ss = &spapr->icp->ss[cs->cpu_index];
uint32_t xirr = icp_accept(ss);
args[0] = xirr;
args[1] = cpu_get_real_ticks();
return H_SUCCESS;
}
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
@ -490,6 +702,18 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
return H_SUCCESS;
}
static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
CPUState *cs = CPU(cpu);
ICPState *ss = &spapr->icp->ss[cs->cpu_index];
args[0] = ss->xirr;
args[1] = ss->mfrr;
return H_SUCCESS;
}
static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
@ -600,48 +824,39 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
* XICS
*/
static void xics_reset(DeviceState *d)
static void xics_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
{
XICSState *icp = XICS(d);
int i;
for (i = 0; i < icp->nr_servers; i++) {
device_reset(DEVICE(&icp->ss[i]));
}
device_reset(DEVICE(icp->ics));
icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
}
void xics_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
static void xics_set_nr_servers(XICSState *icp, uint32_t nr_servers,
Error **errp)
{
CPUState *cs = CPU(cpu);
CPUPPCState *env = &cpu->env;
ICPState *ss = &icp->ss[cs->cpu_index];
int i;
assert(cs->cpu_index < icp->nr_servers);
icp->nr_servers = nr_servers;
switch (PPC_INPUT(env)) {
case PPC_FLAGS_INPUT_POWER7:
ss->output = env->irq_inputs[POWER7_INPUT_INT];
break;
case PPC_FLAGS_INPUT_970:
ss->output = env->irq_inputs[PPC970_INPUT_INT];
break;
default:
fprintf(stderr, "XICS interrupt controller does not support this CPU "
"bus model\n");
abort();
icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
for (i = 0; i < icp->nr_servers; i++) {
char buffer[32];
object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
errp);
}
}
static void xics_realize(DeviceState *dev, Error **errp)
{
XICSState *icp = XICS(dev);
ICSState *ics = icp->ics;
Error *error = NULL;
int i;
if (!icp->nr_servers) {
error_setg(errp, "Number of servers needs to be greater 0");
return;
}
/* Registration of global state belongs into realize */
spapr_rtas_register("ibm,set-xive", rtas_set_xive);
spapr_rtas_register("ibm,get-xive", rtas_get_xive);
@ -651,20 +866,22 @@ static void xics_realize(DeviceState *dev, Error **errp)
spapr_register_hypercall(H_CPPR, h_cppr);
spapr_register_hypercall(H_IPI, h_ipi);
spapr_register_hypercall(H_XIRR, h_xirr);
spapr_register_hypercall(H_XIRR_X, h_xirr_x);
spapr_register_hypercall(H_EOI, h_eoi);
spapr_register_hypercall(H_IPOLL, h_ipoll);
ics->nr_irqs = icp->nr_irqs;
ics->offset = XICS_IRQ_BASE;
ics->icp = icp;
qdev_init_nofail(DEVICE(ics));
object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
if (error) {
error_propagate(errp, error);
return;
}
icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
for (i = 0; i < icp->nr_servers; i++) {
char buffer[32];
object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL);
qdev_init_nofail(DEVICE(&icp->ss[i]));
object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
if (error) {
error_propagate(errp, error);
return;
}
}
}
@ -674,33 +891,31 @@ static void xics_initfn(Object *obj)
xics->ics = ICS(object_new(TYPE_ICS));
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
xics->ics->icp = xics;
}
static Property xics_properties[] = {
DEFINE_PROP_UINT32("nr_servers", XICSState, nr_servers, -1),
DEFINE_PROP_UINT32("nr_irqs", XICSState, nr_irqs, -1),
DEFINE_PROP_END_OF_LIST(),
};
static void xics_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
XICSStateClass *xsc = XICS_CLASS(oc);
dc->realize = xics_realize;
dc->props = xics_properties;
dc->reset = xics_reset;
xsc->set_nr_irqs = xics_set_nr_irqs;
xsc->set_nr_servers = xics_set_nr_servers;
}
static const TypeInfo xics_info = {
.name = TYPE_XICS,
.parent = TYPE_SYS_BUS_DEVICE,
.parent = TYPE_XICS_COMMON,
.instance_size = sizeof(XICSState),
.class_size = sizeof(XICSStateClass),
.class_init = xics_class_init,
.instance_init = xics_initfn,
};
static void xics_register_types(void)
{
type_register_static(&xics_common_info);
type_register_static(&xics_info);
type_register_static(&ics_info);
type_register_static(&icp_info);

494
hw/intc/xics_kvm.c Normal file
View file

@ -0,0 +1,494 @@
/*
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
*
* PAPR Virtualized Interrupt System, aka ICS/ICP aka xics, in-kernel emulation
*
* Copyright (c) 2013 David Gibson, IBM Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "hw/hw.h"
#include "trace.h"
#include "hw/ppc/spapr.h"
#include "hw/ppc/xics.h"
#include "kvm_ppc.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include <sys/ioctl.h>
typedef struct KVMXICSState {
XICSState parent_obj;
uint32_t set_xive_token;
uint32_t get_xive_token;
uint32_t int_off_token;
uint32_t int_on_token;
int kernel_xics_fd;
} KVMXICSState;
/*
* ICP-KVM
*/
static void icp_get_kvm_state(ICPState *ss)
{
uint64_t state;
struct kvm_one_reg reg = {
.id = KVM_REG_PPC_ICP_STATE,
.addr = (uintptr_t)&state,
};
int ret;
/* ICP for this CPU thread is not in use, exiting */
if (!ss->cs) {
return;
}
ret = kvm_vcpu_ioctl(ss->cs, KVM_GET_ONE_REG, &reg);
if (ret != 0) {
error_report("Unable to retrieve KVM interrupt controller state"
" for CPU %d: %s", ss->cs->cpu_index, strerror(errno));
exit(1);
}
ss->xirr = state >> KVM_REG_PPC_ICP_XISR_SHIFT;
ss->mfrr = (state >> KVM_REG_PPC_ICP_MFRR_SHIFT)
& KVM_REG_PPC_ICP_MFRR_MASK;
ss->pending_priority = (state >> KVM_REG_PPC_ICP_PPRI_SHIFT)
& KVM_REG_PPC_ICP_PPRI_MASK;
}
static int icp_set_kvm_state(ICPState *ss, int version_id)
{
uint64_t state;
struct kvm_one_reg reg = {
.id = KVM_REG_PPC_ICP_STATE,
.addr = (uintptr_t)&state,
};
int ret;
/* ICP for this CPU thread is not in use, exiting */
if (!ss->cs) {
return 0;
}
state = ((uint64_t)ss->xirr << KVM_REG_PPC_ICP_XISR_SHIFT)
| ((uint64_t)ss->mfrr << KVM_REG_PPC_ICP_MFRR_SHIFT)
| ((uint64_t)ss->pending_priority << KVM_REG_PPC_ICP_PPRI_SHIFT);
ret = kvm_vcpu_ioctl(ss->cs, KVM_SET_ONE_REG, &reg);
if (ret != 0) {
error_report("Unable to restore KVM interrupt controller state (0x%"
PRIx64 ") for CPU %d: %s", state, ss->cs->cpu_index,
strerror(errno));
return ret;
}
return 0;
}
static void icp_kvm_reset(DeviceState *dev)
{
ICPState *icp = ICP(dev);
icp->xirr = 0;
icp->pending_priority = 0xff;
icp->mfrr = 0xff;
/* Make all outputs are deasserted */
qemu_set_irq(icp->output, 0);
icp_set_kvm_state(icp, 1);
}
static void icp_kvm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ICPStateClass *icpc = ICP_CLASS(klass);
dc->reset = icp_kvm_reset;
icpc->pre_save = icp_get_kvm_state;
icpc->post_load = icp_set_kvm_state;
}
static const TypeInfo icp_kvm_info = {
.name = TYPE_KVM_ICP,
.parent = TYPE_ICP,
.instance_size = sizeof(ICPState),
.class_init = icp_kvm_class_init,
.class_size = sizeof(ICPStateClass),
};
/*
* ICS-KVM
*/
static void ics_get_kvm_state(ICSState *ics)
{
KVMXICSState *icpkvm = KVM_XICS(ics->icp);
uint64_t state;
struct kvm_device_attr attr = {
.flags = 0,
.group = KVM_DEV_XICS_GRP_SOURCES,
.addr = (uint64_t)(uintptr_t)&state,
};
int i;
for (i = 0; i < ics->nr_irqs; i++) {
ICSIRQState *irq = &ics->irqs[i];
int ret;
attr.attr = i + ics->offset;
ret = ioctl(icpkvm->kernel_xics_fd, KVM_GET_DEVICE_ATTR, &attr);
if (ret != 0) {
error_report("Unable to retrieve KVM interrupt controller state"
" for IRQ %d: %s", i + ics->offset, strerror(errno));
exit(1);
}
irq->server = state & KVM_XICS_DESTINATION_MASK;
irq->saved_priority = (state >> KVM_XICS_PRIORITY_SHIFT)
& KVM_XICS_PRIORITY_MASK;
/*
* To be consistent with the software emulation in xics.c, we
* split out the masked state + priority that we get from the
* kernel into 'current priority' (0xff if masked) and
* 'saved priority' (if masked, this is the priority the
* interrupt had before it was masked). Masking and unmasking
* are done with the ibm,int-off and ibm,int-on RTAS calls.
*/
if (state & KVM_XICS_MASKED) {
irq->priority = 0xff;
} else {
irq->priority = irq->saved_priority;
}
if (state & KVM_XICS_PENDING) {
if (state & KVM_XICS_LEVEL_SENSITIVE) {
irq->status |= XICS_STATUS_ASSERTED;
} else {
/*
* A pending edge-triggered interrupt (or MSI)
* must have been rejected previously when we
* first detected it and tried to deliver it,
* so mark it as pending and previously rejected
* for consistency with how xics.c works.
*/
irq->status |= XICS_STATUS_MASKED_PENDING
| XICS_STATUS_REJECTED;
}
}
}
}
static int ics_set_kvm_state(ICSState *ics, int version_id)
{
KVMXICSState *icpkvm = KVM_XICS(ics->icp);
uint64_t state;
struct kvm_device_attr attr = {
.flags = 0,
.group = KVM_DEV_XICS_GRP_SOURCES,
.addr = (uint64_t)(uintptr_t)&state,
};
int i;
for (i = 0; i < ics->nr_irqs; i++) {
ICSIRQState *irq = &ics->irqs[i];
int ret;
attr.attr = i + ics->offset;
state = irq->server;
state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK)
<< KVM_XICS_PRIORITY_SHIFT;
if (irq->priority != irq->saved_priority) {
assert(irq->priority == 0xff);
state |= KVM_XICS_MASKED;
}
if (ics->islsi[i]) {
state |= KVM_XICS_LEVEL_SENSITIVE;
if (irq->status & XICS_STATUS_ASSERTED) {
state |= KVM_XICS_PENDING;
}
} else {
if (irq->status & XICS_STATUS_MASKED_PENDING) {
state |= KVM_XICS_PENDING;
}
}
ret = ioctl(icpkvm->kernel_xics_fd, KVM_SET_DEVICE_ATTR, &attr);
if (ret != 0) {
error_report("Unable to restore KVM interrupt controller state"
" for IRQs %d: %s", i + ics->offset, strerror(errno));
return ret;
}
}
return 0;
}
static void ics_kvm_set_irq(void *opaque, int srcno, int val)
{
ICSState *ics = opaque;
struct kvm_irq_level args;
int rc;
args.irq = srcno + ics->offset;
if (!ics->islsi[srcno]) {
if (!val) {
return;
}
args.level = KVM_INTERRUPT_SET;
} else {
args.level = val ? KVM_INTERRUPT_SET_LEVEL : KVM_INTERRUPT_UNSET;
}
rc = kvm_vm_ioctl(kvm_state, KVM_IRQ_LINE, &args);
if (rc < 0) {
perror("kvm_irq_line");
}
}
static void ics_kvm_reset(DeviceState *dev)
{
ics_set_kvm_state(ICS(dev), 1);
}
static void ics_kvm_realize(DeviceState *dev, Error **errp)
{
ICSState *ics = ICS(dev);
if (!ics->nr_irqs) {
error_setg(errp, "Number of interrupts needs to be greater 0");
return;
}
ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool));
ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs);
}
static void ics_kvm_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ICSStateClass *icsc = ICS_CLASS(klass);
dc->realize = ics_kvm_realize;
dc->reset = ics_kvm_reset;
icsc->pre_save = ics_get_kvm_state;
icsc->post_load = ics_set_kvm_state;
}
static const TypeInfo ics_kvm_info = {
.name = TYPE_KVM_ICS,
.parent = TYPE_ICS,
.instance_size = sizeof(ICSState),
.class_init = ics_kvm_class_init,
};
/*
* XICS-KVM
*/
static void xics_kvm_cpu_setup(XICSState *icp, PowerPCCPU *cpu)
{
CPUState *cs;
ICPState *ss;
KVMXICSState *icpkvm = KVM_XICS(icp);
cs = CPU(cpu);
ss = &icp->ss[cs->cpu_index];
assert(cs->cpu_index < icp->nr_servers);
if (icpkvm->kernel_xics_fd == -1) {
abort();
}
if (icpkvm->kernel_xics_fd != -1) {
int ret;
struct kvm_enable_cap xics_enable_cap = {
.cap = KVM_CAP_IRQ_XICS,
.flags = 0,
.args = {icpkvm->kernel_xics_fd, cs->cpu_index, 0, 0},
};
ss->cs = cs;
ret = kvm_vcpu_ioctl(ss->cs, KVM_ENABLE_CAP, &xics_enable_cap);
if (ret < 0) {
error_report("Unable to connect CPU%d to kernel XICS: %s",
cs->cpu_index, strerror(errno));
exit(1);
}
}
}
static void xics_kvm_set_nr_irqs(XICSState *icp, uint32_t nr_irqs, Error **errp)
{
icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
}
static void xics_kvm_set_nr_servers(XICSState *icp, uint32_t nr_servers,
Error **errp)
{
int i;
icp->nr_servers = nr_servers;
icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
for (i = 0; i < icp->nr_servers; i++) {
char buffer[32];
object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_KVM_ICP);
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
errp);
}
}
static void rtas_dummy(PowerPCCPU *cpu, sPAPREnvironment *spapr,
uint32_t token,
uint32_t nargs, target_ulong args,
uint32_t nret, target_ulong rets)
{
error_report("pseries: %s must never be called for in-kernel XICS",
__func__);
}
static void xics_kvm_realize(DeviceState *dev, Error **errp)
{
KVMXICSState *icpkvm = KVM_XICS(dev);
XICSState *icp = XICS_COMMON(dev);
int i, rc;
Error *error = NULL;
struct kvm_create_device xics_create_device = {
.type = KVM_DEV_TYPE_XICS,
.flags = 0,
};
if (!kvm_enabled() || !kvm_check_extension(kvm_state, KVM_CAP_IRQ_XICS)) {
error_setg(errp,
"KVM and IRQ_XICS capability must be present for in-kernel XICS");
goto fail;
}
icpkvm->set_xive_token = spapr_rtas_register("ibm,set-xive", rtas_dummy);
icpkvm->get_xive_token = spapr_rtas_register("ibm,get-xive", rtas_dummy);
icpkvm->int_off_token = spapr_rtas_register("ibm,int-off", rtas_dummy);
icpkvm->int_on_token = spapr_rtas_register("ibm,int-on", rtas_dummy);
rc = kvmppc_define_rtas_kernel_token(icpkvm->set_xive_token,
"ibm,set-xive");
if (rc < 0) {
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,set-xive");
goto fail;
}
rc = kvmppc_define_rtas_kernel_token(icpkvm->get_xive_token,
"ibm,get-xive");
if (rc < 0) {
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,get-xive");
goto fail;
}
rc = kvmppc_define_rtas_kernel_token(icpkvm->int_on_token, "ibm,int-on");
if (rc < 0) {
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-on");
goto fail;
}
rc = kvmppc_define_rtas_kernel_token(icpkvm->int_off_token, "ibm,int-off");
if (rc < 0) {
error_setg(errp, "kvmppc_define_rtas_kernel_token: ibm,int-off");
goto fail;
}
/* Create the kernel ICP */
rc = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &xics_create_device);
if (rc < 0) {
error_setg_errno(errp, -rc, "Error on KVM_CREATE_DEVICE for XICS");
goto fail;
}
icpkvm->kernel_xics_fd = xics_create_device.fd;
object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
if (error) {
error_propagate(errp, error);
goto fail;
}
assert(icp->nr_servers);
for (i = 0; i < icp->nr_servers; i++) {
object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
if (error) {
error_propagate(errp, error);
goto fail;
}
}
kvm_kernel_irqchip = true;
kvm_irqfds_allowed = true;
kvm_msi_via_irqfd_allowed = true;
kvm_gsi_direct_mapping = true;
return;
fail:
kvmppc_define_rtas_kernel_token(0, "ibm,set-xive");
kvmppc_define_rtas_kernel_token(0, "ibm,get-xive");
kvmppc_define_rtas_kernel_token(0, "ibm,int-on");
kvmppc_define_rtas_kernel_token(0, "ibm,int-off");
}
static void xics_kvm_initfn(Object *obj)
{
XICSState *xics = XICS_COMMON(obj);
xics->ics = ICS(object_new(TYPE_KVM_ICS));
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
xics->ics->icp = xics;
}
static void xics_kvm_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
XICSStateClass *xsc = XICS_COMMON_CLASS(oc);
dc->realize = xics_kvm_realize;
xsc->cpu_setup = xics_kvm_cpu_setup;
xsc->set_nr_irqs = xics_kvm_set_nr_irqs;
xsc->set_nr_servers = xics_kvm_set_nr_servers;
}
static const TypeInfo xics_kvm_info = {
.name = TYPE_KVM_XICS,
.parent = TYPE_XICS_COMMON,
.instance_size = sizeof(KVMXICSState),
.class_init = xics_kvm_class_init,
.instance_init = xics_kvm_initfn,
};
static void xics_kvm_register_types(void)
{
type_register_static(&xics_kvm_info);
type_register_static(&ics_kvm_info);
type_register_static(&icp_kvm_info);
}
type_init(xics_kvm_register_types)

View file

@ -62,7 +62,7 @@
*
* We load our kernel at 4M, leaving space for SLOF initial image
*/
#define FDT_MAX_SIZE 0x10000
#define FDT_MAX_SIZE 0x40000
#define RTAS_MAX_SIZE 0x10000
#define FW_MAX_SIZE 0x400000
#define FW_FILE_NAME "slof.bin"
@ -161,14 +161,33 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
return NULL;
}
return XICS(dev);
return XICS_COMMON(dev);
}
static XICSState *xics_system_init(int nr_servers, int nr_irqs)
{
XICSState *icp = NULL;
icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs);
if (kvm_enabled()) {
QemuOpts *machine_opts = qemu_get_machine_opts();
bool irqchip_allowed = qemu_opt_get_bool(machine_opts,
"kernel_irqchip", true);
bool irqchip_required = qemu_opt_get_bool(machine_opts,
"kernel_irqchip", false);
if (irqchip_allowed) {
icp = try_create_xics(TYPE_KVM_XICS, nr_servers, nr_irqs);
}
if (irqchip_required && !icp) {
perror("Failed to create in-kernel XICS\n");
abort();
}
}
if (!icp) {
icp = try_create_xics(TYPE_XICS, nr_servers, nr_irqs);
}
if (!icp) {
perror("Failed to create XICS\n");
abort();
@ -185,9 +204,8 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
int smt = kvmppc_smt_threads();
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
assert(spapr->cpu_model);
CPU_FOREACH(cpu) {
DeviceClass *dc = DEVICE_GET_CLASS(cpu);
uint32_t associativity[] = {cpu_to_be32(0x5),
cpu_to_be32(0x0),
cpu_to_be32(0x0),
@ -199,7 +217,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
continue;
}
snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model,
snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name,
cpu->cpu_index);
offset = fdt_path_offset(fdt, cpu_model);
@ -269,10 +287,10 @@ static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
} while (0)
static void *spapr_create_fdt_skel(const char *cpu_model,
hwaddr initrd_base,
static void *spapr_create_fdt_skel(hwaddr initrd_base,
hwaddr initrd_size,
hwaddr kernel_size,
bool little_endian,
const char *boot_device,
const char *kernel_cmdline,
uint32_t epow_irq)
@ -286,7 +304,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
char qemu_hypertas_prop[] = "hcall-memop1";
uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
char *modelname;
int i, smt = kvmppc_smt_threads();
unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
@ -326,6 +343,9 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
cpu_to_be64(kernel_size) };
_FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop))));
if (little_endian) {
_FDT((fdt_property(fdt, "qemu,boot-kernel-le", NULL, 0)));
}
}
if (boot_device) {
_FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
@ -342,18 +362,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
_FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
modelname = g_strdup(cpu_model);
for (i = 0; i < strlen(modelname); i++) {
modelname[i] = toupper(modelname[i]);
}
/* This is needed during FDT finalization */
spapr->cpu_model = g_strdup(modelname);
CPU_FOREACH(cs) {
PowerPCCPU *cpu = POWERPC_CPU(cs);
CPUPPCState *env = &cpu->env;
DeviceClass *dc = DEVICE_GET_CLASS(cs);
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
int index = cs->cpu_index;
uint32_t servers_prop[smp_threads];
@ -370,7 +382,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
continue;
}
nodename = g_strdup_printf("%s@%x", modelname, index);
nodename = g_strdup_printf("%s@%x", dc->fw_name, index);
_FDT((fdt_begin_node(fdt, nodename)));
@ -418,6 +430,10 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
gservers_prop, sizeof(gservers_prop))));
if (env->spr_cb[SPR_PURR].oea_read) {
_FDT((fdt_property(fdt, "ibm,purr", NULL, 0)));
}
if (env->mmu_model & POWERPC_MMU_1TSEG) {
_FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
segs, sizeof(segs))));
@ -450,8 +466,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
_FDT((fdt_end_node(fdt)));
}
g_free(modelname);
_FDT((fdt_end_node(fdt)));
/* RTAS */
@ -1102,6 +1116,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
uint32_t initrd_base = 0;
long kernel_size = 0, initrd_size = 0;
long load_limit, rtas_limit, fw_size;
bool kernel_le = false;
char *filename;
msi_supported = true;
@ -1175,8 +1190,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
}
env = &cpu->env;
xics_cpu_setup(spapr->icp, cpu);
/* Set time-base frequency to 512 MHz */
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
@ -1190,6 +1203,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
kvmppc_set_papr(cpu);
}
xics_cpu_setup(spapr->icp, cpu);
qemu_register_reset(spapr_cpu_reset, cpu);
}
@ -1281,6 +1296,12 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
if (kernel_size < 0) {
kernel_size = load_elf(kernel_filename,
translate_kernel_address, NULL,
NULL, &lowaddr, NULL, 0, ELF_MACHINE, 0);
kernel_le = kernel_size > 0;
}
if (kernel_size < 0) {
kernel_size = load_image_targphys(kernel_filename,
KERNEL_LOAD_ADDR,
@ -1329,9 +1350,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
&savevm_htab_handlers, spapr);
/* Prepare the device tree */
spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
initrd_base, initrd_size,
kernel_size,
spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
kernel_size, kernel_le,
boot_device, kernel_cmdline,
spapr->epow_irq);
assert(spapr->fdt_skel != NULL);

View file

@ -521,9 +521,9 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr,
target_ulong opcode, target_ulong *args)
{
target_ulong rtas_r3 = args[0];
uint32_t token = ldl_be_phys(rtas_r3);
uint32_t nargs = ldl_be_phys(rtas_r3 + 4);
uint32_t nret = ldl_be_phys(rtas_r3 + 8);
uint32_t token = rtas_ld(rtas_r3, 0);
uint32_t nargs = rtas_ld(rtas_r3, 1);
uint32_t nret = rtas_ld(rtas_r3, 2);
return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12,
nret, rtas_r3 + 12 + 4*nargs);

View file

@ -432,6 +432,17 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level)
qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level);
}
static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin)
{
sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque);
PCIINTxRoute route;
route.mode = PCI_INTX_ENABLED;
route.irq = sphb->lsi_table[pin].irq;
return route;
}
/*
* MSI/MSIX memory region implementation.
* The handler handles both MSI and MSIX.
@ -610,6 +621,8 @@ static int spapr_phb_init(SysBusDevice *s)
pci_setup_iommu(bus, spapr_pci_dma_iommu, sphb);
pci_bus_set_route_irq_fn(bus, spapr_route_intx_pin_to_irq);
QLIST_INSERT_HEAD(&spapr->phbs, sphb, list);
/* Initialize the LSI table */

View file

@ -1359,6 +1359,9 @@ typedef struct elf64_shdr {
#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */
#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
#define NT_S390_TIMER 0x301 /* s390 timer register */
#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */
#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */
#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */
/* Note header in a PT_NOTE section */

View file

@ -29,7 +29,6 @@ typedef struct sPAPREnvironment {
target_ulong entry_point;
uint32_t next_irq;
uint64_t rtc_offset;
char *cpu_model;
bool has_graphics;
uint32_t epow_irq;
@ -283,6 +282,7 @@ typedef struct sPAPREnvironment {
#define H_GET_EM_PARMS 0x2B8
#define H_SET_MPP 0x2D0
#define H_GET_MPP 0x2D4
#define H_XIRR_X 0x2FC
#define H_SET_MODE 0x31C
#define MAX_HCALL_OPCODE H_SET_MODE
@ -332,14 +332,19 @@ static inline int spapr_allocate_lsi(int hint)
return spapr_allocate_irq(hint, true);
}
static inline uint64_t ppc64_phys_to_real(uint64_t addr)
{
return addr & ~0xF000000000000000ULL;
}
static inline uint32_t rtas_ld(target_ulong phys, int n)
{
return ldl_be_phys(phys + 4*n);
return ldl_be_phys(ppc64_phys_to_real(phys + 4*n));
}
static inline void rtas_st(target_ulong phys, int n, uint32_t val)
{
stl_be_phys(phys + 4*n, val);
stl_be_phys(ppc64_phys_to_real(phys + 4*n), val);
}
typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr,

View file

@ -29,9 +29,24 @@
#include "hw/sysbus.h"
#define TYPE_XICS_COMMON "xics-common"
#define XICS_COMMON(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS_COMMON)
#define TYPE_XICS "xics"
#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_XICS)
#define TYPE_KVM_XICS "xics-kvm"
#define KVM_XICS(obj) OBJECT_CHECK(KVMXICSState, (obj), TYPE_KVM_XICS)
#define XICS_COMMON_CLASS(klass) \
OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS_COMMON)
#define XICS_CLASS(klass) \
OBJECT_CLASS_CHECK(XICSStateClass, (klass), TYPE_XICS)
#define XICS_COMMON_GET_CLASS(obj) \
OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS_COMMON)
#define XICS_GET_CLASS(obj) \
OBJECT_GET_CLASS(XICSStateClass, (obj), TYPE_XICS)
#define XICS_IPI 0x2
#define XICS_BUID 0x1
#define XICS_IRQ_BASE (XICS_BUID << 12)
@ -41,11 +56,22 @@
* (the kernel implementation supports more but we don't exploit
* that yet)
*/
typedef struct XICSStateClass XICSStateClass;
typedef struct XICSState XICSState;
typedef struct ICPStateClass ICPStateClass;
typedef struct ICPState ICPState;
typedef struct ICSStateClass ICSStateClass;
typedef struct ICSState ICSState;
typedef struct ICSIRQState ICSIRQState;
struct XICSStateClass {
DeviceClass parent_class;
void (*cpu_setup)(XICSState *icp, PowerPCCPU *cpu);
void (*set_nr_irqs)(XICSState *icp, uint32_t nr_irqs, Error **errp);
void (*set_nr_servers)(XICSState *icp, uint32_t nr_servers, Error **errp);
};
struct XICSState {
/*< private >*/
SysBusDevice parent_obj;
@ -59,10 +85,26 @@ struct XICSState {
#define TYPE_ICP "icp"
#define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP)
#define TYPE_KVM_ICP "icp-kvm"
#define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP)
#define ICP_CLASS(klass) \
OBJECT_CLASS_CHECK(ICPStateClass, (klass), TYPE_ICP)
#define ICP_GET_CLASS(obj) \
OBJECT_GET_CLASS(ICPStateClass, (obj), TYPE_ICP)
struct ICPStateClass {
DeviceClass parent_class;
void (*pre_save)(ICPState *s);
int (*post_load)(ICPState *s, int version_id);
};
struct ICPState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
CPUState *cs;
uint32_t xirr;
uint8_t pending_priority;
uint8_t mfrr;
@ -72,6 +114,21 @@ struct ICPState {
#define TYPE_ICS "ics"
#define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS)
#define TYPE_KVM_ICS "icskvm"
#define KVM_ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_KVM_ICS)
#define ICS_CLASS(klass) \
OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS)
#define ICS_GET_CLASS(obj) \
OBJECT_GET_CLASS(ICSStateClass, (obj), TYPE_ICS)
struct ICSStateClass {
DeviceClass parent_class;
void (*pre_save)(ICSState *s);
int (*post_load)(ICSState *s, int version_id);
};
struct ICSState {
/*< private >*/
DeviceState parent_obj;

View file

@ -3180,6 +3180,9 @@ static const MonitorDef monitor_defs[] = {
{ "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) },
{ "srr1", offsetof(CPUPPCState, spr[SPR_SRR1]) },
{ "dar", offsetof(CPUPPCState, spr[SPR_DAR]) },
{ "dsisr", offsetof(CPUPPCState, spr[SPR_DSISR]) },
{ "cfar", offsetof(CPUPPCState, spr[SPR_CFAR]) },
{ "sprg0", offsetof(CPUPPCState, spr[SPR_SPRG0]) },
{ "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) },
{ "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) },

View file

@ -17,7 +17,7 @@
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
implementation for certain IBM POWER hardware. The sources are at
https://github.com/aik/SLOF, and the image currently in qemu is
built from git tag qemu-slof-20130430.
built from git tag qemu-slof-20130827.
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
legacy x86 software to communicate with an attached serial console as

Binary file not shown.

@ -1 +1 @@
Subproject commit 8cfdfc43f4c4c8c8dfa4b7cf16f7c19c84eee812
Subproject commit a523d1b0cd6e96cf5e393f0a10f897e8ed639fdc

View file

@ -2,7 +2,7 @@ obj-y += cpu-models.o
obj-y += translate.o
ifeq ($(CONFIG_SOFTMMU),y)
obj-y += machine.o mmu_helper.o mmu-hash32.o
obj-$(TARGET_PPC64) += mmu-hash64.o
obj-$(TARGET_PPC64) += mmu-hash64.o arch_dump.o
endif
obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o

253
target-ppc/arch_dump.c Normal file
View file

@ -0,0 +1,253 @@
/*
* writing ELF notes for ppc64 arch
*
*
* Copyright IBM, Corp. 2013
*
* Authors:
* Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*
*/
#include "cpu.h"
#include "elf.h"
#include "exec/cpu-all.h"
#include "sysemu/dump.h"
#include "sysemu/kvm.h"
struct PPC64UserRegStruct {
uint64_t gpr[32];
uint64_t nip;
uint64_t msr;
uint64_t orig_gpr3;
uint64_t ctr;
uint64_t link;
uint64_t xer;
uint64_t ccr;
uint64_t softe;
uint64_t trap;
uint64_t dar;
uint64_t dsisr;
uint64_t result;
} QEMU_PACKED;
struct PPC64ElfPrstatus {
char pad1[112];
struct PPC64UserRegStruct pr_reg;
uint64_t pad2[4];
} QEMU_PACKED;
struct PPC64ElfFpregset {
uint64_t fpr[32];
uint64_t fpscr;
} QEMU_PACKED;
struct PPC64ElfVmxregset {
ppc_avr_t avr[32];
ppc_avr_t vscr;
union {
ppc_avr_t unused;
uint32_t value;
} vrsave;
} QEMU_PACKED;
struct PPC64ElfVsxregset {
uint64_t vsr[32];
} QEMU_PACKED;
struct PPC64ElfSperegset {
uint32_t evr[32];
uint64_t spe_acc;
uint32_t spe_fscr;
} QEMU_PACKED;
typedef struct noteStruct {
Elf64_Nhdr hdr;
char name[5];
char pad3[3];
union {
struct PPC64ElfPrstatus prstatus;
struct PPC64ElfFpregset fpregset;
struct PPC64ElfVmxregset vmxregset;
struct PPC64ElfVsxregset vsxregset;
struct PPC64ElfSperegset speregset;
} contents;
} QEMU_PACKED Note;
static void ppc64_write_elf64_prstatus(Note *note, PowerPCCPU *cpu)
{
int i;
uint64_t cr;
struct PPC64ElfPrstatus *prstatus;
struct PPC64UserRegStruct *reg;
note->hdr.n_type = cpu_to_be32(NT_PRSTATUS);
prstatus = &note->contents.prstatus;
memset(prstatus, 0, sizeof(*prstatus));
reg = &prstatus->pr_reg;
for (i = 0; i < 32; i++) {
reg->gpr[i] = cpu_to_be64(cpu->env.gpr[i]);
}
reg->nip = cpu_to_be64(cpu->env.nip);
reg->msr = cpu_to_be64(cpu->env.msr);
reg->ctr = cpu_to_be64(cpu->env.ctr);
reg->link = cpu_to_be64(cpu->env.lr);
reg->xer = cpu_to_be64(cpu_read_xer(&cpu->env));
cr = 0;
for (i = 0; i < 8; i++) {
cr |= (cpu->env.crf[i] & 15) << (4 * (7 - i));
}
reg->ccr = cpu_to_be64(cr);
}
static void ppc64_write_elf64_fpregset(Note *note, PowerPCCPU *cpu)
{
int i;
struct PPC64ElfFpregset *fpregset;
note->hdr.n_type = cpu_to_be32(NT_PRFPREG);
fpregset = &note->contents.fpregset;
memset(fpregset, 0, sizeof(*fpregset));
for (i = 0; i < 32; i++) {
fpregset->fpr[i] = cpu_to_be64(cpu->env.fpr[i]);
}
fpregset->fpscr = cpu_to_be64(cpu->env.fpscr);
}
static void ppc64_write_elf64_vmxregset(Note *note, PowerPCCPU *cpu)
{
int i;
struct PPC64ElfVmxregset *vmxregset;
note->hdr.n_type = cpu_to_be32(NT_PPC_VMX);
vmxregset = &note->contents.vmxregset;
memset(vmxregset, 0, sizeof(*vmxregset));
for (i = 0; i < 32; i++) {
vmxregset->avr[i].u64[0] = cpu_to_be64(cpu->env.avr[i].u64[0]);
vmxregset->avr[i].u64[1] = cpu_to_be64(cpu->env.avr[i].u64[1]);
}
vmxregset->vscr.u32[3] = cpu_to_be32(cpu->env.vscr);
}
static void ppc64_write_elf64_vsxregset(Note *note, PowerPCCPU *cpu)
{
int i;
struct PPC64ElfVsxregset *vsxregset;
note->hdr.n_type = cpu_to_be32(NT_PPC_VSX);
vsxregset = &note->contents.vsxregset;
memset(vsxregset, 0, sizeof(*vsxregset));
for (i = 0; i < 32; i++) {
vsxregset->vsr[i] = cpu_to_be64(cpu->env.vsr[i]);
}
}
static void ppc64_write_elf64_speregset(Note *note, PowerPCCPU *cpu)
{
struct PPC64ElfSperegset *speregset;
note->hdr.n_type = cpu_to_be32(NT_PPC_SPE);
speregset = &note->contents.speregset;
memset(speregset, 0, sizeof(*speregset));
speregset->spe_acc = cpu_to_be64(cpu->env.spe_acc);
speregset->spe_fscr = cpu_to_be32(cpu->env.spe_fscr);
}
struct NoteFuncDescStruct {
int contents_size;
void (*note_contents_func)(Note *note, PowerPCCPU *cpu);
} note_func[] = {
{sizeof(((Note *)0)->contents.prstatus), ppc64_write_elf64_prstatus},
{sizeof(((Note *)0)->contents.fpregset), ppc64_write_elf64_fpregset},
{sizeof(((Note *)0)->contents.vmxregset), ppc64_write_elf64_vmxregset},
{sizeof(((Note *)0)->contents.vsxregset), ppc64_write_elf64_vsxregset},
{sizeof(((Note *)0)->contents.speregset), ppc64_write_elf64_speregset},
{ 0, NULL}
};
typedef struct NoteFuncDescStruct NoteFuncDesc;
int cpu_get_dump_info(ArchDumpInfo *info,
const struct GuestPhysBlockList *guest_phys_blocks)
{
/*
* Currently only handling PPC64 big endian.
*/
info->d_machine = EM_PPC64;
info->d_endian = ELFDATA2MSB;
info->d_class = ELFCLASS64;
return 0;
}
ssize_t cpu_get_note_size(int class, int machine, int nr_cpus)
{
int name_size = 8; /* "CORE" or "QEMU" rounded */
size_t elf_note_size = 0;
int note_head_size;
NoteFuncDesc *nf;
if (class != ELFCLASS64) {
return -1;
}
assert(machine == EM_PPC64);
note_head_size = sizeof(Elf64_Nhdr);
for (nf = note_func; nf->note_contents_func; nf++) {
elf_note_size = elf_note_size + note_head_size + name_size +
nf->contents_size;
}
return (elf_note_size) * nr_cpus;
}
static int ppc64_write_all_elf64_notes(const char *note_name,
WriteCoreDumpFunction f,
PowerPCCPU *cpu, int id,
void *opaque)
{
Note note;
int ret = -1;
int note_size;
NoteFuncDesc *nf;
for (nf = note_func; nf->note_contents_func; nf++) {
note.hdr.n_namesz = cpu_to_be32(sizeof(note.name));
note.hdr.n_descsz = cpu_to_be32(nf->contents_size);
strncpy(note.name, note_name, sizeof(note.name));
(*nf->note_contents_func)(&note, cpu);
note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size;
ret = f(&note, note_size, opaque);
if (ret < 0) {
return -1;
}
}
return 0;
}
int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
int cpuid, void *opaque)
{
PowerPCCPU *cpu = POWERPC_CPU(cs);
return ppc64_write_all_elf64_notes("CORE", f, cpu, cpuid, opaque);
}
int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f,
CPUState *cpu, void *opaque)
{
return 0;
}

View file

@ -108,7 +108,10 @@ void ppc_cpu_dump_statistics(CPUState *cpu, FILE *f,
hwaddr ppc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
int ppc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
int ppc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
int ppc64_cpu_write_elf64_qemunote(WriteCoreDumpFunction f,
CPUState *cpu, void *opaque);
int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs,
int cpuid, void *opaque);
#ifndef CONFIG_USER_ONLY
extern const struct VMStateDescription vmstate_ppc_cpu;
#endif

View file

@ -405,6 +405,7 @@ struct ppc_slb_t {
uint64_t vsid;
};
#define MAX_SLB_ENTRIES 64
#define SEGMENT_SHIFT_256M 28
#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1))
@ -949,7 +950,7 @@ struct CPUPPCState {
#if !defined(CONFIG_USER_ONLY)
#if defined(TARGET_PPC64)
/* PowerPC 64 SLB area */
ppc_slb_t slb[64];
ppc_slb_t slb[MAX_SLB_ENTRIES];
int32_t slb_nr;
#endif
/* segment registers */

View file

@ -818,7 +818,7 @@ int kvm_arch_put_registers(CPUState *cs, int level)
/* Sync SLB */
#ifdef TARGET_PPC64
for (i = 0; i < 64; i++) {
for (i = 0; i < ARRAY_SIZE(env->slb); i++) {
sregs.u.s.ppc64.slb[i].slbe = env->slb[i].esid;
sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid;
}
@ -1033,9 +1033,22 @@ int kvm_arch_get_registers(CPUState *cs)
/* Sync SLB */
#ifdef TARGET_PPC64
for (i = 0; i < 64; i++) {
ppc_store_slb(env, sregs.u.s.ppc64.slb[i].slbe,
sregs.u.s.ppc64.slb[i].slbv);
/*
* The packed SLB array we get from KVM_GET_SREGS only contains
* information about valid entries. So we flush our internal
* copy to get rid of stale ones, then put all valid SLB entries
* back in.
*/
memset(env->slb, 0, sizeof(env->slb));
for (i = 0; i < ARRAY_SIZE(env->slb); i++) {
target_ulong rb = sregs.u.s.ppc64.slb[i].slbe;
target_ulong rs = sregs.u.s.ppc64.slb[i].slbv;
/*
* Only restore valid entries
*/
if (rb & SLB_ESID_V) {
ppc_store_slb(env, rb, rs);
}
}
#endif
@ -1789,6 +1802,20 @@ static int kvm_ppc_register_host_cpu_type(void)
return 0;
}
int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function)
{
struct kvm_rtas_token_args args = {
.token = token,
};
if (!kvm_check_extension(kvm_state, KVM_CAP_PPC_RTAS)) {
return -ENOENT;
}
strncpy(args.name, function, sizeof(args.name));
return kvm_vm_ioctl(kvm_state, KVM_PPC_RTAS_DEFINE_TOKEN, &args);
}
int kvmppc_get_htab_fd(bool write)
{

View file

@ -38,6 +38,7 @@ uint64_t kvmppc_rma_size(uint64_t current_size, unsigned int hash_shift);
#endif /* !CONFIG_USER_ONLY */
int kvmppc_fixup_cpu(PowerPCCPU *cpu);
bool kvmppc_has_cap_epr(void);
int kvmppc_define_rtas_kernel_token(uint32_t token, const char *function);
int kvmppc_get_htab_fd(bool write);
int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns);
int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index,
@ -164,6 +165,12 @@ static inline bool kvmppc_has_cap_epr(void)
return false;
}
static inline int kvmppc_define_rtas_kernel_token(uint32_t token,
const char *function)
{
return -1;
}
static inline int kvmppc_get_htab_fd(bool write)
{
return -1;

View file

@ -312,7 +312,7 @@ static const VMStateDescription vmstate_slb = {
.minimum_version_id_old = 1,
.fields = (VMStateField []) {
VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU),
VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, 64),
VMSTATE_SLB_ARRAY(env.slb, PowerPCCPU, MAX_SLB_ENTRIES),
VMSTATE_END_OF_LIST()
}
};

View file

@ -212,6 +212,7 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg,
int index = (addr & 0xf) >> sh; \
\
if (msr_le) { \
index = n_elems - index - 1; \
r->element[LO_IDX ? index : (adjust - index)] = \
swap(access(env, addr)); \
} else { \
@ -236,6 +237,7 @@ LVE(lvewx, cpu_ldl_data, bswap32, u32)
int index = (addr & 0xf) >> sh; \
\
if (msr_le) { \
index = n_elems - index - 1; \
access(env, addr, swap(r->element[LO_IDX ? index : \
(adjust - index)])); \
} else { \

View file

@ -108,6 +108,11 @@ static void spr_write_clear (void *opaque, int sprn, int gprn)
tcg_temp_free(t0);
tcg_temp_free(t1);
}
static void spr_access_nop(void *opaque, int sprn, int gprn)
{
}
#endif
/* SPR common to all PowerPC */
@ -1382,7 +1387,7 @@ static void gen_spr_74xx (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Not strictly an SPR */
vscr_init(env, 0x00010000);
@ -5170,7 +5175,7 @@ static void init_proc_750 (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Time base */
gen_tbl(env);
@ -5233,7 +5238,7 @@ static void init_proc_750cl (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Time base */
gen_tbl(env);
@ -5419,7 +5424,7 @@ static void init_proc_750cx (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Time base */
gen_tbl(env);
@ -5486,7 +5491,7 @@ static void init_proc_750fx (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Time base */
gen_tbl(env);
@ -5558,7 +5563,7 @@ static void init_proc_750gx (CPUPPCState *env)
/* XXX : not implemented (XXX: different from 750fx) */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Time base */
gen_tbl(env);
@ -5694,7 +5699,7 @@ static void init_proc_755 (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* XXX : not implemented */
spr_register(env, SPR_L2PMCR, "L2PMCR",
@ -6650,7 +6655,7 @@ static void init_proc_970 (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Memory management */
/* XXX: not correct */
@ -6750,7 +6755,7 @@ static void init_proc_970FX (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Memory management */
/* XXX: not correct */
@ -6862,7 +6867,7 @@ static void init_proc_970GX (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Memory management */
/* XXX: not correct */
@ -6962,7 +6967,7 @@ static void init_proc_970MP (CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Memory management */
/* XXX: not correct */
@ -7054,7 +7059,7 @@ static void init_proc_power5plus(CPUPPCState *env)
/* XXX : not implemented */
spr_register(env, SPR_L2CR, "L2CR",
SPR_NOACCESS, SPR_NOACCESS,
&spr_read_generic, NULL,
&spr_read_generic, spr_access_nop,
0x00000000);
/* Memory management */
/* XXX: not correct */
@ -7103,6 +7108,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER5";
dc->desc = "POWER5+";
pcc->init_proc = init_proc_power5plus;
pcc->check_pow = check_pow_970FX;
@ -7213,6 +7219,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER7";
dc->desc = "POWER7";
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
@ -7247,6 +7254,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
dc->fw_name = "PowerPC,POWER8";
dc->desc = "POWER8";
pcc->init_proc = init_proc_POWER7;
pcc->check_pow = check_pow_nocheck;
@ -8567,6 +8575,10 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
#ifndef CONFIG_USER_ONLY
cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug;
cc->vmsd = &vmstate_ppc_cpu;
#if defined(TARGET_PPC64)
cc->write_elf64_note = ppc64_cpu_write_elf64_note;
cc->write_elf64_qemunote = ppc64_cpu_write_elf64_qemunote;
#endif
#endif
cc->gdb_num_core_regs = 71;
@ -8575,6 +8587,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
#else
cc->gdb_core_xml_file = "power-core.xml";
#endif
dc->fw_name = "PowerPC,UNKNOWN";
}
static const TypeInfo ppc_cpu_type_info = {