mirror of
https://github.com/xemu-project/xemu.git
synced 2024-11-24 12:09:58 +00:00
target-arm queue:
* support VGICv3 in KVM * fix bug in ACPI table entries for flash devices in virt board * update Allwinner entry in MAINTAINERS -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWA0QDAAoJEDwlJe0UNgzeO08P+QHd7Qah8Q+h44A0NCGzz2KT +vqTYRYXdjSRs+jfJQERx6avP3hkNxM9BHuJgKkyjYE4dOvnBfgP4oW0wp+kOJKx Jqov7fp/1ptGBc42AyxkGm2fyIBH+4wXN8/FAOwOqXJPOBbRxu4sddS0oRIx57wp HrwuDg2sZBNJQxbq7Et1DT7l1tEN9MYNooODNW+VTkjlnETsQIYdUZzqPT07diw6 dAB085RGPJj7SHd6vBLRlhH0PhnMJ7Ivn8I9xnmqL6ROBa08zZEi5ocdUeDaFW+G UKn9m04ZpvyVutvCuqBE6yJKG198PV1EE9EA4fbNYbMPtQGAlKIxqev+DJiKwmgB iL8+YaoDjI1T6pzcLeRKBmuSZcHocFSljfvOEgM9gkGUQ8V6jzOfEpeEBHtcT09y aELCHbxE+PNDVqW3CgtACvJSgtoGNE5MPkjrmjK4QI2OQgvqVKfk1MfW3ivfiVQc iRbwkvPqCcU+B6fY8PbxP9v4Hf6YXAoBJqnCsFeK7YEapKpxx8nF7AeCO7OcvaDZ zSHwWl6/MivzzQd0CQIutcEjiN0r+FZGKIxvLwcbomyTRTtWYb26MH9KqBzOGCB4 itZXO0sF9Ot77aQOGu3l95C9CO5aQumUkWQNlQvktGRHtZPhfc/uf/VBhofuJBMl Q80N/N8hvwOW8DInC+Yx =iLxN -----END PGP SIGNATURE----- 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: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
eb9d0ea063
@ -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,33 +443,43 @@ build_madt(GArray *table_data, GArray *linker, VirtGuestInfo *guest_info,
|
||||
|
||||
madt = acpi_data_push(table_data, sizeof *madt);
|
||||
|
||||
for (i = 0; i < guest_info->smp_cpus; i++) {
|
||||
AcpiMadtGenericInterrupt *gicc = acpi_data_push(table_data,
|
||||
sizeof *gicc);
|
||||
gicc->type = ACPI_APIC_GENERIC_INTERRUPT;
|
||||
gicc->length = sizeof(*gicc);
|
||||
gicc->base_address = memmap[VIRT_GIC_CPU].base;
|
||||
gicc->cpu_interface_number = i;
|
||||
gicc->arm_mpidr = i;
|
||||
gicc->uid = i;
|
||||
if (test_bit(i, cpuinfo->found_cpus)) {
|
||||
gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
gic_msi->gic_msi_frame_id = 0;
|
||||
gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base);
|
||||
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);
|
||||
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);
|
||||
gicc->type = ACPI_APIC_GENERIC_INTERRUPT;
|
||||
gicc->length = sizeof(*gicc);
|
||||
gicc->base_address = memmap[VIRT_GIC_CPU].base;
|
||||
gicc->cpu_interface_number = i;
|
||||
gicc->arm_mpidr = i;
|
||||
gicc->uid = i;
|
||||
if (test_bit(i, cpuinfo->found_cpus)) {
|
||||
gicc->flags = cpu_to_le32(ACPI_GICC_ENABLED);
|
||||
}
|
||||
}
|
||||
|
||||
gic_msi = acpi_data_push(table_data, sizeof *gic_msi);
|
||||
gic_msi->type = ACPI_APIC_GENERIC_MSI_FRAME;
|
||||
gic_msi->length = sizeof(*gic_msi);
|
||||
gic_msi->gic_msi_frame_id = 0;
|
||||
gic_msi->base_address = cpu_to_le64(memmap[VIRT_GIC_V2M].base);
|
||||
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",
|
||||
|
124
hw/arm/virt.c
124
hw/arm/virt.c
@ -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;
|
||||
|
||||
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
|
||||
GIC_FDT_IRQ_PPI_CPU_WIDTH, (1 << vbi->smp_cpus) - 1);
|
||||
if (gictype == 2) {
|
||||
irqflags = deposit32(irqflags, GIC_FDT_IRQ_PPI_CPU_START,
|
||||
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");
|
||||
/* '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);
|
||||
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_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", "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);
|
||||
sysbus_mmio_map(gicbusdev, 1, vbi->memmap[VIRT_GIC_CPU].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);
|
||||
|
||||
create_v2m(vbi, pic);
|
||||
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 void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
|
||||
{
|
||||
GICState *s = (GICState *)opaque;
|
||||
|
||||
kvm_arm_gic_set_irq(s->num_irq, irq, level);
|
||||
}
|
||||
|
||||
static bool kvm_arm_gic_can_save_restore(GICState *s)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return kvm_device_ioctl(s->dev_fd, KVM_HAS_DEVICE_ATTR, &attr) == 0;
|
||||
}
|
||||
|
||||
static void kvm_gic_access(GICState *s, int group, int offset,
|
||||
int cpu, uint32_t *val, bool write)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
#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,23 +538,24 @@ 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;
|
||||
|
||||
/* 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_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_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT)) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (kvm_gic_supports_attr(s, 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);
|
||||
}
|
||||
|
||||
/* Tell the kernel to complete VGIC initialization now */
|
||||
if (kvm_gic_supports_attr(s, 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);
|
||||
}
|
||||
|
||||
/* Distributor */
|
||||
kvm_arm_register_device(&s->iomem,
|
||||
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|
||||
|
140
hw/intc/arm_gicv3_common.c
Normal file
140
hw/intc/arm_gicv3_common.c
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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)
|
149
hw/intc/arm_gicv3_kvm.c
Normal file
149
hw/intc/arm_gicv3_kvm.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* 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)
|
35
hw/intc/vgic_common.h
Normal file
35
hw/intc/vgic_common.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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,
|
||||
};
|
||||
|
68
include/hw/intc/arm_gicv3_common.h
Normal file
68
include/hw/intc/arm_gicv3_common.h
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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
|
||||
|
34
kvm-all.c
34
kvm-all.c
@ -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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -1,5 +1,6 @@
|
||||
#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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user