@@ -298,7 +298,7 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
AssignedDevice *r_dev = DO_UPCAST(AssignedDevice, dev, pci_dev);
AssignedDevRegion *region = &r_dev->v_addrs[region_num];
int first_map = (region->e_size == 0);
- CPUState *env;
+ int r;
region->e_physbase = addr;
region->e_size = size;
@@ -307,17 +307,11 @@ static void assigned_dev_ioport_map(PCIDevice *pci_dev, int region_num,
addr, region->u.r_baseport, type, size, region_num);
if (first_map && region->region->resource_fd < 0) {
- struct ioperm_data *data;
-
- data = qemu_mallocz(sizeof(struct ioperm_data));
- data->start_port = region->u.r_baseport;
- data->num = region->r_size;
- data->turn_on = 1;
-
- kvm_add_ioperm_data(data);
-
- for (env = first_cpu; env; env = env->next_cpu)
- kvm_ioperm(env, data);
+ r = kvm_add_ioport_region(region->u.r_baseport, region->r_size);
+ if (r < 0) {
+ fprintf(stderr, "%s: failed to enable ioport access (%m)\n",
+ __func__);
+ }
}
register_ioport_read(addr, size, 1, assigned_dev_ioport_readb,
@@ -832,7 +826,7 @@ static void free_assigned_device(AssignedDevice *dev)
}
if (pci_region->type & IORESOURCE_IO) {
if (pci_region->resource_fd < 0) {
- kvm_remove_ioperm_data(region->u.r_baseport, region->r_size);
+ kvm_remove_ioport_region(region->u.r_baseport, region->r_size);
}
} else if (pci_region->type & IORESOURCE_MEM) {
if (region->u.r_virtbase) {
@@ -164,14 +164,18 @@ static int _kvm_arch_init_vcpu(CPUState *env)
#ifdef KVM_EXIT_TPR_ACCESS
kvm_enable_tpr_access_reporting(env);
#endif
- return 0;
+
+ return kvm_update_ioport_access(env);
}
#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
-void kvm_arch_do_ioperm(void *_data)
+int kvm_arch_set_ioport_access(unsigned long start, unsigned long size,
+ bool enable)
{
- struct ioperm_data *data = _data;
- ioperm(data->start_port, data->num, data->turn_on);
+ if (ioperm(start, size, enable) < 0) {
+ return -errno;
+ }
+ return 0;
}
#endif
@@ -66,11 +66,6 @@ static int qemu_system_ready;
CPUState *kvm_debug_cpu_requested;
-#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
-/* The list of ioperm_data */
-static QLIST_HEAD(, ioperm_data) ioperm_head;
-#endif
-
#define ALIGN(x, y) (((x)+(y)-1) & ~((y)-1))
static inline void set_gsi(KVMState *s, unsigned int gsi)
@@ -856,19 +851,10 @@ static int kvm_main_loop_cpu(CPUState *env)
static void *ap_main_loop(void *_env)
{
CPUState *env = _env;
-#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
- struct ioperm_data *data = NULL;
-#endif
current_env = env;
env->thread_id = kvm_get_thread_id();
-#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
- /* do ioperm for io ports of assigned devices */
- QLIST_FOREACH(data, &ioperm_head, entries)
- on_vcpu(env, kvm_arch_do_ioperm, data);
-#endif
-
pthread_mutex_lock(&qemu_mutex);
cpu_single_env = env;
@@ -1069,36 +1055,95 @@ void qemu_mutex_lock_iothread(void)
}
#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
-void kvm_add_ioperm_data(struct ioperm_data *data)
+typedef struct KVMIOPortRegion {
+ unsigned long start;
+ unsigned long size;
+ int status;
+ QLIST_ENTRY(KVMIOPortRegion) entry;
+} KVMIOPortRegion;
+
+static QLIST_HEAD(, KVMIOPortRegion) ioport_regions;
+
+static void do_set_ioport_access(void *data)
{
- QLIST_INSERT_HEAD(&ioperm_head, data, entries);
+ KVMIOPortRegion *region = data;
+ bool enable = region->status > 0;
+ int r;
+
+ r = kvm_arch_set_ioport_access(region->start, region->size, enable);
+ if (r < 0) {
+ region->status = r;
+ } else {
+ region->status = 1;
+ }
}
-void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num)
+int kvm_add_ioport_region(unsigned long start, unsigned long size)
{
- struct ioperm_data *data;
-
- data = QLIST_FIRST(&ioperm_head);
- while (data) {
- struct ioperm_data *next = QLIST_NEXT(data, entries);
+ KVMIOPortRegion *region = qemu_mallocz(sizeof(KVMIOPortRegion));
+ CPUState *env;
+ int r = 0;
- if (data->start_port == start_port && data->num == num) {
- QLIST_REMOVE(data, entries);
- qemu_free(data);
+ region->start = start;
+ region->size = size;
+ region->status = 1;
+ QLIST_INSERT_HEAD(&ioport_regions, region, entry);
+
+ if (qemu_system_ready) {
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ on_vcpu(env, do_set_ioport_access, region);
+ if (region->status < 0) {
+ r = region->status;
+ kvm_remove_ioport_region(start, size);
+ break;
+ }
}
-
- data = next;
}
+ return r;
}
-void kvm_ioperm(CPUState *env, void *data)
+int kvm_remove_ioport_region(unsigned long start, unsigned long size)
{
- if (kvm_enabled() && qemu_system_ready) {
- on_vcpu(env, kvm_arch_do_ioperm, data);
+ KVMIOPortRegion *region, *tmp;
+ CPUState *env;
+ int r = -ENOENT;
+
+ QLIST_FOREACH_SAFE(region, &ioport_regions, entry, tmp) {
+ if (region->start == start && region->size == size) {
+ region->status = 0;
+ }
+ if (qemu_system_ready) {
+ for (env = first_cpu; env != NULL; env = env->next_cpu) {
+ on_vcpu(env, do_set_ioport_access, region);
+ }
+ }
+ QLIST_REMOVE(region, entry);
+ qemu_free(region);
+ r = 0;
}
+ return r;
}
+#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */
-#endif
+int kvm_update_ioport_access(CPUState *env)
+{
+#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
+ KVMIOPortRegion *region;
+ int r;
+
+ assert(qemu_cpu_is_self(env));
+
+ QLIST_FOREACH(region, &ioport_regions, entry) {
+ bool enable = region->status > 0;
+
+ r = kvm_arch_set_ioport_access(region->start, region->size, enable);
+ if (r < 0) {
+ return r;
+ }
+ }
+#endif /* CONFIG_KVM_DEVICE_ASSIGNMENT */
+ return 0;
+}
int kvm_set_boot_cpu_id(KVMState *s, uint32_t id)
{
@@ -282,31 +282,20 @@ void kvm_tpr_access_report(CPUState *env, uint64_t rip, int is_write);
int kvm_arch_init_irq_routing(void);
-#ifdef CONFIG_KVM_DEVICE_ASSIGNMENT
-struct ioperm_data;
+int kvm_add_ioport_region(unsigned long start, unsigned long size);
+int kvm_remove_ioport_region(unsigned long start, unsigned long size);
-void kvm_ioperm(CPUState *env, void *data);
-void kvm_add_ioperm_data(struct ioperm_data *data);
-void kvm_remove_ioperm_data(unsigned long start_port, unsigned long num);
-void kvm_arch_do_ioperm(void *_data);
-#endif
+int kvm_update_ioport_access(CPUState *env);
+int kvm_arch_set_ioport_access(unsigned long start, unsigned long size,
+ bool enable);
#ifdef CONFIG_KVM
-#include "qemu-queue.h"
-
extern int kvm_irqchip;
extern int kvm_pit;
extern int kvm_pit_reinject;
extern int kvm_nested;
extern unsigned int kvm_shadow_memory;
-struct ioperm_data {
- unsigned long start_port;
- unsigned long num;
- int turn_on;
- QLIST_ENTRY(ioperm_data) entries;
-};
-
int kvm_handle_tpr_access(CPUState *env);
#else
Clean up the interface for enabling/disabling direct ioport access for assigned devices. There is now only a register and a deregister service. Both are automatically updating the access on all vcpus. Besides that, there is an update service for newly created VCPUs that applies all currently registered regions. Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com> --- hw/device-assignment.c | 20 +++------ qemu-kvm-x86.c | 12 ++++-- qemu-kvm.c | 107 ++++++++++++++++++++++++++++++++++-------------- qemu-kvm.h | 21 ++------- 4 files changed, 96 insertions(+), 64 deletions(-)