mirror of https://github.com/xemu-project/xemu.git
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:
commit
1ba1905abd
5
cpus.c
5
cpus.c
|
@ -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;
|
||||
|
|
|
@ -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
4
dump.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
327
hw/intc/xics.c
327
hw/intc/xics.c
|
@ -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);
|
||||
|
|
|
@ -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, ®);
|
||||
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, ®);
|
||||
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)
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]) },
|
||||
|
|
|
@ -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
|
||||
|
|
BIN
pc-bios/slof.bin
BIN
pc-bios/slof.bin
Binary file not shown.
|
@ -1 +1 @@
|
|||
Subproject commit 8cfdfc43f4c4c8c8dfa4b7cf16f7c19c84eee812
|
||||
Subproject commit a523d1b0cd6e96cf5e393f0a10f897e8ed639fdc
|
|
@ -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
|
||||
|
|
|
@ -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 = ¬e->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 = ¬e->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 = ¬e->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 = ¬e->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 = ¬e->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)(¬e, cpu);
|
||||
|
||||
note_size = sizeof(note) - sizeof(note.contents) + nf->contents_size;
|
||||
ret = f(¬e, 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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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 { \
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in New Issue