提交 eb9d0ea0 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20150924' into staging

target-arm queue:
 * support VGICv3 in KVM
 * fix bug in ACPI table entries for flash devices in virt board
 * update Allwinner entry in MAINTAINERS

# gpg: Signature made Thu 24 Sep 2015 01:29:55 BST using RSA key ID 14360CDE
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>"
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>"
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>"

* remotes/pmaydell/tags/pull-target-arm-20150924:
  MAINTAINERS: update Allwinner A10 maintainer
  hw/arm/virt-acpi-build: Fix wrong size of flash in ACPI table
  hw/arm/virt: Add gic-version option to virt machine
  hw/intc: Initial implementation of vGICv3
  arm_kvm: Do not assume particular GIC type in kvm_arch_irqchip_create()
  intc/gic: Extract some reusable vGIC code
  hw/intc: Implement GIC-500 base class
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
......@@ -266,10 +266,10 @@ F: *win32*
ARM Machines
------------
Allwinner-a10
M: Li Guang <lig.fnst@cn.fujitsu.com>
M: Beniamino Galvani <b.galvani@gmail.com>
S: Maintained
F: hw/*/allwinner-a10*
F: include/hw/*/allwinner-a10*
F: hw/*/allwinner*
F: include/hw/*/allwinner*
F: hw/arm/cubieboard.c
Exynos
......
......@@ -114,7 +114,7 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap)
{
Aml *dev, *crs;
hwaddr base = flash_memmap->base;
hwaddr size = flash_memmap->size;
hwaddr size = flash_memmap->size / 2;
dev = aml_device("FLS0");
aml_append(dev, aml_name_decl("_HID", aml_string("LNRO0015")));
......@@ -443,6 +443,20 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
madt = acpi_data_push(table_data, sizeof *madt);
gicd = acpi_data_push(table_data, sizeof *gicd);
gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR;
gicd->length = sizeof(*gicd);
gicd->base_address = memmap[VIRT_GIC_DIST].base;
if (guest_info->gic_version == 3) {
AcpiMadtGenericRedistributor *gicr = acpi_data_push(table_data,
sizeof *gicr);
gicr->type = ACPI_APIC_GENERIC_REDISTRIBUTOR;
gicr->length = sizeof(*gicr);
gicr->base_address = cpu_to_le64(memmap[VIRT_GIC_REDIST].base);
gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST].size);
} else {
for (i = 0; i < guest_info->smp_cpus; i++) {
AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data,
sizeof *gicc);
......@@ -457,11 +471,6 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
}
}
gicd = acpi_data_push(table_data, sizeof *gicd);
gicd->type = ACPI_APIC_GENERIC_DISTRIBUTOR;
gicd->length = sizeof(*gicd);
gicd->base_address = memmap[VIRT_GIC_DIST].base;
gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
gic_msi->length = sizeof(*gic_msi);
......@@ -470,6 +479,7 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
gic_msi->flags = cpu_to_le32(1);
gic_msi->spi_count = cpu_to_le16(NUM_GICV2M_SPIS);
gic_msi->spi_base = cpu_to_le16(irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE);
}
build_header(linker, table_data,
(void *)(table_data->data + madt_start), "APIC",
......
......@@ -51,6 +51,7 @@
#include "hw/intc/arm_gic_common.h"
#include "kvm_arm.h"
#include "hw/smbios/smbios.h"
#include "qapi/visitor.h"
/* Number of external interrupt lines to configure the GIC with */
#define NUM_IRQS 256
......@@ -81,6 +82,7 @@ typedef struct {
MachineState parent;
bool secure;
bool highmem;
int32_t gic_version;
} VirtMachineState;
#define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt")
......@@ -111,6 +113,10 @@ static const MemMapEntry a15memmap[] = {
[VIRT_GIC_DIST] = { 0x08000000, 0x00010000 },
[VIRT_GIC_CPU] = { 0x08010000, 0x00010000 },
[VIRT_GIC_V2M] = { 0x08020000, 0x00001000 },
/* The space in between here is reserved for GICv3 CPU/vCPU/HYP */
[VIRT_GIC_ITS] = { 0x08080000, 0x00020000 },
/* This redistributor space allows up to 2*64kB*123 CPUs */
[VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 },
[VIRT_UART] = { 0x09000000, 0x00001000 },
[VIRT_RTC] = { 0x09010000, 0x00001000 },
[VIRT_FW_CFG] = { 0x09020000, 0x0000000a },
......@@ -255,7 +261,7 @@ static void fdt_add_psci_node(const VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
static void fdt_add_timer_nodes(const VirtBoardInfo *vbi, int gictype)
{
/* Note that on A15 h/w these interrupts are level-triggered,
* but for the GIC implementation provided by both QEMU and KVM
......@@ -264,8 +270,11 @@ static void fdt_add_timer_nodes(const VirtBoardInfo *vbi)
ARMCPU *armcpu;
uint32_t irqflags = GIC_FDT_IRQ_FLAGS_EDGE_LO_HI;
if (gictype == 2) {
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1);
GIC_FDT_IRQ_PPI_CPU_WIDTH,
(1 << vbi->smp_cpus) - 1);
}
qemu_fdt_add_subnode(vbi->fdt, "/timer");
......@@ -355,25 +364,36 @@ static void fdt_add_v2m_gic_node(VirtBoardInfo *vbi)
qemu_fdt_setprop_cell(vbi->fdt, "/intc/v2m", "phandle", vbi->v2m_phandle);
}
static void fdt_add_gic_node(VirtBoardInfo *vbi)
static void fdt_add_gic_node(VirtBoardInfo *vbi, int type)
{
vbi->gic_phandle = qemu_fdt_alloc_phandle(vbi->fdt);
qemu_fdt_setprop_cell(vbi->fdt, "/", "interrupt-parent", vbi->gic_phandle);
qemu_fdt_add_subnode(vbi->fdt, "/intc");
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
if (type == 3) {
qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
"arm,gic-v3");
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
2, vbi->memmap[VIRT_GIC_DIST].base,
2, vbi->memmap[VIRT_GIC_DIST].size,
2, vbi->memmap[VIRT_GIC_REDIST].base,
2, vbi->memmap[VIRT_GIC_REDIST].size);
} else {
/* 'cortex-a15-gic' means 'GIC v2' */
qemu_fdt_setprop_string(vbi->fdt, "/intc", "compatible",
"arm,cortex-a15-gic");
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#interrupt-cells", 3);
qemu_fdt_setprop(vbi->fdt, "/intc", "interrupt-controller", NULL, 0);
qemu_fdt_setprop_sized_cells(vbi->fdt, "/intc", "reg",
2, vbi->memmap[VIRT_GIC_DIST].base,
2, vbi->memmap[VIRT_GIC_DIST].size,
2, vbi->memmap[VIRT_GIC_CPU].base,
2, vbi->memmap[VIRT_GIC_CPU].size);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#address-cells", 0x2);
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "#size-cells", 0x2);
qemu_fdt_setprop(vbi->fdt, "/intc", "ranges", NULL, 0);
}
qemu_fdt_setprop_cell(vbi->fdt, "/intc", "phandle", vbi->gic_phandle);
}
......@@ -396,18 +416,18 @@ static void create_v2m(VirtBoardInfo *vbi, qemu_irq *pic)
fdt_add_v2m_gic_node(vbi);
}
static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure)
static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, int type, bool secure)
{
/* We create a standalone GIC v2 */
/* We create a standalone GIC */
DeviceState *gicdev;
SysBusDevice *gicbusdev;
const char *gictype;
int i;
gictype = gic_class_name();
gictype = (type == 3) ? gicv3_class_name() : gic_class_name();
gicdev = qdev_create(NULL, gictype);
qdev_prop_set_uint32(gicdev, "revision", 2);
qdev_prop_set_uint32(gicdev, "revision", type);
qdev_prop_set_uint32(gicdev, "num-cpu", smp_cpus);
/* Note that the num-irq property counts both internal and external
* interrupts; there are always 32 of the former (mandated by GIC spec).
......@@ -419,7 +439,11 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure)
qdev_init_nofail(gicdev);
gicbusdev = SYS_BUS_DEVICE(gicdev);
sysbus_mmio_map(gicbusdev, 0, vbi->memmap[VIRT_GIC_DIST].base);
if (type == 3) {
sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_REDIST].base);
} else {
sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].base);
}
/* Wire the outputs from each CPU's generic timer to the
* appropriate GIC PPI inputs, and the GIC's IRQ output to
......@@ -454,9 +478,11 @@ static void create_gic(VirtBoardInfo *vbi, qemu_irq *pic, bool secure)
pic[i] = qdev_get_gpio_in(gicdev, i);
}
fdt_add_gic_node(vbi);
fdt_add_gic_node(vbi, type);
if (type == 2) {
create_v2m(vbi, pic);
}
}
static void create_uart(const VirtBoardInfo *vbi, qemu_irq *pic)
......@@ -773,7 +799,10 @@ static void create_pcie(const VirtBoardInfo *vbi, qemu_irq *pic,
qemu_fdt_setprop_cells(vbi->fdt, nodename, "bus-range", 0,
nr_pcie_buses - 1);
qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent", vbi->v2m_phandle);
if (vbi->v2m_phandle) {
qemu_fdt_setprop_cells(vbi->fdt, nodename, "msi-parent",
vbi->v2m_phandle);
}
qemu_fdt_setprop_sized_cells(vbi->fdt, nodename, "reg",
2, base_ecam, 2, size_ecam);
......@@ -888,6 +917,7 @@ static void machvirt_init(MachineState *machine)
VirtMachineState *vms = VIRT_MACHINE(machine);
qemu_irq pic[NUM_IRQS];
MemoryRegion *sysmem = get_system_memory();
int gic_version = vms->gic_version;
int n;
MemoryRegion *ram = g_new(MemoryRegion, 1);
const char *cpu_model = machine->cpu_model;
......@@ -900,6 +930,18 @@ static void machvirt_init(MachineState *machine)
cpu_model = "cortex-a15";
}
/* We can probe only here because during property set
* KVM is not available yet
*/
if (!gic_version) {
gic_version = kvm_arm_vgic_probe();
if (!gic_version) {
error_report("Unable to determine GIC version supported by host\n"
"Probably KVM acceleration is not supported\n");
exit(1);
}
}
/* Separate the actual CPU model name from any appended features */
cpustr = g_strsplit(cpu_model, ",", 2);
......@@ -960,7 +1002,7 @@ static void machvirt_init(MachineState *machine)
object_property_set_bool(cpuobj, true, "realized", NULL);
}
g_strfreev(cpustr);
fdt_add_timer_nodes(vbi);
fdt_add_timer_nodes(vbi, gic_version);
fdt_add_cpu_nodes(vbi);
fdt_add_psci_node(vbi);
......@@ -970,7 +1012,7 @@ static void machvirt_init(MachineState *machine)
create_flash(vbi);
create_gic(vbi, pic, vms->secure);
create_gic(vbi, pic, gic_version, vms->secure);
create_uart(vbi, pic);
......@@ -992,6 +1034,7 @@ static void machvirt_init(MachineState *machine)
guest_info->memmap = vbi->memmap;
guest_info->irqmap = vbi->irqmap;
guest_info->use_highmem = vms->highmem;
guest_info->gic_version = gic_version;
guest_info_state->machine_done.notify = virt_guest_info_machine_done;
qemu_add_machine_init_done_notifier(&guest_info_state->machine_done);
......@@ -1043,6 +1086,31 @@ static void virt_set_highmem(Object *obj, bool value, Error **errp)
vms->highmem = value;
}
static char *virt_get_gic_version(Object *obj, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
const char *val = vms->gic_version == 3 ? "3" : "2";
return g_strdup(val);
}
static void virt_set_gic_version(Object *obj, const char *value, Error **errp)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
if (!strcmp(value, "3")) {
vms->gic_version = 3;
} else if (!strcmp(value, "2")) {
vms->gic_version = 2;
} else if (!strcmp(value, "host")) {
vms->gic_version = 0; /* Will probe later */
} else {
error_report("Invalid gic-version option value\n"
"Allowed values are: 3, 2, host\n");
exit(1);
}
}
static void virt_instance_init(Object *obj)
{
VirtMachineState *vms = VIRT_MACHINE(obj);
......@@ -1067,6 +1135,13 @@ static void virt_instance_init(Object *obj)
"Set on/off to enable/disable using "
"physical address space above 32 bits",
NULL);
/* Default GIC type is v2 */
vms->gic_version = 2;
object_property_add_str(obj, "gic-version", virt_get_gic_version,
virt_set_gic_version, NULL);
object_property_set_description(obj, "gic-version",
"Set GIC version. "
"Valid values are 2, 3 and host", NULL);
}
static void virt_class_init(ObjectClass *oc, void *data)
......@@ -1075,7 +1150,10 @@ static void virt_class_init(ObjectClass *oc, void *data)
mc->desc = "ARM Virtual Machine",
mc->init = machvirt_init;
mc->max_cpus = 8;
/* Our maximum number of CPUs depends on how many redistributors
* we can fit into memory map
*/
mc->max_cpus = a15memmap[VIRT_GIC_REDIST].size / 0x20000;
mc->has_dynamic_sysbus = true;
mc->block_default_type = IF_VIRTIO;
mc->no_cdrom = 1;
......
......@@ -12,10 +12,12 @@ common-obj-$(CONFIG_IOAPIC) += ioapic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic_common.o
common-obj-$(CONFIG_ARM_GIC) += arm_gic.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv2m.o
common-obj-$(CONFIG_ARM_GIC) += arm_gicv3_common.o
common-obj-$(CONFIG_OPENPIC) += openpic.o
obj-$(CONFIG_APIC) += apic.o apic_common.o
obj-$(CONFIG_ARM_GIC_KVM) += arm_gic_kvm.o
obj-$(call land,$(CONFIG_ARM_GIC_KVM),$(TARGET_AARCH64)) += arm_gicv3_kvm.o
obj-$(CONFIG_STELLARIS) += armv7m_nvic.o
obj-$(CONFIG_EXYNOS4) += exynos4210_gic.o exynos4210_combiner.o
obj-$(CONFIG_GRLIB) += grlib_irqmp.o
......
......@@ -23,6 +23,7 @@
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "gic_internal.h"
#include "vgic_common.h"
//#define DEBUG_GIC_KVM
......@@ -52,7 +53,7 @@ typedef struct KVMARMGICClass {
void (*parent_reset)(DeviceState *dev);
} KVMARMGICClass;
static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level)
{
/* Meaning of the 'irq' parameter:
* [0..N-1] : external interrupts
......@@ -63,10 +64,9 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
* has separate fields in the irq number for type,
* CPU number and interrupt number.
*/
GICState *s = (GICState *)opaque;
int kvm_irq, irqtype, cpu;
if (irq < (s->num_irq - GIC_INTERNAL)) {
if (irq < (num_irq - GIC_INTERNAL)) {
/* External interrupt. The kernel numbers these like the GIC
* hardware, with external interrupt IDs starting after the
* internal ones.
......@@ -77,7 +77,7 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
} else {
/* Internal interrupt: decode into (cpu, interrupt id) */
irqtype = KVM_ARM_IRQ_TYPE_PPI;
irq -= (s->num_irq - GIC_INTERNAL);
irq -= (num_irq - GIC_INTERNAL);
cpu = irq / GIC_INTERNAL;
irq %= GIC_INTERNAL;
}
......@@ -87,69 +87,36 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
kvm_set_irq(kvm_state, kvm_irq, !!level);
}
static bool kvm_arm_gic_can_save_restore(GICState *s)
static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
{
return s->dev_fd >= 0;
}
static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum)
{
struct kvm_device_attr attr = {
.group = group,
.attr = attrnum,
.flags = 0,
};
if (s->dev_fd == -1) {
return false;
}
GICState *s = (GICState *)opaque;
return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0;
kvm_arm_gic_set_irq(s->num_irq, irq, level);
}
static void kvm_gic_access(GICState *s, int group, int offset,
int cpu, uint32_t *val, bool write)
static bool kvm_arm_gic_can_save_restore(GICState *s)
{
struct kvm_device_attr attr;
int type;
int err;
cpu = cpu & 0xff;
attr.flags = 0;
attr.group = group;
attr.attr = (((uint64_t)cpu << KVM_DEV_ARM_VGIC_CPUID_SHIFT) &
KVM_DEV_ARM_VGIC_CPUID_MASK) |
(((uint64_t)offset << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) &
KVM_DEV_ARM_VGIC_OFFSET_MASK);
attr.addr = (uintptr_t)val;
if (write) {
type = KVM_SET_DEVICE_ATTR;
} else {
type = KVM_GET_DEVICE_ATTR;
}
err = kvm_device_ioctl(s->dev_fd, type, &attr);
if (err < 0) {
fprintf(stderr, "KVM_{SET/GET}_DEVICE_ATTR failed: %s\n",
strerror(-err));
abort();
}
return s->dev_fd >= 0;
}
#define KVM_VGIC_ATTR(offset, cpu) \
((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \
KVM_DEV_ARM_VGIC_CPUID_MASK) | \
(((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \
KVM_DEV_ARM_VGIC_OFFSET_MASK))
static void kvm_gicd_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
offset, cpu, val, write);
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
KVM_VGIC_ATTR(offset, cpu), val, write);
}
static void kvm_gicc_access(GICState *s, int offset, int cpu,
uint32_t *val, bool write)
{
kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
offset, cpu, val, write);
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
KVM_VGIC_ATTR(offset, cpu), val, write);
}
#define for_each_irq_reg(_ctr, _max_irq, _field_width) \
......@@ -559,7 +526,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
return;
}
gic_init_irqs_and_mmio(s, kvm_arm_gic_set_irq, NULL);
gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL);
for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
qemu_irq irq = qdev_get_gpio_in(dev, i);
......@@ -571,21 +538,22 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp)
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
if (ret >= 0) {
s->dev_fd = ret;
} else if (ret != -ENODEV && ret != -ENOTSUP) {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
return;
}
if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
/* Newstyle API is used, we may have attributes */
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0)) {
uint32_t numirqs = s->num_irq;
kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, 0, &numirqs, 1);
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0,
&numirqs, true);
}
/* Tell the kernel to complete VGIC initialization now */
if (kvm_gic_supports_attr(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT)) {
kvm_gic_access(s, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, 0, 0, 1);
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
}
} else if (ret != -ENODEV && ret != -ENOTSUP) {
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
return;
}
/* Distributor */
......
/*
* ARM GICv3 support - common bits of emulated and KVM kernel model
*
* Copyright (c) 2012 Linaro Limited
* Copyright (c) 2015 Huawei.
* Written by Peter Maydell
* Extended to 64 cores by Shlomo Pongratz
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/intc/arm_gicv3_common.h"
static void gicv3_pre_save(void *opaque)
{
GICv3State *s = (GICv3State *)opaque;
ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
if (c->pre_save) {
c->pre_save(s);
}
}
static int gicv3_post_load(void *opaque, int version_id)
{
GICv3State *s = (GICv3State *)opaque;
ARMGICv3CommonClass *c = ARM_GICV3_COMMON_GET_CLASS(s);
if (c->post_load) {
c->post_load(s);
}
return 0;
}
static const VMStateDescription vmstate_gicv3 = {
.name = "arm_gicv3",
.unmigratable = 1,
.pre_save = gicv3_pre_save,
.post_load = gicv3_post_load,
};
void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
const MemoryRegionOps *ops)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(s);
int i;
/* For the GIC, also expose incoming GPIO lines for PPIs for each CPU.
* GPIO array layout is thus:
* [0..N-1] spi
* [N..N+31] PPIs for CPU 0
* [N+32..N+63] PPIs for CPU 1
* ...
*/
i = s->num_irq - GIC_INTERNAL + GIC_INTERNAL * s->num_cpu;
qdev_init_gpio_in(DEVICE(s), handler, i);
s->parent_irq = g_malloc(s->num_cpu * sizeof(qemu_irq));
s->parent_fiq = g_malloc(s->num_cpu * sizeof(qemu_irq));
for (i = 0; i < s->num_cpu; i++) {
sysbus_init_irq(sbd, &s->parent_irq[i]);
}
for (i = 0; i < s->num_cpu; i++) {
sysbus_init_irq(sbd, &s->parent_fiq[i]);
}
memory_region_init_io(&s->iomem_dist, OBJECT(s), ops, s,
"gicv3_dist", 0x10000);
memory_region_init_io(&s->iomem_redist, OBJECT(s), ops ? &ops[1] : NULL, s,
"gicv3_redist", 0x20000 * s->num_cpu);
sysbus_init_mmio(sbd, &s->iomem_dist);
sysbus_init_mmio(sbd, &s->iomem_redist);
}
static void arm_gicv3_common_realize(DeviceState *dev, Error **errp)
{
GICv3State *s = ARM_GICV3_COMMON(dev);
/* revision property is actually reserved and currently used only in order
* to keep the interface compatible with GICv2 code, avoiding extra
* conditions. However, in future it could be used, for example, if we
* implement GICv4.
*/
if (s->revision != 3) {
error_setg(errp, "unsupported GIC revision %d", s->revision);
return;
}
}
static void arm_gicv3_common_reset(DeviceState *dev)
{
/* TODO */
}
static Property arm_gicv3_common_properties[] = {
DEFINE_PROP_UINT32("num-cpu", GICv3State, num_cpu, 1),
DEFINE_PROP_UINT32("num-irq", GICv3State, num_irq, 32),
DEFINE_PROP_UINT32("revision", GICv3State, revision, 3),
DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0),
DEFINE_PROP_END_OF_LIST(),
};
static void arm_gicv3_common_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->reset = arm_gicv3_common_reset;
dc->realize = arm_gicv3_common_realize;
dc->props = arm_gicv3_common_properties;
dc->vmsd = &vmstate_gicv3;
}
static const TypeInfo arm_gicv3_common_type = {
.name = TYPE_ARM_GICV3_COMMON,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(GICv3State),
.class_size = sizeof(ARMGICv3CommonClass),
.class_init = arm_gicv3_common_class_init,
.abstract = true,
};
static void register_types(void)
{
type_register_static(&arm_gicv3_common_type);
}
type_init(register_types)
/*
* ARM Generic Interrupt Controller using KVM in-kernel support
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Written by Pavel Fedin
* Based on vGICv2 code by Peter Maydell
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/intc/arm_gicv3_common.h"
#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "vgic_common.h"
#ifdef DEBUG_GICV3_KVM
#define DPRINTF(fmt, ...) \
do { fprintf(stderr, "kvm_gicv3: " fmt, ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) \
do { } while (0)
#endif
#define TYPE_KVM_ARM_GICV3 "kvm-arm-gicv3"
#define KVM_ARM_GICV3(obj) \
OBJECT_CHECK(GICv3State, (obj), TYPE_KVM_ARM_GICV3)
#define KVM_ARM_GICV3_CLASS(klass) \
OBJECT_CLASS_CHECK(KVMARMGICv3Class, (klass), TYPE_KVM_ARM_GICV3)
#define KVM_ARM_GICV3_GET_CLASS(obj) \
OBJECT_GET_CLASS(KVMARMGICv3Class, (obj), TYPE_KVM_ARM_GICV3)
typedef struct KVMARMGICv3Class {
ARMGICv3CommonClass parent_class;
DeviceRealize parent_realize;
void (*parent_reset)(DeviceState *dev);
} KVMARMGICv3Class;
static void kvm_arm_gicv3_set_irq(void *opaque, int irq, int level)
{
GICv3State *s = (GICv3State *)opaque;
kvm_arm_gic_set_irq(s->num_irq, irq, level);
}
static void kvm_arm_gicv3_put(GICv3State *s)
{
/* TODO */
DPRINTF("Cannot put kernel gic state, no kernel interface\n");
}
static void kvm_arm_gicv3_get(GICv3State *s)
{
/* TODO */
DPRINTF("Cannot get kernel gic state, no kernel interface\n");
}
static void kvm_arm_gicv3_reset(DeviceState *dev)
{
GICv3State *s = ARM_GICV3_COMMON(dev);
KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
DPRINTF("Reset\n");
kgc->parent_reset(dev);
kvm_arm_gicv3_put(s);
}
static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
{
GICv3State *s = KVM_ARM_GICV3(dev);
KVMARMGICv3Class *kgc = KVM_ARM_GICV3_GET_CLASS(s);
Error *local_err = NULL;
DPRINTF("kvm_arm_gicv3_realize\n");
kgc->parent_realize(dev, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
if (s->security_extn) {
error_setg(errp, "the in-kernel VGICv3 does not implement the "
"security extensions");
return;
}
gicv3_init_irqs_and_mmio(s, kvm_arm_gicv3_set_irq, NULL);
/* Try to create the device via the device control API */
s->dev_fd = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V3, false);
if (s->dev_fd < 0) {
error_setg_errno(errp, -s->dev_fd, "error creating in-kernel VGIC");
return;
}
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
0, &s->num_irq, true);
/* Tell the kernel to complete VGIC initialization now */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true);
kvm_arm_register_device(&s->iomem_dist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_DIST, s->dev_fd);
kvm_arm_register_device(&s->iomem_redist, -1, KVM_DEV_ARM_VGIC_GRP_ADDR,
KVM_VGIC_V3_ADDR_TYPE_REDIST, s->dev_fd);
}
static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass);
KVMARMGICv3Class *kgc = KVM_ARM_GICV3_CLASS(klass);
agcc->pre_save = kvm_arm_gicv3_get;
agcc->post_load = kvm_arm_gicv3_put;
kgc->parent_realize = dc->realize;
kgc->parent_reset = dc->reset;
dc->realize = kvm_arm_gicv3_realize;
dc->reset = kvm_arm_gicv3_reset;
}
static const TypeInfo kvm_arm_gicv3_info = {
.name = TYPE_KVM_ARM_GICV3,
.parent = TYPE_ARM_GICV3_COMMON,
.instance_size = sizeof(GICv3State),
.class_init = kvm_arm_gicv3_class_init,
.class_size = sizeof(KVMARMGICv3Class),
};
static void kvm_arm_gicv3_register_types(void)
{
type_register_static(&kvm_arm_gicv3_info);
}
type_init(kvm_arm_gicv3_register_types)
/*
* ARM KVM vGIC utility functions
*
* Copyright (c) 2015 Samsung Electronics
* Written by Pavel Fedin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef QEMU_ARM_VGIC_COMMON_H
#define QEMU_ARM_VGIC_COMMON_H
/**
* kvm_arm_gic_set_irq - Send an IRQ to the in-kernel vGIC
* @num_irq: Total number of IRQs configured for the GIC instance
* @irq: qemu internal IRQ line number:
* [0..N-1] : external interrupts
* [N..N+31] : PPI (internal) interrupts for CPU 0
* [N+32..N+63] : PPI (internal interrupts for CPU 1
* @level: level of the IRQ line.
*/
void kvm_arm_gic_set_irq(uint32_t num_irq, int irq, int level);
#endif
......@@ -384,6 +384,15 @@ struct AcpiMadtGenericMsiFrame {
typedef struct AcpiMadtGenericMsiFrame AcpiMadtGenericMsiFrame;
struct AcpiMadtGenericRedistributor {
ACPI_SUB_HEADER_DEF
uint16_t reserved;
uint64_t base_address;
uint32_t range_length;
} QEMU_PACKED;
typedef struct AcpiMadtGenericRedistributor AcpiMadtGenericRedistributor;
/*
* Generic Timer Description Table (GTDT)
*/
......
......@@ -32,6 +32,7 @@ typedef struct VirtGuestInfo {
const MemMapEntry *memmap;
const int *irqmap;
bool use_highmem;
int gic_version;
} VirtGuestInfo;
......
......@@ -46,6 +46,9 @@ enum {
VIRT_CPUPERIPHS,
VIRT_GIC_DIST,
VIRT_GIC_CPU,
VIRT_GIC_V2M,
VIRT_GIC_ITS,
VIRT_GIC_REDIST,
VIRT_UART,
VIRT_MMIO,
VIRT_RTC,
......@@ -54,7 +57,6 @@ enum {
VIRT_PCIE_MMIO,
VIRT_PCIE_PIO,
VIRT_PCIE_ECAM,
VIRT_GIC_V2M,
VIRT_PLATFORM_BUS,
VIRT_PCIE_MMIO_HIGH,
};
......
/*
* ARM GIC support
*
* Copyright (c) 2012 Linaro Limited
* Copyright (c) 2015 Huawei.
* Written by Peter Maydell
* Extended to 64 cores by Shlomo Pongratz
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_ARM_GICV3_COMMON_H
#define HW_ARM_GICV3_COMMON_H
#include "hw/sysbus.h"
#include "hw/intc/arm_gic_common.h"
typedef struct GICv3State {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
qemu_irq *parent_irq;
qemu_irq *parent_fiq;
MemoryRegion iomem_dist; /* Distributor */
MemoryRegion iomem_redist; /* Redistributors */
uint32_t num_cpu;
uint32_t num_irq;
uint32_t revision;
bool security_extn;
int dev_fd; /* kvm device fd if backed by kvm vgic support */
} GICv3State;
#define TYPE_ARM_GICV3_COMMON "arm-gicv3-common"
#define ARM_GICV3_COMMON(obj) \
OBJECT_CHECK(GICv3State, (obj), TYPE_ARM_GICV3_COMMON)
#define ARM_GICV3_COMMON_CLASS(klass) \
OBJECT_CLASS_CHECK(ARMGICv3CommonClass, (klass), TYPE_ARM_GICV3_COMMON)
#define ARM_GICV3_COMMON_GET_CLASS(obj) \
OBJECT_GET_CLASS(ARMGICv3CommonClass, (obj), TYPE_ARM_GICV3_COMMON)
typedef struct ARMGICv3CommonClass {
/*< private >*/
SysBusDeviceClass parent_class;
/*< public >*/
void (*pre_save)(GICv3State *s);
void (*post_load)(GICv3State *s);
} ARMGICv3CommonClass;
void gicv3_init_irqs_and_mmio(GICv3State *s, qemu_irq_handler handler,
const MemoryRegionOps *ops);
#endif
......@@ -239,6 +239,32 @@ int kvm_device_ioctl(int fd, int type, ...);
*/
int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr);
/**
* kvm_device_check_attr - check for existence of a specific device attribute
* @fd: The device file descriptor
* @group: the group
* @attr: the attribute of that group to query for
*
* Returns: 1 if the attribute exists
* 0 if the attribute either does not exist or if the vm device
* interface is unavailable
*/
int kvm_device_check_attr(int fd, uint32_t group, uint64_t attr);
/**
* kvm_device_access - set or get value of a specific vm attribute
* @fd: The device file descriptor
* @group: the group
* @attr: the attribute of that group to set or get
* @val: pointer to a storage area for the value
* @write: true for set and false for get operation
*
* This function is not allowed to fail. Use kvm_device_check_attr()
* in order to check for the availability of optional attributes.
*/
void kvm_device_access(int fd, int group, uint64_t attr,
void *val, bool write);
/**
* kvm_create_device - create a KVM device for the device control API
* @KVMState: The KVMState pointer
......
......@@ -24,6 +24,7 @@
#include "qemu/atomic.h"
#include "qemu/option.h"
#include "qemu/config-file.h"
#include "qemu/error-report.h"
#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/s390x/adapter.h"
......@@ -2008,6 +2009,39 @@ int kvm_vm_check_attr(KVMState *s, uint32_t group, uint64_t attr)
return ret ? 0 : 1;
}
int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr)
{
struct kvm_device_attr attribute = {
.group = group,
.attr = attr,
.flags = 0,
};
return kvm_device_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute) ? 0 : 1;
}
void kvm_device_access(int fd, int group, uint64_t attr,
void *val, bool write)
{
struct kvm_device_attr kvmattr;
int err;
kvmattr.flags = 0;
kvmattr.group = group;
kvmattr.attr = attr;
kvmattr.addr = (uintptr_t)val;
err = kvm_device_ioctl(fd,
write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR,
&kvmattr);
if (err < 0) {
error_report("KVM_%s_DEVICE_ATTR failed: %s\n"
"Group %d attr 0x%016" PRIx64, write ? "SET" : "GET",
strerror(-err), group, attr);
abort();
}
}
int kvm_has_sync_mmu(void)
{
return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
......
......@@ -585,18 +585,23 @@ void kvm_arch_init_irq_routing(KVMState *s)
int kvm_arch_irqchip_create(KVMState *s)
{
int ret;
/* If we can create the VGIC using the newer device control API, we
* let the device do this when it initializes itself, otherwise we
* fall back to the old API */
return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL);
}
ret = kvm_create_device(s, KVM_DEV_TYPE_ARM_VGIC_V2, true);
if (ret == 0) {
return 1;
}
int kvm_arm_vgic_probe(void)
{
if (kvm_create_device(kvm_state,
KVM_DEV_TYPE_ARM_VGIC_V3, true) == 0) {
return 3;
} else if (kvm_create_device(kvm_state,
KVM_DEV_TYPE_ARM_VGIC_V2, true) == 0) {
return 2;
} else {
return 0;
}
}
int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route,
......
......@@ -189,6 +189,15 @@ int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu);
*/
int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu);
int kvm_arm_vgic_probe(void);
#else
static inline int kvm_arm_vgic_probe(void)
{
return 0;
}
#endif
static inline const char *gic_class_name(void)
......@@ -196,4 +205,14 @@ static inline const char *gic_class_name(void)
return kvm_irqchip_in_kernel() ? "kvm-arm-gic" : "arm_gic";
}
/**
* gicv3_class_name
*
* Return name of GICv3 class to use depending on whether KVM acceleration is
* in use. May throw an error if the chosen implementation is not available.
*
* Returns: class name to use
*/
const char *gicv3_class_name(void);
#endif
#include "hw/hw.h"
#include "hw/boards.h"
#include "qemu/error-report.h"
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "internals.h"
......@@ -328,3 +329,20 @@ const VMStateDescription vmstate_arm_cpu = {
NULL
}
};
const char *gicv3_class_name(void)
{
if (kvm_irqchip_in_kernel()) {
#ifdef TARGET_AARCH64
return "kvm-arm-gicv3";
#else
error_report("KVM GICv3 acceleration is not supported on this "
"platform\n");
#endif
} else {
/* TODO: Software emulation is not implemented yet */
error_report("KVM is currently required for GICv3 emulation\n");
}
exit(1);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册