|
@@ -23,6 +23,7 @@
|
|
#include "sysemu/kvm.h"
|
|
#include "sysemu/kvm.h"
|
|
#include "kvm_arm.h"
|
|
#include "kvm_arm.h"
|
|
#include "gic_internal.h"
|
|
#include "gic_internal.h"
|
|
|
|
+#include "vgic_common.h"
|
|
|
|
|
|
//#define DEBUG_GIC_KVM
|
|
//#define DEBUG_GIC_KVM
|
|
|
|
|
|
@@ -52,7 +53,7 @@ typedef struct KVMARMGICClass {
|
|
void (*parent_reset)(DeviceState *dev);
|
|
void (*parent_reset)(DeviceState *dev);
|
|
} KVMARMGICClass;
|
|
} 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:
|
|
/* Meaning of the 'irq' parameter:
|
|
* [0..N-1] : external interrupts
|
|
* [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,
|
|
* has separate fields in the irq number for type,
|
|
* CPU number and interrupt number.
|
|
* CPU number and interrupt number.
|
|
*/
|
|
*/
|
|
- GICState *s = (GICState *)opaque;
|
|
|
|
int kvm_irq, irqtype, cpu;
|
|
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
|
|
/* External interrupt. The kernel numbers these like the GIC
|
|
* hardware, with external interrupt IDs starting after the
|
|
* hardware, with external interrupt IDs starting after the
|
|
* internal ones.
|
|
* internal ones.
|
|
@@ -77,7 +77,7 @@ static void kvm_arm_gic_set_irq(void *opaque, int irq, int level)
|
|
} else {
|
|
} else {
|
|
/* Internal interrupt: decode into (cpu, interrupt id) */
|
|
/* Internal interrupt: decode into (cpu, interrupt id) */
|
|
irqtype = KVM_ARM_IRQ_TYPE_PPI;
|
|
irqtype = KVM_ARM_IRQ_TYPE_PPI;
|
|
- irq -= (s->num_irq - GIC_INTERNAL);
|
|
|
|
|
|
+ irq -= (num_irq - GIC_INTERNAL);
|
|
cpu = irq / GIC_INTERNAL;
|
|
cpu = irq / GIC_INTERNAL;
|
|
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);
|
|
kvm_set_irq(kvm_state, kvm_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)
|
|
|
|
|
|
+static void kvm_arm_gicv2_set_irq(void *opaque, int irq, int level)
|
|
{
|
|
{
|
|
- 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,
|
|
static void kvm_gicd_access(GICState *s, int offset, int cpu,
|
|
uint32_t *val, bool write)
|
|
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,
|
|
static void kvm_gicc_access(GICState *s, int offset, int cpu,
|
|
uint32_t *val, bool write)
|
|
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) \
|
|
#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;
|
|
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++) {
|
|
for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) {
|
|
qemu_irq irq = qdev_get_gpio_in(dev, 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);
|
|
ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_ARM_VGIC_V2, false);
|
|
if (ret >= 0) {
|
|
if (ret >= 0) {
|
|
s->dev_fd = ret;
|
|
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) {
|
|
} else if (ret != -ENODEV && ret != -ENOTSUP) {
|
|
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
|
|
error_setg_errno(errp, -ret, "error creating in-kernel VGIC");
|
|
return;
|
|
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 */
|
|
/* Distributor */
|
|
kvm_arm_register_device(&s->iomem,
|
|
kvm_arm_register_device(&s->iomem,
|
|
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|
|
(KVM_ARM_DEVICE_VGIC_V2 << KVM_ARM_DEVICE_ID_SHIFT)
|