intc/gic: Extract some reusable vGIC code
Some functions previously used only by vGICv2 are useful also for vGICv3 implementation. Untie them from GICState and make accessible from within other modules: - kvm_arm_gic_set_irq() - kvm_gic_supports_attr() - moved to common code and renamed to kvm_device_check_attr() - kvm_gic_access() - turned into GIC-independent kvm_device_access(). Data pointer changed to void * because some GICv3 registers are 64-bit wide Some of these changes are not used right now, but they will be helpful for implementing live migration. Actually kvm_dist_get() and kvm_dist_put() could also be made reusable, but they would require two extra parameters (s->dev_fd and s->num_cpu) as well as lots of typecasts of 's' to DeviceState * and back to GICState *. This makes the code very ugly so i decided to stop at this point. I tried also an approach with making a base class for all possible GICs, but it would contain only three variables (dev_fd, cpu_num and irq_num), and accessing them through the rest of the code would be again tedious (either ugly casts or qemu-style separate object pointer). So i disliked it too. Signed-off-by: Pavel Fedin <p.fedin@samsung.com> Tested-by: Ashok kumar <ashoks@broadcom.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 2ef56d1dd64ffb75ed02a10dcdaf605e5b8ff4f8.1441784344.git.p.fedin@samsung.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
ff8f06ee76
commit
4b3cfe72d9
|
@ -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 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)
|
static bool kvm_arm_gic_can_save_restore(GICState *s)
|
||||||
{
|
{
|
||||||
return s->dev_fd >= 0;
|
return s->dev_fd >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool kvm_gic_supports_attr(GICState *s, int group, int attrnum)
|
#define KVM_VGIC_ATTR(offset, cpu) \
|
||||||
{
|
((((uint64_t)(cpu) << KVM_DEV_ARM_VGIC_CPUID_SHIFT) & \
|
||||||
struct kvm_device_attr attr = {
|
KVM_DEV_ARM_VGIC_CPUID_MASK) | \
|
||||||
.group = group,
|
(((uint64_t)(offset) << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) & \
|
||||||
.attr = attrnum,
|
KVM_DEV_ARM_VGIC_OFFSET_MASK))
|
||||||
.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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||||
offset, cpu, val, write);
|
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,
|
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_REGS,
|
||||||
offset, cpu, val, write);
|
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)
|
||||||
|
|
|
@ -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
|
|
@ -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);
|
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
|
* kvm_create_device - create a KVM device for the device control API
|
||||||
* @KVMState: The KVMState pointer
|
* @KVMState: The KVMState pointer
|
||||||
|
|
34
kvm-all.c
34
kvm-all.c
|
@ -24,6 +24,7 @@
|
||||||
#include "qemu/atomic.h"
|
#include "qemu/atomic.h"
|
||||||
#include "qemu/option.h"
|
#include "qemu/option.h"
|
||||||
#include "qemu/config-file.h"
|
#include "qemu/config-file.h"
|
||||||
|
#include "qemu/error-report.h"
|
||||||
#include "hw/hw.h"
|
#include "hw/hw.h"
|
||||||
#include "hw/pci/msi.h"
|
#include "hw/pci/msi.h"
|
||||||
#include "hw/s390x/adapter.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;
|
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)
|
int kvm_has_sync_mmu(void)
|
||||||
{
|
{
|
||||||
return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
return kvm_check_extension(kvm_state, KVM_CAP_SYNC_MMU);
|
||||||
|
|
Loading…
Reference in New Issue