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);
|
l = sizeof(buf);
|
||||||
if (l > size)
|
if (l > size)
|
||||||
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) {
|
if (fwrite(buf, 1, l, f) != l) {
|
||||||
error_set(errp, QERR_IO_ERROR);
|
error_set(errp, QERR_IO_ERROR);
|
||||||
goto exit;
|
goto exit;
|
||||||
|
|
|
@ -46,6 +46,7 @@ CONFIG_E500=y
|
||||||
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM))
|
||||||
# For pSeries
|
# For pSeries
|
||||||
CONFIG_XICS=$(CONFIG_PSERIES)
|
CONFIG_XICS=$(CONFIG_PSERIES)
|
||||||
|
CONFIG_XICS_KVM=$(and $(CONFIG_PSERIES),$(CONFIG_KVM))
|
||||||
# For PReP
|
# For PReP
|
||||||
CONFIG_I82378=y
|
CONFIG_I82378=y
|
||||||
CONFIG_I8259=y
|
CONFIG_I8259=y
|
||||||
|
|
4
dump.c
4
dump.c
|
@ -66,7 +66,7 @@ typedef struct DumpState {
|
||||||
uint32_t sh_info;
|
uint32_t sh_info;
|
||||||
bool have_section;
|
bool have_section;
|
||||||
bool resume;
|
bool resume;
|
||||||
size_t note_size;
|
ssize_t note_size;
|
||||||
hwaddr memory_offset;
|
hwaddr memory_offset;
|
||||||
int fd;
|
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->note_size = cpu_get_note_size(s->dump_info.d_class,
|
||||||
s->dump_info.d_machine, nr_cpus);
|
s->dump_info.d_machine, nr_cpus);
|
||||||
if (ret < 0) {
|
if (s->note_size < 0) {
|
||||||
error_set(errp, QERR_UNSUPPORTED);
|
error_set(errp, QERR_UNSUPPORTED);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,3 +23,4 @@ obj-$(CONFIG_OMAP) += omap_intc.o
|
||||||
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
|
obj-$(CONFIG_OPENPIC_KVM) += openpic_kvm.o
|
||||||
obj-$(CONFIG_SH4) += sh_intc.o
|
obj-$(CONFIG_SH4) += sh_intc.o
|
||||||
obj-$(CONFIG_XICS) += xics.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 "hw/hw.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
#include "qemu/timer.h"
|
||||||
#include "hw/ppc/spapr.h"
|
#include "hw/ppc/spapr.h"
|
||||||
#include "hw/ppc/xics.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
|
* 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 = {
|
static const VMStateDescription vmstate_icp_server = {
|
||||||
.name = "icp/server",
|
.name = "icp/server",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.minimum_version_id_old = 1,
|
.minimum_version_id_old = 1,
|
||||||
|
.pre_save = icp_dispatch_pre_save,
|
||||||
|
.post_load = icp_dispatch_post_load,
|
||||||
.fields = (VMStateField []) {
|
.fields = (VMStateField []) {
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
VMSTATE_UINT32(xirr, ICPState),
|
VMSTATE_UINT32(xirr, ICPState),
|
||||||
|
@ -187,11 +351,12 @@ static void icp_class_init(ObjectClass *klass, void *data)
|
||||||
dc->vmsd = &vmstate_icp_server;
|
dc->vmsd = &vmstate_icp_server;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeInfo icp_info = {
|
static const TypeInfo icp_info = {
|
||||||
.name = TYPE_ICP,
|
.name = TYPE_ICP,
|
||||||
.parent = TYPE_DEVICE,
|
.parent = TYPE_DEVICE,
|
||||||
.instance_size = sizeof(ICPState),
|
.instance_size = sizeof(ICPState),
|
||||||
.class_init = icp_class_init,
|
.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;
|
int i;
|
||||||
ICSState *ics = opaque;
|
|
||||||
|
|
||||||
for (i = 0; i < ics->icp->nr_servers; i++) {
|
for (i = 0; i < ics->icp->nr_servers; i++) {
|
||||||
icp_resend(ics->icp, i);
|
icp_resend(ics->icp, i);
|
||||||
|
@ -365,6 +529,28 @@ static int ics_post_load(void *opaque, int version_id)
|
||||||
return 0;
|
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 = {
|
static const VMStateDescription vmstate_ics_irq = {
|
||||||
.name = "ics/irq",
|
.name = "ics/irq",
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
|
@ -384,7 +570,8 @@ static const VMStateDescription vmstate_ics = {
|
||||||
.version_id = 1,
|
.version_id = 1,
|
||||||
.minimum_version_id = 1,
|
.minimum_version_id = 1,
|
||||||
.minimum_version_id_old = 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 []) {
|
.fields = (VMStateField []) {
|
||||||
/* Sanity check */
|
/* Sanity check */
|
||||||
VMSTATE_UINT32_EQUAL(nr_irqs, ICSState),
|
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);
|
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->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState));
|
||||||
ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool));
|
ics->islsi = g_malloc0(ics->nr_irqs * sizeof(bool));
|
||||||
ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs);
|
ics->qirqs = qemu_allocate_irqs(ics_set_irq, ics, ics->nr_irqs);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ics_class_init(ObjectClass *klass, void *data)
|
static void ics_class_init(ObjectClass *klass, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
ICSStateClass *isc = ICS_CLASS(klass);
|
||||||
|
|
||||||
dc->init = ics_realize;
|
dc->realize = ics_realize;
|
||||||
dc->vmsd = &vmstate_ics;
|
dc->vmsd = &vmstate_ics;
|
||||||
dc->reset = ics_reset;
|
dc->reset = ics_reset;
|
||||||
|
isc->post_load = ics_post_load;
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypeInfo ics_info = {
|
static const TypeInfo ics_info = {
|
||||||
.name = TYPE_ICS,
|
.name = TYPE_ICS,
|
||||||
.parent = TYPE_DEVICE,
|
.parent = TYPE_DEVICE,
|
||||||
.instance_size = sizeof(ICSState),
|
.instance_size = sizeof(ICSState),
|
||||||
.class_init = ics_class_init,
|
.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;
|
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,
|
static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||||
target_ulong opcode, target_ulong *args)
|
target_ulong opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
|
@ -490,6 +702,18 @@ static target_ulong h_eoi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||||
return H_SUCCESS;
|
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,
|
static void rtas_set_xive(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||||
uint32_t token,
|
uint32_t token,
|
||||||
uint32_t nargs, target_ulong args,
|
uint32_t nargs, target_ulong args,
|
||||||
|
@ -600,48 +824,39 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||||
* XICS
|
* 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);
|
icp->nr_irqs = icp->ics->nr_irqs = nr_irqs;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < icp->nr_servers; i++) {
|
|
||||||
device_reset(DEVICE(&icp->ss[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
device_reset(DEVICE(icp->ics));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
int i;
|
||||||
CPUPPCState *env = &cpu->env;
|
|
||||||
ICPState *ss = &icp->ss[cs->cpu_index];
|
|
||||||
|
|
||||||
assert(cs->cpu_index < icp->nr_servers);
|
icp->nr_servers = nr_servers;
|
||||||
|
|
||||||
switch (PPC_INPUT(env)) {
|
icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
|
||||||
case PPC_FLAGS_INPUT_POWER7:
|
for (i = 0; i < icp->nr_servers; i++) {
|
||||||
ss->output = env->irq_inputs[POWER7_INPUT_INT];
|
char buffer[32];
|
||||||
break;
|
object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
|
||||||
|
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
|
||||||
case PPC_FLAGS_INPUT_970:
|
object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]),
|
||||||
ss->output = env->irq_inputs[PPC970_INPUT_INT];
|
errp);
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "XICS interrupt controller does not support this CPU "
|
|
||||||
"bus model\n");
|
|
||||||
abort();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xics_realize(DeviceState *dev, Error **errp)
|
static void xics_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
XICSState *icp = XICS(dev);
|
XICSState *icp = XICS(dev);
|
||||||
ICSState *ics = icp->ics;
|
Error *error = NULL;
|
||||||
int i;
|
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 */
|
/* Registration of global state belongs into realize */
|
||||||
spapr_rtas_register("ibm,set-xive", rtas_set_xive);
|
spapr_rtas_register("ibm,set-xive", rtas_set_xive);
|
||||||
spapr_rtas_register("ibm,get-xive", rtas_get_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_CPPR, h_cppr);
|
||||||
spapr_register_hypercall(H_IPI, h_ipi);
|
spapr_register_hypercall(H_IPI, h_ipi);
|
||||||
spapr_register_hypercall(H_XIRR, h_xirr);
|
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_EOI, h_eoi);
|
||||||
|
spapr_register_hypercall(H_IPOLL, h_ipoll);
|
||||||
|
|
||||||
ics->nr_irqs = icp->nr_irqs;
|
object_property_set_bool(OBJECT(icp->ics), true, "realized", &error);
|
||||||
ics->offset = XICS_IRQ_BASE;
|
if (error) {
|
||||||
ics->icp = icp;
|
error_propagate(errp, error);
|
||||||
qdev_init_nofail(DEVICE(ics));
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
icp->ss = g_malloc0(icp->nr_servers*sizeof(ICPState));
|
|
||||||
for (i = 0; i < icp->nr_servers; i++) {
|
for (i = 0; i < icp->nr_servers; i++) {
|
||||||
char buffer[32];
|
object_property_set_bool(OBJECT(&icp->ss[i]), true, "realized", &error);
|
||||||
object_initialize(&icp->ss[i], sizeof(icp->ss[i]), TYPE_ICP);
|
if (error) {
|
||||||
snprintf(buffer, sizeof(buffer), "icp[%d]", i);
|
error_propagate(errp, error);
|
||||||
object_property_add_child(OBJECT(icp), buffer, OBJECT(&icp->ss[i]), NULL);
|
return;
|
||||||
qdev_init_nofail(DEVICE(&icp->ss[i]));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -674,33 +891,31 @@ static void xics_initfn(Object *obj)
|
||||||
|
|
||||||
xics->ics = ICS(object_new(TYPE_ICS));
|
xics->ics = ICS(object_new(TYPE_ICS));
|
||||||
object_property_add_child(obj, "ics", OBJECT(xics->ics), NULL);
|
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)
|
static void xics_class_init(ObjectClass *oc, void *data)
|
||||||
{
|
{
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
|
XICSStateClass *xsc = XICS_CLASS(oc);
|
||||||
|
|
||||||
dc->realize = xics_realize;
|
dc->realize = xics_realize;
|
||||||
dc->props = xics_properties;
|
xsc->set_nr_irqs = xics_set_nr_irqs;
|
||||||
dc->reset = xics_reset;
|
xsc->set_nr_servers = xics_set_nr_servers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo xics_info = {
|
static const TypeInfo xics_info = {
|
||||||
.name = TYPE_XICS,
|
.name = TYPE_XICS,
|
||||||
.parent = TYPE_SYS_BUS_DEVICE,
|
.parent = TYPE_XICS_COMMON,
|
||||||
.instance_size = sizeof(XICSState),
|
.instance_size = sizeof(XICSState),
|
||||||
|
.class_size = sizeof(XICSStateClass),
|
||||||
.class_init = xics_class_init,
|
.class_init = xics_class_init,
|
||||||
.instance_init = xics_initfn,
|
.instance_init = xics_initfn,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void xics_register_types(void)
|
static void xics_register_types(void)
|
||||||
{
|
{
|
||||||
|
type_register_static(&xics_common_info);
|
||||||
type_register_static(&xics_info);
|
type_register_static(&xics_info);
|
||||||
type_register_static(&ics_info);
|
type_register_static(&ics_info);
|
||||||
type_register_static(&icp_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
|
* 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 RTAS_MAX_SIZE 0x10000
|
||||||
#define FW_MAX_SIZE 0x400000
|
#define FW_MAX_SIZE 0x400000
|
||||||
#define FW_FILE_NAME "slof.bin"
|
#define FW_FILE_NAME "slof.bin"
|
||||||
|
@ -161,14 +161,33 @@ static XICSState *try_create_xics(const char *type, int nr_servers,
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return XICS(dev);
|
return XICS_COMMON(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static XICSState *xics_system_init(int nr_servers, int nr_irqs)
|
static XICSState *xics_system_init(int nr_servers, int nr_irqs)
|
||||||
{
|
{
|
||||||
XICSState *icp = NULL;
|
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) {
|
if (!icp) {
|
||||||
perror("Failed to create XICS\n");
|
perror("Failed to create XICS\n");
|
||||||
abort();
|
abort();
|
||||||
|
@ -185,9 +204,8 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
|
||||||
int smt = kvmppc_smt_threads();
|
int smt = kvmppc_smt_threads();
|
||||||
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
|
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
|
||||||
|
|
||||||
assert(spapr->cpu_model);
|
|
||||||
|
|
||||||
CPU_FOREACH(cpu) {
|
CPU_FOREACH(cpu) {
|
||||||
|
DeviceClass *dc = DEVICE_GET_CLASS(cpu);
|
||||||
uint32_t associativity[] = {cpu_to_be32(0x5),
|
uint32_t associativity[] = {cpu_to_be32(0x5),
|
||||||
cpu_to_be32(0x0),
|
cpu_to_be32(0x0),
|
||||||
cpu_to_be32(0x0),
|
cpu_to_be32(0x0),
|
||||||
|
@ -199,7 +217,7 @@ static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model,
|
snprintf(cpu_model, 32, "/cpus/%s@%x", dc->fw_name,
|
||||||
cpu->cpu_index);
|
cpu->cpu_index);
|
||||||
|
|
||||||
offset = fdt_path_offset(fdt, cpu_model);
|
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)
|
} while (0)
|
||||||
|
|
||||||
|
|
||||||
static void *spapr_create_fdt_skel(const char *cpu_model,
|
static void *spapr_create_fdt_skel(hwaddr initrd_base,
|
||||||
hwaddr initrd_base,
|
|
||||||
hwaddr initrd_size,
|
hwaddr initrd_size,
|
||||||
hwaddr kernel_size,
|
hwaddr kernel_size,
|
||||||
|
bool little_endian,
|
||||||
const char *boot_device,
|
const char *boot_device,
|
||||||
const char *kernel_cmdline,
|
const char *kernel_cmdline,
|
||||||
uint32_t epow_irq)
|
uint32_t epow_irq)
|
||||||
|
@ -286,7 +304,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||||
char qemu_hypertas_prop[] = "hcall-memop1";
|
char qemu_hypertas_prop[] = "hcall-memop1";
|
||||||
uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
|
uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
|
||||||
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
|
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
|
||||||
char *modelname;
|
|
||||||
int i, smt = kvmppc_smt_threads();
|
int i, smt = kvmppc_smt_threads();
|
||||||
unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
|
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) };
|
cpu_to_be64(kernel_size) };
|
||||||
|
|
||||||
_FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop))));
|
_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) {
|
if (boot_device) {
|
||||||
_FDT((fdt_property_string(fdt, "qemu,boot-device", 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, "#address-cells", 0x1)));
|
||||||
_FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
|
_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) {
|
CPU_FOREACH(cs) {
|
||||||
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
PowerPCCPU *cpu = POWERPC_CPU(cs);
|
||||||
CPUPPCState *env = &cpu->env;
|
CPUPPCState *env = &cpu->env;
|
||||||
|
DeviceClass *dc = DEVICE_GET_CLASS(cs);
|
||||||
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
|
PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs);
|
||||||
int index = cs->cpu_index;
|
int index = cs->cpu_index;
|
||||||
uint32_t servers_prop[smp_threads];
|
uint32_t servers_prop[smp_threads];
|
||||||
|
@ -370,7 +382,7 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||||
continue;
|
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)));
|
_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",
|
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
|
||||||
gservers_prop, sizeof(gservers_prop))));
|
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) {
|
if (env->mmu_model & POWERPC_MMU_1TSEG) {
|
||||||
_FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
|
_FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
|
||||||
segs, sizeof(segs))));
|
segs, sizeof(segs))));
|
||||||
|
@ -450,8 +466,6 @@ static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||||
_FDT((fdt_end_node(fdt)));
|
_FDT((fdt_end_node(fdt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
g_free(modelname);
|
|
||||||
|
|
||||||
_FDT((fdt_end_node(fdt)));
|
_FDT((fdt_end_node(fdt)));
|
||||||
|
|
||||||
/* RTAS */
|
/* RTAS */
|
||||||
|
@ -1102,6 +1116,7 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||||
uint32_t initrd_base = 0;
|
uint32_t initrd_base = 0;
|
||||||
long kernel_size = 0, initrd_size = 0;
|
long kernel_size = 0, initrd_size = 0;
|
||||||
long load_limit, rtas_limit, fw_size;
|
long load_limit, rtas_limit, fw_size;
|
||||||
|
bool kernel_le = false;
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
msi_supported = true;
|
msi_supported = true;
|
||||||
|
@ -1175,8 +1190,6 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||||
}
|
}
|
||||||
env = &cpu->env;
|
env = &cpu->env;
|
||||||
|
|
||||||
xics_cpu_setup(spapr->icp, cpu);
|
|
||||||
|
|
||||||
/* Set time-base frequency to 512 MHz */
|
/* Set time-base frequency to 512 MHz */
|
||||||
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
|
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
|
||||||
|
|
||||||
|
@ -1190,6 +1203,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||||
kvmppc_set_papr(cpu);
|
kvmppc_set_papr(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xics_cpu_setup(spapr->icp, cpu);
|
||||||
|
|
||||||
qemu_register_reset(spapr_cpu_reset, 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,
|
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
|
||||||
NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
|
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) {
|
if (kernel_size < 0) {
|
||||||
kernel_size = load_image_targphys(kernel_filename,
|
kernel_size = load_image_targphys(kernel_filename,
|
||||||
KERNEL_LOAD_ADDR,
|
KERNEL_LOAD_ADDR,
|
||||||
|
@ -1329,9 +1350,8 @@ static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||||
&savevm_htab_handlers, spapr);
|
&savevm_htab_handlers, spapr);
|
||||||
|
|
||||||
/* Prepare the device tree */
|
/* Prepare the device tree */
|
||||||
spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
|
spapr->fdt_skel = spapr_create_fdt_skel(initrd_base, initrd_size,
|
||||||
initrd_base, initrd_size,
|
kernel_size, kernel_le,
|
||||||
kernel_size,
|
|
||||||
boot_device, kernel_cmdline,
|
boot_device, kernel_cmdline,
|
||||||
spapr->epow_irq);
|
spapr->epow_irq);
|
||||||
assert(spapr->fdt_skel != NULL);
|
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 opcode, target_ulong *args)
|
||||||
{
|
{
|
||||||
target_ulong rtas_r3 = args[0];
|
target_ulong rtas_r3 = args[0];
|
||||||
uint32_t token = ldl_be_phys(rtas_r3);
|
uint32_t token = rtas_ld(rtas_r3, 0);
|
||||||
uint32_t nargs = ldl_be_phys(rtas_r3 + 4);
|
uint32_t nargs = rtas_ld(rtas_r3, 1);
|
||||||
uint32_t nret = ldl_be_phys(rtas_r3 + 8);
|
uint32_t nret = rtas_ld(rtas_r3, 2);
|
||||||
|
|
||||||
return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12,
|
return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12,
|
||||||
nret, rtas_r3 + 12 + 4*nargs);
|
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);
|
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.
|
* MSI/MSIX memory region implementation.
|
||||||
* The handler handles both MSI and MSIX.
|
* 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_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);
|
QLIST_INSERT_HEAD(&spapr->phbs, sphb, list);
|
||||||
|
|
||||||
/* Initialize the LSI table */
|
/* Initialize the LSI table */
|
||||||
|
|
|
@ -1359,6 +1359,9 @@ typedef struct elf64_shdr {
|
||||||
#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */
|
#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */
|
||||||
#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
|
#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */
|
||||||
#define NT_S390_TIMER 0x301 /* s390 timer 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 */
|
/* Note header in a PT_NOTE section */
|
||||||
|
|
|
@ -29,7 +29,6 @@ typedef struct sPAPREnvironment {
|
||||||
target_ulong entry_point;
|
target_ulong entry_point;
|
||||||
uint32_t next_irq;
|
uint32_t next_irq;
|
||||||
uint64_t rtc_offset;
|
uint64_t rtc_offset;
|
||||||
char *cpu_model;
|
|
||||||
bool has_graphics;
|
bool has_graphics;
|
||||||
|
|
||||||
uint32_t epow_irq;
|
uint32_t epow_irq;
|
||||||
|
@ -283,6 +282,7 @@ typedef struct sPAPREnvironment {
|
||||||
#define H_GET_EM_PARMS 0x2B8
|
#define H_GET_EM_PARMS 0x2B8
|
||||||
#define H_SET_MPP 0x2D0
|
#define H_SET_MPP 0x2D0
|
||||||
#define H_GET_MPP 0x2D4
|
#define H_GET_MPP 0x2D4
|
||||||
|
#define H_XIRR_X 0x2FC
|
||||||
#define H_SET_MODE 0x31C
|
#define H_SET_MODE 0x31C
|
||||||
#define MAX_HCALL_OPCODE H_SET_MODE
|
#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);
|
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)
|
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)
|
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,
|
typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPREnvironment *spapr,
|
||||||
|
|
|
@ -29,9 +29,24 @@
|
||||||
|
|
||||||
#include "hw/sysbus.h"
|
#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 TYPE_XICS "xics"
|
||||||
#define XICS(obj) OBJECT_CHECK(XICSState, (obj), TYPE_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_IPI 0x2
|
||||||
#define XICS_BUID 0x1
|
#define XICS_BUID 0x1
|
||||||
#define XICS_IRQ_BASE (XICS_BUID << 12)
|
#define XICS_IRQ_BASE (XICS_BUID << 12)
|
||||||
|
@ -41,11 +56,22 @@
|
||||||
* (the kernel implementation supports more but we don't exploit
|
* (the kernel implementation supports more but we don't exploit
|
||||||
* that yet)
|
* that yet)
|
||||||
*/
|
*/
|
||||||
|
typedef struct XICSStateClass XICSStateClass;
|
||||||
typedef struct XICSState XICSState;
|
typedef struct XICSState XICSState;
|
||||||
|
typedef struct ICPStateClass ICPStateClass;
|
||||||
typedef struct ICPState ICPState;
|
typedef struct ICPState ICPState;
|
||||||
|
typedef struct ICSStateClass ICSStateClass;
|
||||||
typedef struct ICSState ICSState;
|
typedef struct ICSState ICSState;
|
||||||
typedef struct ICSIRQState ICSIRQState;
|
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 {
|
struct XICSState {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
SysBusDevice parent_obj;
|
SysBusDevice parent_obj;
|
||||||
|
@ -59,10 +85,26 @@ struct XICSState {
|
||||||
#define TYPE_ICP "icp"
|
#define TYPE_ICP "icp"
|
||||||
#define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP)
|
#define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP)
|
||||||
|
|
||||||
|
#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 {
|
struct ICPState {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
DeviceState parent_obj;
|
DeviceState parent_obj;
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
|
CPUState *cs;
|
||||||
uint32_t xirr;
|
uint32_t xirr;
|
||||||
uint8_t pending_priority;
|
uint8_t pending_priority;
|
||||||
uint8_t mfrr;
|
uint8_t mfrr;
|
||||||
|
@ -72,6 +114,21 @@ struct ICPState {
|
||||||
#define TYPE_ICS "ics"
|
#define TYPE_ICS "ics"
|
||||||
#define ICS(obj) OBJECT_CHECK(ICSState, (obj), TYPE_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 {
|
struct ICSState {
|
||||||
/*< private >*/
|
/*< private >*/
|
||||||
DeviceState parent_obj;
|
DeviceState parent_obj;
|
||||||
|
|
|
@ -3180,6 +3180,9 @@ static const MonitorDef monitor_defs[] = {
|
||||||
|
|
||||||
{ "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) },
|
{ "srr0", offsetof(CPUPPCState, spr[SPR_SRR0]) },
|
||||||
{ "srr1", offsetof(CPUPPCState, spr[SPR_SRR1]) },
|
{ "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]) },
|
{ "sprg0", offsetof(CPUPPCState, spr[SPR_SPRG0]) },
|
||||||
{ "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) },
|
{ "sprg1", offsetof(CPUPPCState, spr[SPR_SPRG1]) },
|
||||||
{ "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) },
|
{ "sprg2", offsetof(CPUPPCState, spr[SPR_SPRG2]) },
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
- SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware
|
||||||
implementation for certain IBM POWER hardware. The sources are at
|
implementation for certain IBM POWER hardware. The sources are at
|
||||||
https://github.com/aik/SLOF, and the image currently in qemu is
|
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
|
- sgabios (the Serial Graphics Adapter option ROM) provides a means for
|
||||||
legacy x86 software to communicate with an attached serial console as
|
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
|
obj-y += translate.o
|
||||||
ifeq ($(CONFIG_SOFTMMU),y)
|
ifeq ($(CONFIG_SOFTMMU),y)
|
||||||
obj-y += machine.o mmu_helper.o mmu-hash32.o
|
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
|
endif
|
||||||
obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
|
obj-$(CONFIG_KVM) += kvm.o kvm_ppc.o
|
||||||
obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.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);
|
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_read_register(CPUState *cpu, uint8_t *buf, int reg);
|
||||||
int ppc_cpu_gdb_write_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
|
#ifndef CONFIG_USER_ONLY
|
||||||
extern const struct VMStateDescription vmstate_ppc_cpu;
|
extern const struct VMStateDescription vmstate_ppc_cpu;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -405,6 +405,7 @@ struct ppc_slb_t {
|
||||||
uint64_t vsid;
|
uint64_t vsid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MAX_SLB_ENTRIES 64
|
||||||
#define SEGMENT_SHIFT_256M 28
|
#define SEGMENT_SHIFT_256M 28
|
||||||
#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1))
|
#define SEGMENT_MASK_256M (~((1ULL << SEGMENT_SHIFT_256M) - 1))
|
||||||
|
|
||||||
|
@ -949,7 +950,7 @@ struct CPUPPCState {
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
#if defined(TARGET_PPC64)
|
#if defined(TARGET_PPC64)
|
||||||
/* PowerPC 64 SLB area */
|
/* PowerPC 64 SLB area */
|
||||||
ppc_slb_t slb[64];
|
ppc_slb_t slb[MAX_SLB_ENTRIES];
|
||||||
int32_t slb_nr;
|
int32_t slb_nr;
|
||||||
#endif
|
#endif
|
||||||
/* segment registers */
|
/* segment registers */
|
||||||
|
|
|
@ -818,7 +818,7 @@ int kvm_arch_put_registers(CPUState *cs, int level)
|
||||||
|
|
||||||
/* Sync SLB */
|
/* Sync SLB */
|
||||||
#ifdef TARGET_PPC64
|
#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].slbe = env->slb[i].esid;
|
||||||
sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid;
|
sregs.u.s.ppc64.slb[i].slbv = env->slb[i].vsid;
|
||||||
}
|
}
|
||||||
|
@ -1033,9 +1033,22 @@ int kvm_arch_get_registers(CPUState *cs)
|
||||||
|
|
||||||
/* Sync SLB */
|
/* Sync SLB */
|
||||||
#ifdef TARGET_PPC64
|
#ifdef TARGET_PPC64
|
||||||
for (i = 0; i < 64; i++) {
|
/*
|
||||||
ppc_store_slb(env, sregs.u.s.ppc64.slb[i].slbe,
|
* The packed SLB array we get from KVM_GET_SREGS only contains
|
||||||
sregs.u.s.ppc64.slb[i].slbv);
|
* 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
|
#endif
|
||||||
|
|
||||||
|
@ -1789,6 +1802,20 @@ static int kvm_ppc_register_host_cpu_type(void)
|
||||||
return 0;
|
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)
|
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 */
|
#endif /* !CONFIG_USER_ONLY */
|
||||||
int kvmppc_fixup_cpu(PowerPCCPU *cpu);
|
int kvmppc_fixup_cpu(PowerPCCPU *cpu);
|
||||||
bool kvmppc_has_cap_epr(void);
|
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_get_htab_fd(bool write);
|
||||||
int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns);
|
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,
|
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;
|
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)
|
static inline int kvmppc_get_htab_fd(bool write)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -312,7 +312,7 @@ static const VMStateDescription vmstate_slb = {
|
||||||
.minimum_version_id_old = 1,
|
.minimum_version_id_old = 1,
|
||||||
.fields = (VMStateField []) {
|
.fields = (VMStateField []) {
|
||||||
VMSTATE_INT32_EQUAL(env.slb_nr, PowerPCCPU),
|
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()
|
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; \
|
int index = (addr & 0xf) >> sh; \
|
||||||
\
|
\
|
||||||
if (msr_le) { \
|
if (msr_le) { \
|
||||||
|
index = n_elems - index - 1; \
|
||||||
r->element[LO_IDX ? index : (adjust - index)] = \
|
r->element[LO_IDX ? index : (adjust - index)] = \
|
||||||
swap(access(env, addr)); \
|
swap(access(env, addr)); \
|
||||||
} else { \
|
} else { \
|
||||||
|
@ -236,6 +237,7 @@ LVE(lvewx, cpu_ldl_data, bswap32, u32)
|
||||||
int index = (addr & 0xf) >> sh; \
|
int index = (addr & 0xf) >> sh; \
|
||||||
\
|
\
|
||||||
if (msr_le) { \
|
if (msr_le) { \
|
||||||
|
index = n_elems - index - 1; \
|
||||||
access(env, addr, swap(r->element[LO_IDX ? index : \
|
access(env, addr, swap(r->element[LO_IDX ? index : \
|
||||||
(adjust - index)])); \
|
(adjust - index)])); \
|
||||||
} else { \
|
} else { \
|
||||||
|
|
|
@ -108,6 +108,11 @@ static void spr_write_clear (void *opaque, int sprn, int gprn)
|
||||||
tcg_temp_free(t0);
|
tcg_temp_free(t0);
|
||||||
tcg_temp_free(t1);
|
tcg_temp_free(t1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void spr_access_nop(void *opaque, int sprn, int gprn)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* SPR common to all PowerPC */
|
/* SPR common to all PowerPC */
|
||||||
|
@ -1382,7 +1387,7 @@ static void gen_spr_74xx (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Not strictly an SPR */
|
/* Not strictly an SPR */
|
||||||
vscr_init(env, 0x00010000);
|
vscr_init(env, 0x00010000);
|
||||||
|
@ -5170,7 +5175,7 @@ static void init_proc_750 (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Time base */
|
/* Time base */
|
||||||
gen_tbl(env);
|
gen_tbl(env);
|
||||||
|
@ -5233,7 +5238,7 @@ static void init_proc_750cl (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Time base */
|
/* Time base */
|
||||||
gen_tbl(env);
|
gen_tbl(env);
|
||||||
|
@ -5419,7 +5424,7 @@ static void init_proc_750cx (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Time base */
|
/* Time base */
|
||||||
gen_tbl(env);
|
gen_tbl(env);
|
||||||
|
@ -5486,7 +5491,7 @@ static void init_proc_750fx (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Time base */
|
/* Time base */
|
||||||
gen_tbl(env);
|
gen_tbl(env);
|
||||||
|
@ -5558,7 +5563,7 @@ static void init_proc_750gx (CPUPPCState *env)
|
||||||
/* XXX : not implemented (XXX: different from 750fx) */
|
/* XXX : not implemented (XXX: different from 750fx) */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Time base */
|
/* Time base */
|
||||||
gen_tbl(env);
|
gen_tbl(env);
|
||||||
|
@ -5694,7 +5699,7 @@ static void init_proc_755 (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2PMCR, "L2PMCR",
|
spr_register(env, SPR_L2PMCR, "L2PMCR",
|
||||||
|
@ -6650,7 +6655,7 @@ static void init_proc_970 (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Memory management */
|
/* Memory management */
|
||||||
/* XXX: not correct */
|
/* XXX: not correct */
|
||||||
|
@ -6750,7 +6755,7 @@ static void init_proc_970FX (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Memory management */
|
/* Memory management */
|
||||||
/* XXX: not correct */
|
/* XXX: not correct */
|
||||||
|
@ -6862,7 +6867,7 @@ static void init_proc_970GX (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Memory management */
|
/* Memory management */
|
||||||
/* XXX: not correct */
|
/* XXX: not correct */
|
||||||
|
@ -6962,7 +6967,7 @@ static void init_proc_970MP (CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Memory management */
|
/* Memory management */
|
||||||
/* XXX: not correct */
|
/* XXX: not correct */
|
||||||
|
@ -7054,7 +7059,7 @@ static void init_proc_power5plus(CPUPPCState *env)
|
||||||
/* XXX : not implemented */
|
/* XXX : not implemented */
|
||||||
spr_register(env, SPR_L2CR, "L2CR",
|
spr_register(env, SPR_L2CR, "L2CR",
|
||||||
SPR_NOACCESS, SPR_NOACCESS,
|
SPR_NOACCESS, SPR_NOACCESS,
|
||||||
&spr_read_generic, NULL,
|
&spr_read_generic, spr_access_nop,
|
||||||
0x00000000);
|
0x00000000);
|
||||||
/* Memory management */
|
/* Memory management */
|
||||||
/* XXX: not correct */
|
/* XXX: not correct */
|
||||||
|
@ -7103,6 +7108,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data)
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||||
|
|
||||||
|
dc->fw_name = "PowerPC,POWER5";
|
||||||
dc->desc = "POWER5+";
|
dc->desc = "POWER5+";
|
||||||
pcc->init_proc = init_proc_power5plus;
|
pcc->init_proc = init_proc_power5plus;
|
||||||
pcc->check_pow = check_pow_970FX;
|
pcc->check_pow = check_pow_970FX;
|
||||||
|
@ -7213,6 +7219,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data)
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||||
|
|
||||||
|
dc->fw_name = "PowerPC,POWER7";
|
||||||
dc->desc = "POWER7";
|
dc->desc = "POWER7";
|
||||||
pcc->init_proc = init_proc_POWER7;
|
pcc->init_proc = init_proc_POWER7;
|
||||||
pcc->check_pow = check_pow_nocheck;
|
pcc->check_pow = check_pow_nocheck;
|
||||||
|
@ -7247,6 +7254,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data)
|
||||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||||
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
|
||||||
|
|
||||||
|
dc->fw_name = "PowerPC,POWER8";
|
||||||
dc->desc = "POWER8";
|
dc->desc = "POWER8";
|
||||||
pcc->init_proc = init_proc_POWER7;
|
pcc->init_proc = init_proc_POWER7;
|
||||||
pcc->check_pow = check_pow_nocheck;
|
pcc->check_pow = check_pow_nocheck;
|
||||||
|
@ -8567,6 +8575,10 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
|
||||||
#ifndef CONFIG_USER_ONLY
|
#ifndef CONFIG_USER_ONLY
|
||||||
cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug;
|
cc->get_phys_page_debug = ppc_cpu_get_phys_page_debug;
|
||||||
cc->vmsd = &vmstate_ppc_cpu;
|
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
|
#endif
|
||||||
|
|
||||||
cc->gdb_num_core_regs = 71;
|
cc->gdb_num_core_regs = 71;
|
||||||
|
@ -8575,6 +8587,8 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data)
|
||||||
#else
|
#else
|
||||||
cc->gdb_core_xml_file = "power-core.xml";
|
cc->gdb_core_xml_file = "power-core.xml";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
dc->fw_name = "PowerPC,UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TypeInfo ppc_cpu_type_info = {
|
static const TypeInfo ppc_cpu_type_info = {
|
||||||
|
|
Loading…
Reference in New Issue