Skip to content

Commit

Permalink
intc/gic: Extract some reusable vGIC code
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
pfedin authored and pm215 committed Sep 24, 2015
1 parent ff8f06e commit 4b3cfe7
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 65 deletions.
98 changes: 33 additions & 65 deletions hw/intc/arm_gic_kvm.c
Expand Up @@ -23,6 +23,7 @@
#include "sysemu/kvm.h"
#include "kvm_arm.h"
#include "gic_internal.h"
#include "vgic_common.h"

//#define DEBUG_GIC_KVM

Expand Down Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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;
}
Expand All @@ -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)
{
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,
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) \
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down
35 changes: 35 additions & 0 deletions hw/intc/vgic_common.h
@@ -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
26 changes: 26 additions & 0 deletions include/sysemu/kvm.h
Expand Up @@ -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
Expand Down
34 changes: 34 additions & 0 deletions kvm-all.c
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 4b3cfe7

Please sign in to comment.