From d75e2f68890bfea22cb2ea5b2de4657cab87a1da Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 24 Jun 2014 07:43:37 +0300 Subject: [PATCH 01/37] numa: fix comment Fix up English in comments: s/the each/each/ Signed-off-by: Michael S. Tsirkin Reviewed-by: Igor Mammedov --- numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numa.c b/numa.c index e471afe04a..47049a568d 100644 --- a/numa.c +++ b/numa.c @@ -172,7 +172,7 @@ void set_numa_nodes(void) if (i == nb_numa_nodes) { uint64_t usedmem = 0; - /* On Linux, the each node's border has to be 8MB aligned, + /* On Linux, each node's border has to be 8MB aligned, * the final node gets the rest. */ for (i = 0; i < nb_numa_nodes - 1; i++) { From 29923e94e7ed93bac3fcc5e8a31b2f163a412172 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 24 Jun 2014 07:44:30 +0300 Subject: [PATCH 02/37] openrisc: fix comment Fix English in comment: s/the each/each/ s/ \*\// \*\// Signed-off-by: Michael S. Tsirkin Reviewed-by: Igor Mammedov --- target-openrisc/translate.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target-openrisc/translate.c b/target-openrisc/translate.c index b728718b64..55ff935d5e 100644 --- a/target-openrisc/translate.c +++ b/target-openrisc/translate.c @@ -531,14 +531,14 @@ static void dec_calc(DisasContext *dc, uint32_t insn) TCGv_i64 high = tcg_temp_new_i64(); TCGv_i32 sr_ove = tcg_temp_local_new_i32(); int lab = gen_new_label(); - /* Calculate the each result. */ + /* Calculate each result. */ tcg_gen_extu_i32_i64(tra, cpu_R[ra]); tcg_gen_extu_i32_i64(trb, cpu_R[rb]); tcg_gen_mul_i64(result, tra, trb); tcg_temp_free_i64(tra); tcg_temp_free_i64(trb); tcg_gen_shri_i64(high, result, TARGET_LONG_BITS); - /* Overflow or not. */ + /* Overflow or not. */ tcg_gen_brcondi_i64(TCG_COND_EQ, high, 0x00000000, lab); tcg_gen_ori_tl(cpu_sr, cpu_sr, (SR_OV | SR_CY)); tcg_gen_andi_tl(sr_ove, cpu_sr, SR_OVE); From 9851d0fe3592f1b493aa9eb0166fdd4ed9f47430 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Tue, 24 Jun 2014 08:50:30 +0300 Subject: [PATCH 03/37] numa: fix comment s/if given for/is given for/; Reported-by: Hu Tao Signed-off-by: Michael S. Tsirkin --- numa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numa.c b/numa.c index 47049a568d..6c2eae7ef3 100644 --- a/numa.c +++ b/numa.c @@ -161,7 +161,7 @@ void set_numa_nodes(void) nb_numa_nodes = MAX_NODES; } - /* If no memory size if given for any node, assume the default case + /* If no memory size is given for any node, assume the default case * and distribute the available memory equally across all nodes */ for (i = 0; i < nb_numa_nodes; i++) { From b8f5cfd6824ee7122e714ffc7e893432dd7beae5 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 24 Jun 2014 23:04:44 -0300 Subject: [PATCH 04/37] pc: Move q35 compat props to PC_COMPAT_* For each compat property on PC_Q35_COMPAT_*, there are only two possibilities: * If the device is never instantiated when using a machine other than pc-q35, then the compat property can be safely added to PC_COMPAT_*; * If the device can be instantiated when using a machine other than pc-q35, that means the other machines also need the compat property to be set. That means we don't need separate PC_Q35_COMPAT_* macros at all, today. The hpet.hpet-intcap case is interesting: piix and q35 do have something that emulates different defaults, but the machine-specific default is applied _after_ compat_props are applied, by simply checking if the property is zero (which is the real default on the hpet code). The hpet.hpet-intcap=0x4 compat property can (should?) be applied to piix too, because 0x4 was the default on both piix and q35 before the hpet-intcap property was introduced. Now, if one day we change the default HPET intcap on one of the PC machine-types again, we may want to introduce PC_{Q35,I440FX}_COMPAT macros. But while we don't need that, we can keep the code simple. Signed-off-by: Eduardo Habkost Cc: Liu Ping Fan Cc: Peter Maydell Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_q35.c | 10 ++++---- include/hw/i386/pc.h | 55 +++++++++++++++----------------------------- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 155db99f63..36b6ab0bce 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -361,7 +361,7 @@ static QEMUMachine pc_q35_machine_v2_0 = { .name = "pc-q35-2.0", .init = pc_q35_init_2_0, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_2_0, + PC_COMPAT_2_0, { /* end of list */ } }, }; @@ -373,7 +373,7 @@ static QEMUMachine pc_q35_machine_v1_7 = { .name = "pc-q35-1.7", .init = pc_q35_init_1_7, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_7, + PC_COMPAT_1_7, { /* end of list */ } }, }; @@ -385,7 +385,7 @@ static QEMUMachine pc_q35_machine_v1_6 = { .name = "pc-q35-1.6", .init = pc_q35_init_1_6, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_6, + PC_COMPAT_1_6, { /* end of list */ } }, }; @@ -395,7 +395,7 @@ static QEMUMachine pc_q35_machine_v1_5 = { .name = "pc-q35-1.5", .init = pc_q35_init_1_5, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_5, + PC_COMPAT_1_5, { /* end of list */ } }, }; @@ -409,7 +409,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { .name = "pc-q35-1.4", .init = pc_q35_init_1_4, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_4, + PC_COMPAT_1_4, { /* end of list */ } }, }; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 486e98feac..81718b8430 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -294,43 +294,6 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); -#define PC_Q35_COMPAT_2_0 \ - PC_COMPAT_2_0, \ - {\ - .driver = "ICH9-LPC",\ - .property = "memory-hotplug-support",\ - .value = "off",\ - },{\ - .driver = "xio3130-downstream",\ - .property = COMPAT_PROP_PCP,\ - .value = "off",\ - },{\ - .driver = "ioh3420",\ - .property = COMPAT_PROP_PCP,\ - .value = "off",\ - } - -#define PC_Q35_COMPAT_1_7 \ - PC_COMPAT_1_7, \ - PC_Q35_COMPAT_2_0, \ - {\ - .driver = "hpet",\ - .property = HPET_INTCAP,\ - .value = stringify(4),\ - } - -#define PC_Q35_COMPAT_1_6 \ - PC_COMPAT_1_6, \ - PC_Q35_COMPAT_1_7 - -#define PC_Q35_COMPAT_1_5 \ - PC_COMPAT_1_5, \ - PC_Q35_COMPAT_1_6 - -#define PC_Q35_COMPAT_1_4 \ - PC_COMPAT_1_4, \ - PC_Q35_COMPAT_1_5 - #define PC_COMPAT_2_0 \ {\ .driver = "virtio-scsi-pci",\ @@ -370,6 +333,19 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = "virtio-net-pci",\ .property = "guest_announce",\ .value = "off",\ + },\ + {\ + .driver = "ICH9-LPC",\ + .property = "memory-hotplug-support",\ + .value = "off",\ + },{\ + .driver = "xio3130-downstream",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ + },{\ + .driver = "ioh3420",\ + .property = COMPAT_PROP_PCP,\ + .value = "off",\ } #define PC_COMPAT_1_7 \ @@ -383,6 +359,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = "PIIX4_PM",\ .property = "acpi-pci-hotplug-with-bridge-support",\ .value = "off",\ + },\ + {\ + .driver = "hpet",\ + .property = HPET_INTCAP,\ + .value = stringify(4),\ } #define PC_COMPAT_1_6 \ From fa118d1f8be40860469e8e23745d0202bdf229ba Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Tue, 24 Jun 2014 19:57:55 -0300 Subject: [PATCH 05/37] pc: Fix "prog_if" typo on PC_COMPAT_2_0 The property name is "prog_if", not "prof_if". Signed-off-by: Eduardo Habkost Reported-by: BALATON Zoltan Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/i386/pc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 81718b8430..1c0c382d8c 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -321,7 +321,7 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); },\ {\ .driver = "pci-serial-2x",\ - .property = "prof_if",\ + .property = "prog_if",\ .value = stringify(0),\ },\ {\ From f2ae8abf1fa003e7ec6ee22cc3871924422a01d0 Mon Sep 17 00:00:00 2001 From: Marcelo Tosatti Date: Tue, 24 Jun 2014 18:55:11 -0300 Subject: [PATCH 06/37] mc146818rtc: add rtc-reset-reinjection QMP command It is necessary to reset RTC interrupt reinjection backlog if guest time is synchronized via a different mechanism, such as QGA's guest-set-time command. Failing to do so causes both corrections to be applied (summed), resulting in an incorrect guest time. Signed-off-by: Marcelo Tosatti Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/timer/mc146818rtc.c | 18 ++++++++++++++++++ monitor.c | 7 +++++++ qapi-schema.json | 12 ++++++++++++ qmp-commands.hx | 23 +++++++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 05002bf9d6..307732c744 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -27,6 +27,7 @@ #include "hw/timer/mc146818rtc.h" #include "qapi/visitor.h" #include "qapi-event.h" +#include "qmp-commands.h" #ifdef TARGET_I386 #include "hw/i386/apic.h" @@ -85,6 +86,7 @@ typedef struct RTCState { Notifier clock_reset_notifier; LostTickPolicy lost_tick_policy; Notifier suspend_notifier; + QLIST_ENTRY(RTCState) link; } RTCState; static void rtc_set_time(RTCState *s); @@ -523,6 +525,20 @@ static void rtc_get_time(RTCState *s, struct tm *tm) rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; } +static QLIST_HEAD(, RTCState) rtc_devices = + QLIST_HEAD_INITIALIZER(rtc_devices); + +#ifdef TARGET_I386 +void qmp_rtc_reset_reinjection(Error **errp) +{ + RTCState *s; + + QLIST_FOREACH(s, &rtc_devices, link) { + s->irq_coalesced = 0; + } +} +#endif + static void rtc_set_time(RTCState *s) { struct tm tm; @@ -911,6 +927,8 @@ ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) } else { isa_init_irq(isadev, &s->irq, RTC_ISA_IRQ); } + QLIST_INSERT_HEAD(&rtc_devices, s, link); + return isadev; } diff --git a/monitor.c b/monitor.c index 5718d0b60a..799131bd01 100644 --- a/monitor.c +++ b/monitor.c @@ -5441,3 +5441,10 @@ QemuOptsList qemu_mon_opts = { { /* end of list */ } }, }; + +#ifndef TARGET_I386 +void qmp_rtc_reset_reinjection(Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection"); +} +#endif diff --git a/qapi-schema.json b/qapi-schema.json index a83befc05b..b11aad2068 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3468,3 +3468,15 @@ ## { 'enum': 'GuestPanicAction', 'data': [ 'pause' ] } + +## +# @rtc-reset-reinjection +# +# This command will reset the RTC interrupt reinjection backlog. +# Can be used if another mechanism to synchronize guest time +# is in effect, for example QEMU guest agent's guest-set-time +# command. +# +# Since: 2.1 +## +{ 'command': 'rtc-reset-reinjection' } diff --git a/qmp-commands.hx b/qmp-commands.hx index 65218bc147..1ea18b22a3 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3675,3 +3675,26 @@ Example: { "slot": "3", "slot-type": "DIMM", "source": 0, "status": 0} ]} EQMP + +#if defined TARGET_I386 + { + .name = "rtc-reset-reinjection", + .args_type = "", + .mhandler.cmd_new = qmp_marshal_input_rtc_reset_reinjection, + }, +#endif + +SQMP +rtc-reset-reinjection +--------------------- + +Reset the RTC interrupt reinjection backlog. + +Arguments: None. + +Example: + +-> { "execute": "rtc-reset-reinjection" } +<- { "return": {} } + +EQMP From 46e797c4d34c1340eb981fa599f49f82b2ba5f41 Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Wed, 25 Jun 2014 14:49:36 +0200 Subject: [PATCH 07/37] vhost-user: fix wrong ids in documentation Signed-off-by: Damjan Marion Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/specs/vhost-user.txt | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt index 0ea767e4b8..2641390244 100644 --- a/docs/specs/vhost-user.txt +++ b/docs/specs/vhost-user.txt @@ -132,7 +132,7 @@ Message types * VHOST_USER_GET_FEATURES - Id: 2 + Id: 1 Equivalent ioctl: VHOST_GET_FEATURES Master payload: N/A Slave payload: u64 @@ -141,7 +141,7 @@ Message types * VHOST_USER_SET_FEATURES - Id: 3 + Id: 2 Ioctl: VHOST_SET_FEATURES Master payload: u64 @@ -149,7 +149,7 @@ Message types * VHOST_USER_SET_OWNER - Id: 4 + Id: 3 Equivalent ioctl: VHOST_SET_OWNER Master payload: N/A @@ -159,7 +159,7 @@ Message types * VHOST_USER_RESET_OWNER - Id: 5 + Id: 4 Equivalent ioctl: VHOST_RESET_OWNER Master payload: N/A @@ -168,7 +168,7 @@ Message types * VHOST_USER_SET_MEM_TABLE - Id: 6 + Id: 5 Equivalent ioctl: VHOST_SET_MEM_TABLE Master payload: memory regions description @@ -179,7 +179,7 @@ Message types * VHOST_USER_SET_LOG_BASE - Id: 7 + Id: 6 Equivalent ioctl: VHOST_SET_LOG_BASE Master payload: u64 @@ -187,7 +187,7 @@ Message types * VHOST_USER_SET_LOG_FD - Id: 8 + Id: 7 Equivalent ioctl: VHOST_SET_LOG_FD Master payload: N/A @@ -195,7 +195,7 @@ Message types * VHOST_USER_SET_VRING_NUM - Id: 9 + Id: 8 Equivalent ioctl: VHOST_SET_VRING_NUM Master payload: vring state description @@ -203,7 +203,7 @@ Message types * VHOST_USER_SET_VRING_ADDR - Id: 10 + Id: 9 Equivalent ioctl: VHOST_SET_VRING_ADDR Master payload: vring address description Slave payload: N/A @@ -212,7 +212,7 @@ Message types * VHOST_USER_SET_VRING_BASE - Id: 11 + Id: 10 Equivalent ioctl: VHOST_SET_VRING_BASE Master payload: vring state description @@ -220,7 +220,7 @@ Message types * VHOST_USER_GET_VRING_BASE - Id: 12 + Id: 11 Equivalent ioctl: VHOST_USER_GET_VRING_BASE Master payload: vring state description Slave payload: vring state description @@ -229,7 +229,7 @@ Message types * VHOST_USER_SET_VRING_KICK - Id: 13 + Id: 12 Equivalent ioctl: VHOST_SET_VRING_KICK Master payload: u64 @@ -242,7 +242,7 @@ Message types * VHOST_USER_SET_VRING_CALL - Id: 14 + Id: 13 Equivalent ioctl: VHOST_SET_VRING_CALL Master payload: u64 @@ -255,7 +255,7 @@ Message types * VHOST_USER_SET_VRING_ERR - Id: 15 + Id: 14 Equivalent ioctl: VHOST_SET_VRING_ERR Master payload: u64 From 5f8632d3c3d7bc5ef24166ba7cf90fcfb2adbf7d Mon Sep 17 00:00:00 2001 From: Don Slutz Date: Wed, 25 Jun 2014 16:19:07 -0400 Subject: [PATCH 08/37] pc: make isapc and pc-0.10 to pc-0.13 have 1.7.0 memory layout QEMU 2.0 changed memory layout for isapc and pc-0.10 to pc-0.13. This prevents migration from QEMU 1.7.0 for these machine types when -m 3.5G is specified. Paolo Bonzini asked that: smbios_legacy_mode = true; has_reserved_memory = false; option_rom_has_mr = true; rom_file_has_mr = false; also be done. Cc: qemu-stable@nongnu.org Cc: Paolo Bonzini Signed-off-by: Don Slutz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Fixes: https://bugs.launchpad.net/qemu/+bug/1334307 Tested-by: "Slutz, Donald Christopher" --- hw/i386/pc_piix.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 47546b72ae..2dccb3401b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -392,6 +392,11 @@ static void pc_init_pci_no_kvmclock(MachineState *machine) has_pci_info = false; has_acpi_build = false; smbios_defaults = false; + gigabyte_align = false; + smbios_legacy_mode = true; + has_reserved_memory = false; + option_rom_has_mr = true; + rom_file_has_mr = false; x86_cpu_compat_disable_kvm_features(FEAT_KVM, KVM_FEATURE_PV_EOI); enable_compat_apic_id_mode(); pc_init1(machine, 1, 0); @@ -402,6 +407,11 @@ static void pc_init_isa(MachineState *machine) has_pci_info = false; has_acpi_build = false; smbios_defaults = false; + gigabyte_align = false; + smbios_legacy_mode = true; + has_reserved_memory = false; + option_rom_has_mr = true; + rom_file_has_mr = false; if (!machine->cpu_model) { machine->cpu_model = "486"; } From 2f5732e9648fcddc8759a8fd25c0b41a38352be6 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Fri, 27 Jun 2014 20:02:48 +0100 Subject: [PATCH 09/37] Allow mismatched virtio config-len Commit 'virtio: validate config_len on load' restricted config_len loaded from the wire to match the config_len that the device had. Unfortunately, there are cases where this isn't true, the one we found it on was the wce addition in virtio-blk. Allow mismatched config-lengths: *) If the version on the wire is shorter then fine *) If the version on the wire is longer, load what we have space for and skip the rest. (This is mst@redhat.com's rework of what I originally posted) Signed-off-by: Dr. David Alan Gilbert Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index a3082d569d..c1d538c5f3 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -926,12 +926,18 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) return -1; } config_len = qemu_get_be32(f); - if (config_len != vdev->config_len) { - error_report("Unexpected config length 0x%x. Expected 0x%zx", - config_len, vdev->config_len); - return -1; + + /* + * There are cases where the incoming config can be bigger or smaller + * than what we have; so load what we have space for, and skip + * any excess that's in the stream. + */ + qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len)); + + while (config_len > vdev->config_len) { + qemu_get_byte(f); + config_len--; } - qemu_get_buffer(f, vdev->config, vdev->config_len); num = qemu_get_be32(f); From 1af878e0497a885b6cbdd3a6d91d399f4851d99c Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 26 Jun 2014 18:33:18 -0300 Subject: [PATCH 10/37] numa: Keep track of NUMA nodes present on the command-line Based on "enable sparse node numbering" patch from Nishanth Aravamudan, but without the code to actually support sparse node IDs. This just adds the code to keep track of present/non-present nodes on the command-line, without changing any behavior. Signed-off-by: Nishanth Aravamudan [Rename max_numa_node to max_numa_nodeid -Eduardo] [Initialize max_numa_nodeid to 0 -Eduardo] [Use MAX() macro when setting max_numa_nodeid -Eduardo] Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Hu Tao Reviewed-by: Eric Blake --- include/sysemu/sysemu.h | 7 ++++++- numa.c | 2 ++ vl.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 285c45baf2..d8539fd602 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -146,11 +146,16 @@ extern int mem_prealloc; */ #define MAX_CPUMASK_BITS 255 -extern int nb_numa_nodes; +extern int nb_numa_nodes; /* Number of NUMA nodes */ +extern int max_numa_nodeid; /* Highest specified NUMA node ID, plus one. + * For all nodes, nodeid < max_numa_nodeid + */ + typedef struct node_info { uint64_t node_mem; DECLARE_BITMAP(node_cpu, MAX_CPUMASK_BITS); struct HostMemoryBackend *node_memdev; + bool present; } NodeInfo; extern NodeInfo numa_info[MAX_NODES]; void set_numa_nodes(void); diff --git a/numa.c b/numa.c index 6c2eae7ef3..db10f954dd 100644 --- a/numa.c +++ b/numa.c @@ -106,6 +106,8 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) numa_info[nodenr].node_mem = object_property_get_int(o, "size", NULL); numa_info[nodenr].node_memdev = MEMORY_BACKEND(o); } + numa_info[nodenr].present = true; + max_numa_nodeid = MAX(max_numa_nodeid, nodenr + 1); } int numa_init_func(QemuOpts *opts, void *opaque) diff --git a/vl.c b/vl.c index a1686ef6c4..41ddcd2678 100644 --- a/vl.c +++ b/vl.c @@ -196,6 +196,7 @@ static QTAILQ_HEAD(, FWBootEntry) fw_boot_order = QTAILQ_HEAD_INITIALIZER(fw_boot_order); int nb_numa_nodes; +int max_numa_nodeid; NodeInfo numa_info[MAX_NODES]; uint8_t qemu_uuid[16]; @@ -2984,10 +2985,12 @@ int main(int argc, char **argv, char **envp) for (i = 0; i < MAX_NODES; i++) { numa_info[i].node_mem = 0; + numa_info[i].present = false; bitmap_zero(numa_info[i].node_cpu, MAX_CPUMASK_BITS); } nb_numa_nodes = 0; + max_numa_nodeid = 0; nb_nics = 0; bdrv_init_with_whitelist(); From 1945b9d8b03aad59e86bfe3a3194e6bbeffbad6a Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 26 Jun 2014 18:33:19 -0300 Subject: [PATCH 11/37] numa: Reject duplicate node IDs The same nodeid shouldn't appear multiple times in the command-line. In addition to detecting command-line mistakes, this will fix a bug where nb_numa_nodes may become larger than MAX_NODES (and cause out-of-bounds access on the numa_info array). Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Hu Tao Reviewed-by: Eric Blake --- numa.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/numa.c b/numa.c index db10f954dd..c254127504 100644 --- a/numa.c +++ b/numa.c @@ -62,6 +62,11 @@ static void numa_node_parse(NumaNodeOptions *node, QemuOpts *opts, Error **errp) return; } + if (numa_info[nodenr].present) { + error_setg(errp, "Duplicate NUMA nodeid: %" PRIu16, nodenr); + return; + } + for (cpus = node->cpus; cpus; cpus = cpus->next) { if (cpus->value > MAX_CPUMASK_BITS) { error_setg(errp, "CPU number %" PRIu16 " is bigger than %d", From 12d6e4640ce28d002e96d58c31cd5e7256258626 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Thu, 26 Jun 2014 18:33:20 -0300 Subject: [PATCH 12/37] numa: Reject configuration if not all node IDs are present We don't support sparse NUMA node IDs yet, so this changes QEMU to reject configs where not all nodes are present. Signed-off-by: Eduardo Habkost Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Reviewed-by: Eric Blake --- numa.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/numa.c b/numa.c index c254127504..2fde7409bf 100644 --- a/numa.c +++ b/numa.c @@ -160,9 +160,24 @@ error: void set_numa_nodes(void) { + int i; + + assert(max_numa_nodeid <= MAX_NODES); + + /* No support for sparse NUMA node IDs yet: */ + for (i = max_numa_nodeid - 1; i >= 0; i--) { + /* Report large node IDs first, to make mistakes easier to spot */ + if (!numa_info[i].present) { + error_report("numa: Node ID missing: %d", i); + exit(1); + } + } + + /* This must be always true if all nodes are present: */ + assert(nb_numa_nodes == max_numa_nodeid); + if (nb_numa_nodes > 0) { uint64_t numa_total; - int i; if (nb_numa_nodes > MAX_NODES) { nb_numa_nodes = MAX_NODES; From 3fd74b84076488ae44ba5f3cfed22ff056c5199c Mon Sep 17 00:00:00 2001 From: Damjan Marion Date: Thu, 26 Jun 2014 23:01:32 +0200 Subject: [PATCH 13/37] vhost-user: fix regions provied with VHOST_USER_SET_MEM_TABLE message Old code was affected by memory gaps which resulted in buffer pointers pointing to address outside of the mapped regions. Here we are introducing following changes: - new function qemu_get_ram_block_host_ptr() returns host pointer to the ram block, it is needed to calculate offset of specific region in the host memory - new field mmap_offset is added to the VhostUserMemoryRegion. It contains offset where specific region starts in the mapped memory. As there is stil no wider adoption of vhost-user agreement was made that we will not bump version number due to this change - other fileds in VhostUserMemoryRegion struct are not changed, as they are all needed for usermode app implementation - region data is not taken from ram_list.blocks anymore, instead we use region data which is alredy calculated for use in vhost-net - Now multiple regions can have same FD and user applicaton can call mmap() multiple times with the same FD but with different offset (user needs to take care for offset page alignment) Signed-off-by: Damjan Marion Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin Signed-off-by: Damjan Marion --- docs/specs/vhost-user.txt | 7 ++++--- exec.c | 7 +++++++ hw/virtio/vhost-user.c | 23 ++++++++++++++--------- include/exec/ram_addr.h | 1 + 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt index 2641390244..6abb6977ff 100644 --- a/docs/specs/vhost-user.txt +++ b/docs/specs/vhost-user.txt @@ -78,13 +78,14 @@ Depending on the request type, payload can be: Padding: 32-bit A region is: - --------------------------------------- - | guest address | size | user address | - --------------------------------------- + ----------------------------------------------------- + | guest address | size | user address | mmap offset | + ----------------------------------------------------- Guest address: a 64-bit guest address of the region Size: a 64-bit size User address: a 64-bit user address + mmmap offset: 64-bit offset where region starts in the mapped memory In QEMU the vhost-user message is implemented with the following struct: diff --git a/exec.c b/exec.c index c8494051a6..a94c5832f4 100644 --- a/exec.c +++ b/exec.c @@ -1456,6 +1456,13 @@ int qemu_get_ram_fd(ram_addr_t addr) return block->fd; } +void *qemu_get_ram_block_host_ptr(ram_addr_t addr) +{ + RAMBlock *block = qemu_get_ram_block(addr); + + return block->host; +} + /* Return a host pointer to ram allocated with qemu_ram_alloc. With the exception of the softmmu code in this file, this should only be used for local memory (e.g. video ram) that the device owns, diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 0df6a936a0..38e580642f 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -14,6 +14,7 @@ #include "sysemu/kvm.h" #include "qemu/error-report.h" #include "qemu/sockets.h" +#include "exec/ram_addr.h" #include #include @@ -47,6 +48,7 @@ typedef struct VhostUserMemoryRegion { uint64_t guest_phys_addr; uint64_t memory_size; uint64_t userspace_addr; + uint64_t mmap_offset; } VhostUserMemoryRegion; typedef struct VhostUserMemory { @@ -183,10 +185,10 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request, { VhostUserMsg msg; VhostUserRequest msg_request; - RAMBlock *block = 0; struct vhost_vring_file *file = 0; int need_reply = 0; int fds[VHOST_MEMORY_MAX_NREGIONS]; + int i, fd; size_t fd_num = 0; assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER); @@ -212,14 +214,17 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request, break; case VHOST_SET_MEM_TABLE: - QTAILQ_FOREACH(block, &ram_list.blocks, next) - { - if (block->fd > 0) { - msg.memory.regions[fd_num].userspace_addr = - (uintptr_t) block->host; - msg.memory.regions[fd_num].memory_size = block->length; - msg.memory.regions[fd_num].guest_phys_addr = block->offset; - fds[fd_num++] = block->fd; + for (i = 0; i < dev->mem->nregions; ++i) { + struct vhost_memory_region *reg = dev->mem->regions + i; + fd = qemu_get_ram_fd(reg->guest_phys_addr); + if (fd > 0) { + msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr; + msg.memory.regions[fd_num].memory_size = reg->memory_size; + msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr; + msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr - + (uintptr_t) qemu_get_ram_block_host_ptr(reg->guest_phys_addr); + assert(fd_num < VHOST_MEMORY_MAX_NREGIONS); + fds[fd_num++] = fd; } } diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 55ca67681f..e9eb831ee3 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -29,6 +29,7 @@ ram_addr_t qemu_ram_alloc_from_ptr(ram_addr_t size, void *host, MemoryRegion *mr); ram_addr_t qemu_ram_alloc(ram_addr_t size, MemoryRegion *mr); int qemu_get_ram_fd(ram_addr_t addr); +void *qemu_get_ram_block_host_ptr(ram_addr_t addr); void *qemu_get_ram_ptr(ram_addr_t addr); void qemu_ram_free(ram_addr_t addr); void qemu_ram_free_from_ptr(ram_addr_t addr); From a628fc8dae4c4a9a70f020ab65018a6e085a2088 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 29 Jun 2014 17:58:48 +0300 Subject: [PATCH 14/37] vhost-user: typo fixups Fix typo in field name. Strip two consequitive empty lines. Signed-off-by: Michael S. Tsirkin --- docs/specs/vhost-user.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/specs/vhost-user.txt b/docs/specs/vhost-user.txt index 6abb6977ff..650bb18186 100644 --- a/docs/specs/vhost-user.txt +++ b/docs/specs/vhost-user.txt @@ -85,8 +85,7 @@ Depending on the request type, payload can be: Guest address: a 64-bit guest address of the region Size: a 64-bit size User address: a 64-bit user address - mmmap offset: 64-bit offset where region starts in the mapped memory - + mmap offset: 64-bit offset where region starts in the mapped memory In QEMU the vhost-user message is implemented with the following struct: From 032a74a1c0fcdd5fd1c69e56126b4c857ee36611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 24 Jun 2014 19:11:32 +0200 Subject: [PATCH 15/37] virtio-net: byteswap virtio-net header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCP connectivity fails when the guest has a different endianness. The packets are silently dropped on the host by the tap backend when they are read from user space because the endianness of the virtio-net header is in the wrong order. These lines may appear in the guest console: [ 454.709327] skbuff: bad partial csum: csum=8704/4096 len=74 [ 455.702554] skbuff: bad partial csum: csum=8704/4096 len=74 The issue that got first spotted with a ppc64le PowerKVM guest, but it also exists for the less common case of a x86_64 guest run by a big-endian ppc64 TCG hypervisor. Signed-off-by: Cédric Le Goater [ Ported from PowerKVM, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index e51d753cee..ea1a081993 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -875,6 +875,14 @@ static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) return 1; } +static void virtio_net_hdr_swap(struct virtio_net_hdr *hdr) +{ + tswap16s(&hdr->hdr_len); + tswap16s(&hdr->gso_size); + tswap16s(&hdr->csum_start); + tswap16s(&hdr->csum_offset); +} + /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so * it never finds out that the packets don't have valid checksums. This * causes dhclient to get upset. Fedora's carried a patch for ages to @@ -910,6 +918,7 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); + virtio_net_hdr_swap(wbuf); iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1118,6 +1127,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) exit(1); } + if (n->has_vnet_hdr) { + if (out_sg[0].iov_len < n->guest_hdr_len) { + error_report("virtio-net header incorrect"); + exit(1); + } + virtio_net_hdr_swap((void *) out_sg[0].iov_base); + } + /* * If host wants to see the guest header as is, we can * pass it on unchanged. Otherwise, copy just the parts From e38e943a1fa20d04deb1899be19b12aadec7a585 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 24 Jun 2014 19:13:50 +0200 Subject: [PATCH 16/37] virtio-serial: don't migrate the config space The device configuration is set at realize time and never changes. It should not be migrated as it is done today. For the sake of compatibility, let's just skip them at load time. Signed-off-by: Alexander Graf [ added missing casts to uint16_t *, added From, SoB and commit message, Greg Kurz ] Reviewed-by: Michael S. Tsirkin Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/char/virtio-serial-bus.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index a2958ff02f..e2174b10bb 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -663,6 +663,7 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) uint32_t max_nr_ports, nr_active_ports, ports_map; unsigned int i; int ret; + uint32_t tmp; if (version_id > 3) { return -EINVAL; @@ -678,17 +679,12 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) return 0; } - /* The config space */ - qemu_get_be16s(f, &s->config.cols); - qemu_get_be16s(f, &s->config.rows); - - qemu_get_be32s(f, &max_nr_ports); - tswap32s(&max_nr_ports); - if (max_nr_ports > tswap32(s->config.max_nr_ports)) { - /* Source could have had more ports than us. Fail migration. */ - return -EINVAL; - } + /* Unused */ + qemu_get_be16s(f, (uint16_t *) &tmp); + qemu_get_be16s(f, (uint16_t *) &tmp); + qemu_get_be32s(f, &tmp); + max_nr_ports = tswap32(s->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_get_be32s(f, &ports_map); From 1b5fc0dea469976fcbf6dc2e8f2637a2154af2fb Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:15:31 +0200 Subject: [PATCH 17/37] virtio: introduce device specific migration calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to migrate virtio subsections, they should be streamed after the device itself. We need the device specific code to be called from the common migration code to achieve this. This patch introduces load and save methods for this purpose. Suggested-by: Andreas Färber Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/virtio-blk.c | 2 +- hw/char/virtio-serial-bus.c | 2 +- hw/net/virtio-net.c | 2 +- hw/scsi/virtio-scsi.c | 2 +- hw/virtio/virtio-balloon.c | 2 +- hw/virtio/virtio-rng.c | 2 +- hw/virtio/virtio.c | 13 ++++++++++++- include/hw/virtio/virtio.h | 4 +++- 8 files changed, 21 insertions(+), 8 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index a222e3f9a4..5e2693a568 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -635,7 +635,7 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) if (version_id != 2) return -EINVAL; - ret = virtio_load(vdev, f); + ret = virtio_load(vdev, f, version_id); if (ret) { return ret; } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index e2174b10bb..f919ec2440 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -670,7 +670,7 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) } /* The virtio device */ - ret = virtio_load(VIRTIO_DEVICE(s), f); + ret = virtio_load(VIRTIO_DEVICE(s), f, version_id); if (ret) { return ret; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index ea1a081993..acfe91ccb6 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1362,7 +1362,7 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - ret = virtio_load(vdev, f); + ret = virtio_load(vdev, f, version_id); if (ret) { return ret; } diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 8c8c9d1f61..6b4fd6f625 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -549,7 +549,7 @@ static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) VirtIODevice *vdev = VIRTIO_DEVICE(opaque); int ret; - ret = virtio_load(vdev, f); + ret = virtio_load(vdev, f, version_id); if (ret) { return ret; } diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 2a2e58a297..165592e1d7 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -343,7 +343,7 @@ static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) if (version_id != 1) return -EINVAL; - ret = virtio_load(vdev, f); + ret = virtio_load(vdev, f, version_id); if (ret) { return ret; } diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index b6ab3610cb..025de81345 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -113,7 +113,7 @@ static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) if (version_id != 1) { return -EINVAL; } - virtio_load(vdev, f); + virtio_load(vdev, f, version_id); /* We may have an element ready but couldn't process it due to a quota * limit. Make sure to try again after live migration when the quota may diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index c1d538c5f3..7f9ac5e0b9 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -843,6 +843,7 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); int i; if (k->save_config) { @@ -877,6 +878,10 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) k->save_queue(qbus->parent, i, f); } } + + if (vdc->save != NULL) { + vdc->save(vdev, f); + } } int virtio_set_features(VirtIODevice *vdev, uint32_t val) @@ -895,7 +900,7 @@ int virtio_set_features(VirtIODevice *vdev, uint32_t val) return bad ? -1 : 0; } -int virtio_load(VirtIODevice *vdev, QEMUFile *f) +int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) { int i, ret; int32_t config_len; @@ -904,6 +909,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) uint32_t supported_features; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); if (k->load_config) { ret = k->load_config(qbus->parent, f); @@ -983,6 +989,11 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) } virtio_notify_vector(vdev, VIRTIO_NO_VECTOR); + + if (vdc->load != NULL) { + return vdc->load(vdev, f, version_id); + } + return 0; } diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 3e54e90aad..3505ce511e 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -150,6 +150,8 @@ typedef struct VirtioDeviceClass { * must mask in frontend instead. */ void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); + void (*save)(VirtIODevice *vdev, QEMUFile *f); + int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id); } VirtioDeviceClass; void virtio_init(VirtIODevice *vdev, const char *name, @@ -184,7 +186,7 @@ void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); void virtio_save(VirtIODevice *vdev, QEMUFile *f); -int virtio_load(VirtIODevice *vdev, QEMUFile *f); +int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id); void virtio_notify_config(VirtIODevice *vdev); From 037dab2fe8877f0513417ba7613a93c456962da9 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:19:03 +0200 Subject: [PATCH 18/37] virtio-net: implement per-device migration calls Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index acfe91ccb6..eead70e5a8 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1314,7 +1314,6 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) static void virtio_net_save(QEMUFile *f, void *opaque) { - int i; VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); @@ -1322,6 +1321,12 @@ static void virtio_net_save(QEMUFile *f, void *opaque) * it might keep writing to memory. */ assert(!n->vhost_started); virtio_save(vdev, f); +} + +static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i; qemu_put_buffer(f, n->mac, ETH_ALEN); qemu_put_be32(f, n->vqs[0].tx_waiting); @@ -1357,15 +1362,18 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); - int ret, i, link_down; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } + return virtio_load(vdev, f, version_id); +} + +static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i, link_down; qemu_get_buffer(f, n->mac, ETH_ALEN); n->vqs[0].tx_waiting = qemu_get_be32(f); @@ -1711,6 +1719,8 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->set_status = virtio_net_set_status; vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; + vdc->load = virtio_net_load_device; + vdc->save = virtio_net_save_device; } static const TypeInfo virtio_net_info = { From b2b295a74a17ec8b2eec166cbc65b65307a97ee2 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:19:23 +0200 Subject: [PATCH 19/37] virtio-blk: implement per-device migration calls Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/virtio-blk.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 5e2693a568..b06af8c208 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -611,12 +611,16 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) static void virtio_blk_save(QEMUFile *f, void *opaque) { - VirtIOBlock *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - VirtIOBlockReq *req = s->rq; + VirtIODevice *vdev = VIRTIO_DEVICE(opaque); virtio_save(vdev, f); +} +static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIOBlock *s = VIRTIO_BLK(vdev); + VirtIOBlockReq *req = s->rq; + while (req) { qemu_put_sbyte(f, 1); qemu_put_buffer(f, (unsigned char *)req->elem, @@ -630,15 +634,17 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) { VirtIOBlock *s = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(s); - int ret; if (version_id != 2) return -EINVAL; - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } + return virtio_load(vdev, f, version_id); +} + +static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIOBlock *s = VIRTIO_BLK(vdev); while (qemu_get_sbyte(f)) { VirtIOBlockReq *req = virtio_blk_alloc_request(s); @@ -799,6 +805,8 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) vdc->get_features = virtio_blk_get_features; vdc->set_status = virtio_blk_set_status; vdc->reset = virtio_blk_reset; + vdc->save = virtio_blk_save_device; + vdc->load = virtio_blk_load_device; } static const TypeInfo virtio_device_info = { From 13c6855ab06b910a3a320b0e8f2b5f7ae8e2f351 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:19:48 +0200 Subject: [PATCH 20/37] virtio-serial: implement per-device migration calls Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/char/virtio-serial-bus.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index f919ec2440..b8af1b18cd 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -514,14 +514,17 @@ static void vser_reset(VirtIODevice *vdev) static void virtio_serial_save(QEMUFile *f, void *opaque) { - VirtIOSerial *s = VIRTIO_SERIAL(opaque); + /* The virtio device */ + virtio_save(VIRTIO_DEVICE(opaque), f); +} + +static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIOSerial *s = VIRTIO_SERIAL(vdev); VirtIOSerialPort *port; uint32_t nr_active_ports; unsigned int i, max_nr_ports; - /* The virtio device */ - virtio_save(VIRTIO_DEVICE(s), f); - /* The config space */ qemu_put_be16s(f, &s->config.cols); qemu_put_be16s(f, &s->config.rows); @@ -659,21 +662,22 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) { - VirtIOSerial *s = VIRTIO_SERIAL(opaque); - uint32_t max_nr_ports, nr_active_ports, ports_map; - unsigned int i; - int ret; - uint32_t tmp; - if (version_id > 3) { return -EINVAL; } /* The virtio device */ - ret = virtio_load(VIRTIO_DEVICE(s), f, version_id); - if (ret) { - return ret; - } + return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); +} + +static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIOSerial *s = VIRTIO_SERIAL(vdev); + uint32_t max_nr_ports, nr_active_ports, ports_map; + unsigned int i; + int ret; + uint32_t tmp; if (version_id < 2) { return 0; @@ -1015,6 +1019,8 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) vdc->get_config = get_config; vdc->set_status = set_status; vdc->reset = vser_reset; + vdc->save = virtio_serial_save_device; + vdc->load = virtio_serial_load_device; } static const TypeInfo virtio_device_info = { From 9ea2511c8542b875e89ab73d18480d42d6129a8c Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:20:08 +0200 Subject: [PATCH 21/37] virtio-balloon: implement per-device migration calls Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-balloon.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 165592e1d7..e0ed5eee03 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -325,10 +325,12 @@ static void virtio_balloon_to_target(void *opaque, ram_addr_t target) static void virtio_balloon_save(QEMUFile *f, void *opaque) { - VirtIOBalloon *s = VIRTIO_BALLOON(opaque); - VirtIODevice *vdev = VIRTIO_DEVICE(s); + virtio_save(VIRTIO_DEVICE(opaque), f); +} - virtio_save(vdev, f); +static void virtio_balloon_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(vdev); qemu_put_be32(f, s->num_pages); qemu_put_be32(f, s->actual); @@ -336,17 +338,16 @@ static void virtio_balloon_save(QEMUFile *f, void *opaque) static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) { - VirtIOBalloon *s = VIRTIO_BALLOON(opaque); - VirtIODevice *vdev = VIRTIO_DEVICE(s); - int ret; - if (version_id != 1) return -EINVAL; - ret = virtio_load(vdev, f, version_id); - if (ret) { - return ret; - } + return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); +} + +static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(vdev); s->num_pages = qemu_get_be32(f); s->actual = qemu_get_be32(f); @@ -416,6 +417,8 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) vdc->get_config = virtio_balloon_get_config; vdc->set_config = virtio_balloon_set_config; vdc->get_features = virtio_balloon_get_features; + vdc->save = virtio_balloon_save_device; + vdc->load = virtio_balloon_load_device; } static const TypeInfo virtio_balloon_info = { From 3902d49e13c2428bd6381cfdf183103ca4477c1f Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:20:23 +0200 Subject: [PATCH 22/37] virtio-rng: implement per-device migration calls While we are here, we also check virtio_load() return value. Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-rng.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 025de81345..1356aca8d6 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -107,19 +107,20 @@ static void virtio_rng_save(QEMUFile *f, void *opaque) static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) { - VirtIORNG *vrng = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(vrng); - if (version_id != 1) { return -EINVAL; } - virtio_load(vdev, f, version_id); + return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); +} +static int virtio_rng_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ /* We may have an element ready but couldn't process it due to a quota * limit. Make sure to try again after live migration when the quota may * have been reset. */ - virtio_rng_process(vrng); + virtio_rng_process(VIRTIO_RNG(vdev)); return 0; } @@ -219,6 +220,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) vdc->realize = virtio_rng_device_realize; vdc->unrealize = virtio_rng_device_unrealize; vdc->get_features = get_features; + vdc->load = virtio_rng_load_device; } static void virtio_rng_initfn(Object *obj) From 6b321a3df55de42ad349870a8793e33a69f9d1a3 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:22:30 +0200 Subject: [PATCH 23/37] virtio: add subsections to the migration stream There is a need to add some more fields to VirtIODevice that should be migrated (broken status, endianness). The problem is that we do not want to break compatibility while adding a new feature... This issue has been addressed in the generic VMState code with the use of optional subsections. As a *temporary* alternative to port the whole virtio migration code to VMState, this patch mimics a similar subsectionning ability for virtio, using the VMState code. Since each virtio device is streamed in its own section, the idea is to stream subsections between the end of the device section and the start of the next sections. This allows an older QEMU to complain and exit when fed with subsections: Unknown savevm section type 5 load of migration failed Suggested-by: Alexander Graf Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7f9ac5e0b9..7b317ce279 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -19,6 +19,7 @@ #include "hw/virtio/virtio.h" #include "qemu/atomic.h" #include "hw/virtio/virtio-bus.h" +#include "migration/migration.h" /* * The alignment to use between consumer and producer parts of vring. @@ -839,6 +840,16 @@ void virtio_notify_config(VirtIODevice *vdev) virtio_notify_vector(vdev, vdev->config_vector); } +static const VMStateDescription vmstate_virtio = { + .name = "virtio", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + void virtio_save(VirtIODevice *vdev, QEMUFile *f) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -882,6 +893,9 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) if (vdc->save != NULL) { vdc->save(vdev, f); } + + /* Subsections */ + vmstate_save_state(f, &vmstate_virtio, vdev); } int virtio_set_features(VirtIODevice *vdev, uint32_t val) @@ -991,10 +1005,13 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) virtio_notify_vector(vdev, VIRTIO_NO_VECTOR); if (vdc->load != NULL) { - return vdc->load(vdev, f, version_id); + ret = vdc->load(vdev, f, version_id); + if (ret) { + return ret; + } } - return 0; + return vmstate_load_state(f, &vmstate_virtio, vdev, 1); } void virtio_cleanup(VirtIODevice *vdev) From 98ed8ecfc9dd9e22e4251251492f062dde32c3c4 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:26:29 +0200 Subject: [PATCH 24/37] exec: introduce target_words_bigendian() helper We currently have a virtio_is_big_endian() helper that provides the target endianness to the virtio code. As of today, the helper returns a fixed compile-time value. Of course, this will have to change if we want to support target endianness changes at run-time. Let's move the TARGET_WORDS_BIGENDIAN bits out to a new helper and have virtio_is_big_endian() implemented on top of it. This patch doesn't change any functionality. Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- exec.c | 8 ++------ hw/virtio/virtio-pci.c | 3 --- include/hw/virtio/virtio.h | 6 ++++++ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/exec.c b/exec.c index a94c5832f4..18d6c35942 100644 --- a/exec.c +++ b/exec.c @@ -2759,14 +2759,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, } #endif -#if !defined(CONFIG_USER_ONLY) - /* * A helper function for the _utterly broken_ virtio device model to find out if * it's running on a big endian machine. Don't do this at home kids! */ -bool virtio_is_big_endian(void); -bool virtio_is_big_endian(void) +bool target_words_bigendian(void); +bool target_words_bigendian(void) { #if defined(TARGET_WORDS_BIGENDIAN) return true; @@ -2775,8 +2773,6 @@ bool virtio_is_big_endian(void) #endif } -#endif - #ifndef CONFIG_USER_ONLY bool cpu_physical_memory_is_io(hwaddr phys_addr) { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 57e1e6141e..e11f759e94 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -89,9 +89,6 @@ /* Flags track per-device state like workarounds for quirks in older guests. */ #define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) -/* HACK for virtio to determine if it's running a big endian guest */ -bool virtio_is_big_endian(void); - static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOPCIProxy *dev); diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 3505ce511e..9000ee2f50 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -255,4 +255,10 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, bool set_handler); void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); + +bool target_words_bigendian(void); +static inline bool virtio_is_big_endian(void) +{ + return target_words_bigendian(); +} #endif From bf7663c4bd8f8f619d6dbb5780025d92ace250a8 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:33:21 +0200 Subject: [PATCH 25/37] cpu: introduce CPUClass::virtio_is_big_endian() If we want to support targets that can change endianness (modern PPC and ARM for the moment), we need to add a per-CPU class method to be called from the virtio code. The virtio_ prefix in the name is a hint for people to avoid misusage (aka. anywhere but from the virtio code). The default behaviour is to return the compile-time default target endianness. Suggested-by: Peter Maydell Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/qom/cpu.h | 1 + qom/cpu.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/include/qom/cpu.h b/include/qom/cpu.h index 4b352a28fa..1aafbf5f34 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -116,6 +116,7 @@ typedef struct CPUClass { CPUUnassignedAccess do_unassigned_access; void (*do_unaligned_access)(CPUState *cpu, vaddr addr, int is_write, int is_user, uintptr_t retaddr); + bool (*virtio_is_big_endian)(CPUState *cpu); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, uint8_t *buf, int len, bool is_write); void (*dump_state)(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, diff --git a/qom/cpu.c b/qom/cpu.c index fada2d4b92..b32dd0a562 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -196,6 +196,11 @@ static int cpu_common_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg) return 0; } +bool target_words_bigendian(void); +static bool cpu_common_virtio_is_big_endian(CPUState *cpu) +{ + return target_words_bigendian(); +} void cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags) @@ -334,6 +339,7 @@ static void cpu_class_init(ObjectClass *klass, void *data) k->write_elf64_note = cpu_common_write_elf64_note; k->gdb_read_register = cpu_common_gdb_read_register; k->gdb_write_register = cpu_common_gdb_write_register; + k->virtio_is_big_endian = cpu_common_virtio_is_big_endian; dc->realize = cpu_common_realizefn; /* * Reason: CPUs still need special care by board code: wiring up From 616a655219a92ae7cf5d6a7862e6489c6282009e Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:38:54 +0200 Subject: [PATCH 26/37] virtio: add endian-ambivalent support to VirtIODevice Some CPU families can dynamically change their endianness. This means we can have little endian ppc or big endian arm guests for example. This has an impact on legacy virtio data structures since they are target endian. We hence introduce a new property to track the endianness of each virtio device. It is reasonnably assumed that endianness won't change while the device is in use : we hence capture the device endianness when it gets reset. We migrate this property in a subsection, after the device descriptor. This means the load code must not rely on it until it is restored. As a consequence, the vring sanity checks had to be moved after the call to vmstate_load_state(). We enforce paranoia by poisoning the property at the begining of virtio_load(). Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 8 +-- hw/virtio/virtio.c | 99 +++++++++++++++++++++++++++++++++----- include/hw/virtio/virtio.h | 13 +++-- 3 files changed, 101 insertions(+), 19 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index e11f759e94..317324f23d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -406,13 +406,13 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, break; case 2: val = virtio_config_readw(vdev, addr); - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap16(val); } break; case 4: val = virtio_config_readl(vdev, addr); - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap32(val); } break; @@ -440,13 +440,13 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr, virtio_config_writeb(vdev, addr, val); break; case 2: - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap16(val); } virtio_config_writew(vdev, addr, val); break; case 4: - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap32(val); } virtio_config_writel(vdev, addr, val); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7b317ce279..a0676e06af 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -545,6 +545,27 @@ void virtio_set_status(VirtIODevice *vdev, uint8_t val) vdev->status = val; } +bool target_words_bigendian(void); +static enum virtio_device_endian virtio_default_endian(void) +{ + if (target_words_bigendian()) { + return VIRTIO_DEVICE_ENDIAN_BIG; + } else { + return VIRTIO_DEVICE_ENDIAN_LITTLE; + } +} + +static enum virtio_device_endian virtio_current_cpu_endian(void) +{ + CPUClass *cc = CPU_GET_CLASS(current_cpu); + + if (cc->virtio_is_big_endian(current_cpu)) { + return VIRTIO_DEVICE_ENDIAN_BIG; + } else { + return VIRTIO_DEVICE_ENDIAN_LITTLE; + } +} + void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; @@ -552,6 +573,13 @@ void virtio_reset(void *opaque) int i; virtio_set_status(vdev, 0); + if (current_cpu) { + /* Guest initiated reset */ + vdev->device_endian = virtio_current_cpu_endian(); + } else { + /* System reset */ + vdev->device_endian = virtio_default_endian(); + } if (k->reset) { k->reset(vdev); @@ -840,6 +868,24 @@ void virtio_notify_config(VirtIODevice *vdev) virtio_notify_vector(vdev, vdev->config_vector); } +static bool virtio_device_endian_needed(void *opaque) +{ + VirtIODevice *vdev = opaque; + + assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); + return vdev->device_endian != virtio_default_endian(); +} + +static const VMStateDescription vmstate_virtio_device_endian = { + .name = "virtio/device_endian", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(device_endian, VirtIODevice), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio = { .name = "virtio", .version_id = 1, @@ -847,6 +893,13 @@ static const VMStateDescription vmstate_virtio = { .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_virtio_device_endian, + .needed = &virtio_device_endian_needed + }, + { 0 } } }; @@ -925,6 +978,12 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + /* + * We poison the endianness to ensure it does not get used before + * subsections have been loaded. + */ + vdev->device_endian = VIRTIO_DEVICE_ENDIAN_UNKNOWN; + if (k->load_config) { ret = k->load_config(qbus->parent, f); if (ret) @@ -977,18 +1036,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) vdev->vq[i].notification = true; if (vdev->vq[i].pa) { - uint16_t nheads; virtqueue_init(&vdev->vq[i]); - nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; - /* Check it isn't doing very strange things with descriptor numbers. */ - if (nheads > vdev->vq[i].vring.num) { - error_report("VQ %d size 0x%x Guest index 0x%x " - "inconsistent with Host index 0x%x: delta 0x%x", - i, vdev->vq[i].vring.num, - vring_avail_idx(&vdev->vq[i]), - vdev->vq[i].last_avail_idx, nheads); - return -1; - } } else if (vdev->vq[i].last_avail_idx) { error_report("VQ %d address 0x0 " "inconsistent with Host index 0x%x", @@ -1011,7 +1059,33 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } } - return vmstate_load_state(f, &vmstate_virtio, vdev, 1); + /* Subsections */ + ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1); + if (ret) { + return ret; + } + + if (vdev->device_endian == VIRTIO_DEVICE_ENDIAN_UNKNOWN) { + vdev->device_endian = virtio_default_endian(); + } + + for (i = 0; i < num; i++) { + if (vdev->vq[i].pa) { + uint16_t nheads; + nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; + /* Check it isn't doing strange things with descriptor numbers. */ + if (nheads > vdev->vq[i].vring.num) { + error_report("VQ %d size 0x%x Guest index 0x%x " + "inconsistent with Host index 0x%x: delta 0x%x", + i, vdev->vq[i].vring.num, + vring_avail_idx(&vdev->vq[i]), + vdev->vq[i].last_avail_idx, nheads); + return -1; + } + } + } + + return 0; } void virtio_cleanup(VirtIODevice *vdev) @@ -1068,6 +1142,7 @@ void virtio_init(VirtIODevice *vdev, const char *name, } vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); + vdev->device_endian = virtio_default_endian(); } hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 9000ee2f50..a60104ca24 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -104,6 +104,12 @@ typedef struct VirtQueueElement #define VIRTIO_DEVICE(obj) \ OBJECT_CHECK(VirtIODevice, (obj), TYPE_VIRTIO_DEVICE) +enum virtio_device_endian { + VIRTIO_DEVICE_ENDIAN_UNKNOWN, + VIRTIO_DEVICE_ENDIAN_LITTLE, + VIRTIO_DEVICE_ENDIAN_BIG, +}; + struct VirtIODevice { DeviceState parent_obj; @@ -121,6 +127,7 @@ struct VirtIODevice bool vm_running; VMChangeStateEntry *vmstate; char *bus_name; + uint8_t device_endian; }; typedef struct VirtioDeviceClass { @@ -256,9 +263,9 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); -bool target_words_bigendian(void); -static inline bool virtio_is_big_endian(void) +static inline bool virtio_is_big_endian(VirtIODevice *vdev) { - return target_words_bigendian(); + assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); + return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; } #endif From 0f5d1d2a49778863db54b4b1ac2dc008a8f21f11 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:39:55 +0200 Subject: [PATCH 27/37] virtio: memory accessors for endian-ambivalent targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the virtio-access.h header file taken from Rusty's "endian-ambivalent targets using legacy virtio" patch. It introduces helpers that should be used when accessing vring data or by drivers for data that contains headers. The virtio config space is also target endian, but the current code already handles that with the virtio_is_big_endian() helper. There is no obvious benefit at using the virtio accessors in this case. Now we have two distinct paths: a fast inline one for fixed endian targets, and a slow out-of-line one for targets that define the new TARGET_IS_BIENDIAN macro. Signed-off-by: Rusty Russell [ relicensed virtio-access.h to GPLv2+ on Rusty's request, pass &address_space_memory to physical memory accessors, per-device endianness, virtio tswap16 and tswap64 helpers, faspath for fixed endian targets, Greg Kurz ] Cc: Cédric Le Goater Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/virtio-access.h | 170 ++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 include/hw/virtio/virtio-access.h diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h new file mode 100644 index 0000000000..46456fd9da --- /dev/null +++ b/include/hw/virtio/virtio-access.h @@ -0,0 +1,170 @@ +/* + * Virtio Accessor Support: In case your target can change endian. + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Rusty Russell + * + * 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. + * + */ +#ifndef _QEMU_VIRTIO_ACCESS_H +#define _QEMU_VIRTIO_ACCESS_H +#include "hw/virtio/virtio.h" +#include "exec/address-spaces.h" + +static inline bool virtio_access_is_big_endian(VirtIODevice *vdev) +{ +#if defined(TARGET_IS_BIENDIAN) + return virtio_is_big_endian(vdev); +#elif defined(TARGET_WORDS_BIGENDIAN) + return true; +#else + return false; +#endif +} + +static inline uint16_t virtio_lduw_phys(VirtIODevice *vdev, hwaddr pa) +{ + if (virtio_access_is_big_endian(vdev)) { + return lduw_be_phys(&address_space_memory, pa); + } + return lduw_le_phys(&address_space_memory, pa); +} + +static inline uint32_t virtio_ldl_phys(VirtIODevice *vdev, hwaddr pa) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldl_be_phys(&address_space_memory, pa); + } + return ldl_le_phys(&address_space_memory, pa); +} + +static inline uint64_t virtio_ldq_phys(VirtIODevice *vdev, hwaddr pa) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldq_be_phys(&address_space_memory, pa); + } + return ldq_le_phys(&address_space_memory, pa); +} + +static inline void virtio_stw_phys(VirtIODevice *vdev, hwaddr pa, + uint16_t value) +{ + if (virtio_access_is_big_endian(vdev)) { + stw_be_phys(&address_space_memory, pa, value); + } else { + stw_le_phys(&address_space_memory, pa, value); + } +} + +static inline void virtio_stl_phys(VirtIODevice *vdev, hwaddr pa, + uint32_t value) +{ + if (virtio_access_is_big_endian(vdev)) { + stl_be_phys(&address_space_memory, pa, value); + } else { + stl_le_phys(&address_space_memory, pa, value); + } +} + +static inline void virtio_stw_p(VirtIODevice *vdev, void *ptr, uint16_t v) +{ + if (virtio_access_is_big_endian(vdev)) { + stw_be_p(ptr, v); + } else { + stw_le_p(ptr, v); + } +} + +static inline void virtio_stl_p(VirtIODevice *vdev, void *ptr, uint32_t v) +{ + if (virtio_access_is_big_endian(vdev)) { + stl_be_p(ptr, v); + } else { + stl_le_p(ptr, v); + } +} + +static inline void virtio_stq_p(VirtIODevice *vdev, void *ptr, uint64_t v) +{ + if (virtio_access_is_big_endian(vdev)) { + stq_be_p(ptr, v); + } else { + stq_le_p(ptr, v); + } +} + +static inline int virtio_lduw_p(VirtIODevice *vdev, const void *ptr) +{ + if (virtio_access_is_big_endian(vdev)) { + return lduw_be_p(ptr); + } else { + return lduw_le_p(ptr); + } +} + +static inline int virtio_ldl_p(VirtIODevice *vdev, const void *ptr) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldl_be_p(ptr); + } else { + return ldl_le_p(ptr); + } +} + +static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldq_be_p(ptr); + } else { + return ldq_le_p(ptr); + } +} + +static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? s : bswap16(s); +#else + return virtio_access_is_big_endian(vdev) ? bswap16(s) : s; +#endif +} + +static inline void virtio_tswap16s(VirtIODevice *vdev, uint16_t *s) +{ + *s = virtio_tswap16(vdev, *s); +} + +static inline uint32_t virtio_tswap32(VirtIODevice *vdev, uint32_t s) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? s : bswap32(s); +#else + return virtio_access_is_big_endian(vdev) ? bswap32(s) : s; +#endif +} + +static inline void virtio_tswap32s(VirtIODevice *vdev, uint32_t *s) +{ + *s = virtio_tswap32(vdev, *s); +} + +static inline uint64_t virtio_tswap64(VirtIODevice *vdev, uint64_t s) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? s : bswap64(s); +#else + return virtio_access_is_big_endian(vdev) ? bswap64(s) : s; +#endif +} + +static inline void virtio_tswap64s(VirtIODevice *vdev, uint64_t *s) +{ + *s = virtio_tswap64(vdev, *s); +} +#endif /* _QEMU_VIRTIO_ACCESS_H */ From cee3ca002855f87b78aaa7769efeb90321757e49 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Jun 2014 19:40:16 +0200 Subject: [PATCH 28/37] virtio: allow byte swapping for vring Quoting original text from Rusty: "This is based on a simpler patch by Anthony Liguouri". Signed-off-by: Rusty Russell [ add VirtIODevice * argument to most helpers, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Thomas Huth Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 89 +++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 41 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index a0676e06af..5c981801f3 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -20,6 +20,7 @@ #include "qemu/atomic.h" #include "hw/virtio/virtio-bus.h" #include "migration/migration.h" +#include "hw/virtio/virtio-access.h" /* * The alignment to use between consumer and producer parts of vring. @@ -102,53 +103,56 @@ static void virtqueue_init(VirtQueue *vq) vq->vring.align); } -static inline uint64_t vring_desc_addr(hwaddr desc_pa, int i) +static inline uint64_t vring_desc_addr(VirtIODevice *vdev, hwaddr desc_pa, + int i) { hwaddr pa; pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr); - return ldq_phys(&address_space_memory, pa); + return virtio_ldq_phys(vdev, pa); } -static inline uint32_t vring_desc_len(hwaddr desc_pa, int i) +static inline uint32_t vring_desc_len(VirtIODevice *vdev, hwaddr desc_pa, int i) { hwaddr pa; pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, len); - return ldl_phys(&address_space_memory, pa); + return virtio_ldl_phys(vdev, pa); } -static inline uint16_t vring_desc_flags(hwaddr desc_pa, int i) +static inline uint16_t vring_desc_flags(VirtIODevice *vdev, hwaddr desc_pa, + int i) { hwaddr pa; pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags); - return lduw_phys(&address_space_memory, pa); + return virtio_lduw_phys(vdev, pa); } -static inline uint16_t vring_desc_next(hwaddr desc_pa, int i) +static inline uint16_t vring_desc_next(VirtIODevice *vdev, hwaddr desc_pa, + int i) { hwaddr pa; pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, next); - return lduw_phys(&address_space_memory, pa); + return virtio_lduw_phys(vdev, pa); } static inline uint16_t vring_avail_flags(VirtQueue *vq) { hwaddr pa; pa = vq->vring.avail + offsetof(VRingAvail, flags); - return lduw_phys(&address_space_memory, pa); + return virtio_lduw_phys(vq->vdev, pa); } static inline uint16_t vring_avail_idx(VirtQueue *vq) { hwaddr pa; pa = vq->vring.avail + offsetof(VRingAvail, idx); - return lduw_phys(&address_space_memory, pa); + return virtio_lduw_phys(vq->vdev, pa); } static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) { hwaddr pa; pa = vq->vring.avail + offsetof(VRingAvail, ring[i]); - return lduw_phys(&address_space_memory, pa); + return virtio_lduw_phys(vq->vdev, pa); } static inline uint16_t vring_used_event(VirtQueue *vq) @@ -160,44 +164,44 @@ static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) { hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, ring[i].id); - stl_phys(&address_space_memory, pa, val); + virtio_stl_phys(vq->vdev, pa, val); } static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val) { hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, ring[i].len); - stl_phys(&address_space_memory, pa, val); + virtio_stl_phys(vq->vdev, pa, val); } static uint16_t vring_used_idx(VirtQueue *vq) { hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, idx); - return lduw_phys(&address_space_memory, pa); + return virtio_lduw_phys(vq->vdev, pa); } static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val) { hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, idx); - stw_phys(&address_space_memory, pa, val); + virtio_stw_phys(vq->vdev, pa, val); } static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) { + VirtIODevice *vdev = vq->vdev; hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, flags); - stw_phys(&address_space_memory, - pa, lduw_phys(&address_space_memory, pa) | mask); + virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) | mask); } static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) { + VirtIODevice *vdev = vq->vdev; hwaddr pa; pa = vq->vring.used + offsetof(VRingUsed, flags); - stw_phys(&address_space_memory, - pa, lduw_phys(&address_space_memory, pa) & ~mask); + virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) & ~mask); } static inline void vring_avail_event(VirtQueue *vq, uint16_t val) @@ -207,7 +211,7 @@ static inline void vring_avail_event(VirtQueue *vq, uint16_t val) return; } pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]); - stw_phys(&address_space_memory, pa, val); + virtio_stw_phys(vq->vdev, pa, val); } void virtio_queue_set_notification(VirtQueue *vq, int enable) @@ -324,17 +328,18 @@ static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx) return head; } -static unsigned virtqueue_next_desc(hwaddr desc_pa, +static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa, unsigned int i, unsigned int max) { unsigned int next; /* If this descriptor says it doesn't chain, we're done. */ - if (!(vring_desc_flags(desc_pa, i) & VRING_DESC_F_NEXT)) + if (!(vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_NEXT)) { return max; + } /* Check they're not leading us off end of descriptors. */ - next = vring_desc_next(desc_pa, i); + next = vring_desc_next(vdev, desc_pa, i); /* Make sure compiler knows to grab that: we don't want it changing! */ smp_wmb(); @@ -357,6 +362,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, total_bufs = in_total = out_total = 0; while (virtqueue_num_heads(vq, idx)) { + VirtIODevice *vdev = vq->vdev; unsigned int max, num_bufs, indirect = 0; hwaddr desc_pa; int i; @@ -366,8 +372,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, i = virtqueue_get_head(vq, idx++); desc_pa = vq->vring.desc; - if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) { - if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) { + if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) { + if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) { error_report("Invalid size for indirect buffer table"); exit(1); } @@ -380,8 +386,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, /* loop over the indirect descriptor table */ indirect = 1; - max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); - desc_pa = vring_desc_addr(desc_pa, i); + max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc); + desc_pa = vring_desc_addr(vdev, desc_pa, i); num_bufs = i = 0; } @@ -392,15 +398,15 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, exit(1); } - if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { - in_total += vring_desc_len(desc_pa, i); + if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) { + in_total += vring_desc_len(vdev, desc_pa, i); } else { - out_total += vring_desc_len(desc_pa, i); + out_total += vring_desc_len(vdev, desc_pa, i); } if (in_total >= max_in_bytes && out_total >= max_out_bytes) { goto done; } - } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max); + } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max); if (!indirect) total_bufs = num_bufs; @@ -451,6 +457,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) { unsigned int i, head, max; hwaddr desc_pa = vq->vring.desc; + VirtIODevice *vdev = vq->vdev; if (!virtqueue_num_heads(vq, vq->last_avail_idx)) return 0; @@ -461,19 +468,19 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); - if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(vq, vring_avail_idx(vq)); } - if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) { - if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) { + if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) { + if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) { error_report("Invalid size for indirect buffer table"); exit(1); } /* loop over the indirect descriptor table */ - max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); - desc_pa = vring_desc_addr(desc_pa, i); + max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc); + desc_pa = vring_desc_addr(vdev, desc_pa, i); i = 0; } @@ -481,30 +488,30 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) do { struct iovec *sg; - if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { + if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) { if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) { error_report("Too many write descriptors in indirect table"); exit(1); } - elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i); + elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i); sg = &elem->in_sg[elem->in_num++]; } else { if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) { error_report("Too many read descriptors in indirect table"); exit(1); } - elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i); + elem->out_addr[elem->out_num] = vring_desc_addr(vdev, desc_pa, i); sg = &elem->out_sg[elem->out_num++]; } - sg->iov_len = vring_desc_len(desc_pa, i); + sg->iov_len = vring_desc_len(vdev, desc_pa, i); /* If we've got too many, that implies a descriptor loop. */ if ((elem->in_num + elem->out_num) > max) { error_report("Looped descriptor"); exit(1); } - } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max); + } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max); /* Now map what we have collected */ virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1); From 1399c60d70a261acbeb65f614a00eab2dbf4237b Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Jun 2014 19:42:54 +0200 Subject: [PATCH 29/37] virtio-net: use virtio wrappers to access headers Signed-off-by: Rusty Russell Reviewed-by: Anthony Liguori [ pass VirtIODevice * to memory accessors, converted new tswap locations to virtio_tswap, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index eead70e5a8..268eff9df8 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -23,6 +23,7 @@ #include "hw/virtio/virtio-bus.h" #include "qapi/qmp/qjson.h" #include "qapi-event.h" +#include "hw/virtio/virtio-access.h" #define VIRTIO_NET_VM_VERSION 11 @@ -72,8 +73,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_config netcfg; - stw_p(&netcfg.status, n->status); - stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); + virtio_stw_p(vdev, &netcfg.status, n->status); + virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, n->config_size); } @@ -604,6 +605,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + VirtIODevice *vdev = VIRTIO_DEVICE(n); struct virtio_net_ctrl_mac mac_data; size_t s; NetClientState *nc = qemu_get_queue(n->nic); @@ -632,7 +634,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); - mac_data.entries = ldl_p(&mac_data.entries); + mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); if (s != sizeof(mac_data.entries)) { goto error; } @@ -659,7 +661,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); - mac_data.entries = ldl_p(&mac_data.entries); + mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); if (s != sizeof(mac_data.entries)) { goto error; } @@ -699,12 +701,13 @@ error: static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + VirtIODevice *vdev = VIRTIO_DEVICE(n); uint16_t vid; size_t s; NetClientState *nc = qemu_get_queue(n->nic); s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); - vid = lduw_p(&vid); + vid = virtio_lduw_p(vdev, &vid); if (s != sizeof(vid)) { return VIRTIO_NET_ERR; } @@ -758,7 +761,7 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - queues = lduw_p(&mq.virtqueue_pairs); + queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || @@ -875,12 +878,12 @@ static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) return 1; } -static void virtio_net_hdr_swap(struct virtio_net_hdr *hdr) +static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) { - tswap16s(&hdr->hdr_len); - tswap16s(&hdr->gso_size); - tswap16s(&hdr->csum_start); - tswap16s(&hdr->csum_offset); + virtio_tswap16s(vdev, &hdr->hdr_len); + virtio_tswap16s(vdev, &hdr->gso_size); + virtio_tswap16s(vdev, &hdr->csum_start); + virtio_tswap16s(vdev, &hdr->csum_offset); } /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so @@ -918,7 +921,7 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); - virtio_net_hdr_swap(wbuf); + virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1068,7 +1071,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } if (mhdr_cnt) { - stw_p(&mhdr.num_buffers, i); + virtio_stw_p(vdev, &mhdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, &mhdr.num_buffers, sizeof mhdr.num_buffers); @@ -1132,7 +1135,7 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) error_report("virtio-net header incorrect"); exit(1); } - virtio_net_hdr_swap((void *) out_sg[0].iov_base); + virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base); } /* From 8609d2a87aff1e4b45fa96f7373d9f8e07951deb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Jun 2014 19:43:22 +0200 Subject: [PATCH 30/37] virtio-balloon: use virtio wrappers to access page frame numbers Signed-off-by: Rusty Russell Reviewed-by: Anthony Liguori [ pass VirtIODevice * to memory accessors, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-balloon.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index e0ed5eee03..2c30b3d8bd 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -31,6 +31,7 @@ #endif #include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-access.h" static void balloon_page(void *addr, int deflate) { @@ -206,8 +207,9 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) { ram_addr_t pa; ram_addr_t addr; + int p = virtio_ldl_p(vdev, &pfn); - pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT; + pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; /* FIXME: remove get_system_memory(), but how? */ @@ -248,8 +250,8 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) while (iov_to_buf(elem->out_sg, elem->out_num, offset, &stat, sizeof(stat)) == sizeof(stat)) { - uint16_t tag = tswap16(stat.tag); - uint64_t val = tswap64(stat.val); + uint16_t tag = virtio_tswap16(vdev, stat.tag); + uint64_t val = virtio_tswap64(vdev, stat.val); offset += sizeof(stat); if (tag < VIRTIO_BALLOON_S_NR) From 783d189725d937f8ed9dae68c7e35b6afa0691da Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Jun 2014 19:43:44 +0200 Subject: [PATCH 31/37] virtio-blk: use virtio wrappers to access headers Note that st*_raw and ld*_raw are effectively replaced by st*_p and ld*_p. Signed-off-by: Rusty Russell Reviewed-by: Anthony Liguori [ pass VirtIODevice * to memory accessors, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/virtio-blk.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index b06af8c208..e59ebc962d 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -27,6 +27,7 @@ # include #endif #include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-access.h" static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) { @@ -88,7 +89,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret) trace_virtio_blk_rw_complete(req, ret); if (ret) { - bool is_read = !(ldl_p(&req->out.type) & VIRTIO_BLK_T_OUT); + int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); + bool is_read = !(p & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) return; } @@ -130,6 +132,8 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, { int status = VIRTIO_BLK_S_OK; struct virtio_scsi_inhdr *scsi = NULL; + VirtIODevice *vdev = VIRTIO_DEVICE(blk); + #ifdef __linux__ int i; struct sg_io_hdr hdr; @@ -224,12 +228,12 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, hdr.status = CHECK_CONDITION; } - stl_p(&scsi->errors, - hdr.status | (hdr.msg_status << 8) | - (hdr.host_status << 16) | (hdr.driver_status << 24)); - stl_p(&scsi->residual, hdr.resid); - stl_p(&scsi->sense_len, hdr.sb_len_wr); - stl_p(&scsi->data_len, hdr.dxfer_len); + virtio_stl_p(vdev, &scsi->errors, + hdr.status | (hdr.msg_status << 8) | + (hdr.host_status << 16) | (hdr.driver_status << 24)); + virtio_stl_p(vdev, &scsi->residual, hdr.resid); + virtio_stl_p(vdev, &scsi->sense_len, hdr.sb_len_wr); + virtio_stl_p(vdev, &scsi->data_len, hdr.dxfer_len); return status; #else @@ -239,7 +243,7 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, fail: /* Just put anything nonzero so that the ioctl fails in the guest. */ if (scsi) { - stl_p(&scsi->errors, 255); + virtio_stl_p(vdev, &scsi->errors, 255); } return status; } @@ -289,7 +293,7 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) BlockRequest *blkreq; uint64_t sector; - sector = ldq_p(&req->out.sector); + sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE); @@ -323,7 +327,7 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) { uint64_t sector; - sector = ldq_p(&req->out.sector); + sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); @@ -374,7 +378,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) - sizeof(struct virtio_blk_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - type = ldl_p(&req->out.type); + type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); if (type & VIRTIO_BLK_T_FLUSH) { virtio_blk_handle_flush(req, mrb); @@ -504,12 +508,12 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) bdrv_get_geometry(s->bs, &capacity); memset(&blkcfg, 0, sizeof(blkcfg)); - stq_p(&blkcfg.capacity, capacity); - stl_p(&blkcfg.seg_max, 128 - 2); - stw_p(&blkcfg.cylinders, s->conf->cyls); - stl_p(&blkcfg.blk_size, blk_size); - stw_p(&blkcfg.min_io_size, s->conf->min_io_size / blk_size); - stw_p(&blkcfg.opt_io_size, s->conf->opt_io_size / blk_size); + virtio_stq_p(vdev, &blkcfg.capacity, capacity); + virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2); + virtio_stw_p(vdev, &blkcfg.cylinders, s->conf->cyls); + virtio_stl_p(vdev, &blkcfg.blk_size, blk_size); + virtio_stw_p(vdev, &blkcfg.min_io_size, s->conf->min_io_size / blk_size); + virtio_stw_p(vdev, &blkcfg.opt_io_size, s->conf->opt_io_size / blk_size); blkcfg.heads = s->conf->heads; /* * We must ensure that the block device capacity is a multiple of From 8c085dbe6d1e3fb3f18cdd05e67980a529a3ba4a Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Jun 2014 19:48:53 +0200 Subject: [PATCH 32/37] virtio-scsi: use virtio wrappers to access headers Note that st*_raw and ld*_raw are effectively replaced by st*_p and ld*_p. Signed-off-by: Rusty Russell Reviewed-by: Anthony Liguori [ pass VirtIODevice * to memory accessors, converted new tswap locations to virtio_tswap, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/scsi/virtio-scsi.c | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 6b4fd6f625..04ecfa7e9a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -19,6 +19,7 @@ #include #include #include +#include "hw/virtio/virtio-access.h" typedef struct VirtIOSCSIReq { VirtIOSCSI *dev; @@ -235,7 +236,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ req->resp.tmf.response = VIRTIO_SCSI_S_OK; - tswap32s(&req->req.tmf.subtype); + virtio_tswap32s(VIRTIO_DEVICE(s), &req->req.tmf.subtype); switch (req->req.tmf.subtype) { case VIRTIO_SCSI_T_TMF_ABORT_TASK: case VIRTIO_SCSI_T_TMF_QUERY_TASK: @@ -346,7 +347,7 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) continue; } - tswap32s(&req->req.tmf.type); + virtio_tswap32s(vdev, &req->req.tmf.type); if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) { if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), sizeof(VirtIOSCSICtrlTMFResp)) < 0) { @@ -384,6 +385,7 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, VirtIOSCSIReq *req = r->hba_private; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; + VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); if (r->io_canceled) { return; @@ -392,14 +394,14 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, req->resp.cmd.response = VIRTIO_SCSI_S_OK; req->resp.cmd.status = status; if (req->resp.cmd.status == GOOD) { - req->resp.cmd.resid = tswap32(resid); + req->resp.cmd.resid = virtio_tswap32(vdev, resid); } else { req->resp.cmd.resid = 0; sense_len = scsi_req_get_sense(r, sense, sizeof(sense)); sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd)); qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd), &req->resp, sense_len); - req->resp.cmd.sense_len = tswap32(sense_len); + req->resp.cmd.sense_len = virtio_tswap32(vdev, sense_len); } virtio_scsi_complete_cmd_req(req); } @@ -487,16 +489,16 @@ static void virtio_scsi_get_config(VirtIODevice *vdev, VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(vdev); - stl_p(&scsiconf->num_queues, s->conf.num_queues); - stl_p(&scsiconf->seg_max, 128 - 2); - stl_p(&scsiconf->max_sectors, s->conf.max_sectors); - stl_p(&scsiconf->cmd_per_lun, s->conf.cmd_per_lun); - stl_p(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); - stl_p(&scsiconf->sense_size, s->sense_size); - stl_p(&scsiconf->cdb_size, s->cdb_size); - stw_p(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); - stw_p(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); - stl_p(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); + virtio_stl_p(vdev, &scsiconf->num_queues, s->conf.num_queues); + virtio_stl_p(vdev, &scsiconf->seg_max, 128 - 2); + virtio_stl_p(vdev, &scsiconf->max_sectors, s->conf.max_sectors); + virtio_stl_p(vdev, &scsiconf->cmd_per_lun, s->conf.cmd_per_lun); + virtio_stl_p(vdev, &scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); + virtio_stl_p(vdev, &scsiconf->sense_size, s->sense_size); + virtio_stl_p(vdev, &scsiconf->cdb_size, s->cdb_size); + virtio_stw_p(vdev, &scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); + virtio_stw_p(vdev, &scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); + virtio_stl_p(vdev, &scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); } static void virtio_scsi_set_config(VirtIODevice *vdev, @@ -505,14 +507,14 @@ static void virtio_scsi_set_config(VirtIODevice *vdev, VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - if ((uint32_t) ldl_p(&scsiconf->sense_size) >= 65536 || - (uint32_t) ldl_p(&scsiconf->cdb_size) >= 256) { + if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) >= 65536 || + (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) >= 256) { error_report("bad data written to virtio-scsi configuration space"); exit(1); } - vs->sense_size = ldl_p(&scsiconf->sense_size); - vs->cdb_size = ldl_p(&scsiconf->cdb_size); + vs->sense_size = virtio_ldl_p(vdev, &scsiconf->sense_size); + vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size); } static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, From e0ab7fac65dc75573d3f02105c80981be4f49c2d Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 24 Jun 2014 19:49:31 +0200 Subject: [PATCH 33/37] virtio-serial-bus: use virtio wrappers to access headers We also fix max_nr_ports at reset time as the device endianness may have changed. Signed-off-by: Rusty Russell Reviewed-by: Anthony Liguori [ pass VirtIODevice * to memory accessors, fix max_nr_ports at reset time, Greg Kurz ] Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/char/virtio-serial-bus.c | 46 +++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index b8af1b18cd..07bebc03ac 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -24,6 +24,7 @@ #include "hw/sysbus.h" #include "trace.h" #include "hw/virtio/virtio-serial.h" +#include "hw/virtio/virtio-access.h" static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) { @@ -183,11 +184,12 @@ static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) static size_t send_control_event(VirtIOSerial *vser, uint32_t port_id, uint16_t event, uint16_t value) { + VirtIODevice *vdev = VIRTIO_DEVICE(vser); struct virtio_console_control cpkt; - stl_p(&cpkt.id, port_id); - stw_p(&cpkt.event, event); - stw_p(&cpkt.value, value); + virtio_stl_p(vdev, &cpkt.id, port_id); + virtio_stw_p(vdev, &cpkt.event, event); + virtio_stw_p(vdev, &cpkt.value, value); trace_virtio_serial_send_control_event(port_id, event, value); return send_control_msg(vser, &cpkt, sizeof(cpkt)); @@ -278,6 +280,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) /* Guest wants to notify us of some event */ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) { + VirtIODevice *vdev = VIRTIO_DEVICE(vser); struct VirtIOSerialPort *port; VirtIOSerialPortClass *vsc; struct virtio_console_control cpkt, *gcpkt; @@ -291,8 +294,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) return; } - cpkt.event = lduw_p(&gcpkt->event); - cpkt.value = lduw_p(&gcpkt->value); + cpkt.event = virtio_lduw_p(vdev, &gcpkt->event); + cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); @@ -312,10 +315,10 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) return; } - port = find_port_by_id(vser, ldl_p(&gcpkt->id)); + port = find_port_by_id(vser, virtio_ldl_p(vdev, &gcpkt->id)); if (!port) { error_report("virtio-serial-bus: Unexpected port id %u for device %s", - ldl_p(&gcpkt->id), vser->bus.qbus.name); + virtio_ldl_p(vdev, &gcpkt->id), vser->bus.qbus.name); return; } @@ -342,9 +345,9 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) } if (port->name) { - stl_p(&cpkt.id, port->id); - stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); - stw_p(&cpkt.value, 1); + virtio_stl_p(vdev, &cpkt.id, port->id); + virtio_stw_p(vdev, &cpkt.event, VIRTIO_CONSOLE_PORT_NAME); + virtio_stw_p(vdev, &cpkt.value, 1); buffer_len = sizeof(cpkt) + strlen(port->name) + 1; buffer = g_malloc(buffer_len); @@ -510,6 +513,10 @@ static void vser_reset(VirtIODevice *vdev) vser = VIRTIO_SERIAL(vdev); guest_reset(vser); + + /* In case we have switched endianness */ + vser->config.max_nr_ports = + virtio_tswap32(vdev, vser->serial.max_virtserial_ports); } static void virtio_serial_save(QEMUFile *f, void *opaque) @@ -532,7 +539,7 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) qemu_put_be32s(f, &s->config.max_nr_ports); /* The ports map */ - max_nr_ports = tswap32(s->config.max_nr_ports); + max_nr_ports = virtio_tswap32(vdev, s->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_put_be32s(f, &s->ports_map[i]); } @@ -688,6 +695,12 @@ static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_be16s(f, (uint16_t *) &tmp); qemu_get_be32s(f, &tmp); + /* Note: this is the only location where we use tswap32() instead of + * virtio_tswap32() because: + * - virtio_tswap32() only makes sense when the device is fully restored + * - the target endianness that was used to populate s->config is + * necessarly the default one + */ max_nr_ports = tswap32(s->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_get_be32s(f, &ports_map); @@ -751,9 +764,10 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) /* This function is only used if a port id is not provided by the user */ static uint32_t find_free_port_id(VirtIOSerial *vser) { + VirtIODevice *vdev = VIRTIO_DEVICE(vser); unsigned int i, max_nr_ports; - max_nr_ports = tswap32(vser->config.max_nr_ports); + max_nr_ports = virtio_tswap32(vdev, vser->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { uint32_t map, bit; @@ -806,6 +820,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); VirtIOSerialBus *bus = VIRTIO_SERIAL_BUS(qdev_get_parent_bus(dev)); + VirtIODevice *vdev = VIRTIO_DEVICE(bus->vser); int max_nr_ports; bool plugging_port0; Error *err = NULL; @@ -841,7 +856,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) } } - max_nr_ports = tswap32(port->vser->config.max_nr_ports); + max_nr_ports = virtio_tswap32(vdev, port->vser->config.max_nr_ports); if (port->id >= max_nr_ports) { error_setg(errp, "virtio-serial-bus: Out-of-range port id specified, " "max. allowed: %u", max_nr_ports - 1); @@ -863,7 +878,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) add_port(port->vser, port->id); /* Send an update to the guest about this new port added */ - virtio_notify_config(VIRTIO_DEVICE(port->vser)); + virtio_notify_config(vdev); } static void virtser_port_device_unrealize(DeviceState *dev, Error **errp) @@ -942,7 +957,8 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); } - vser->config.max_nr_ports = tswap32(vser->serial.max_virtserial_ports); + vser->config.max_nr_ports = + virtio_tswap32(vdev, vser->serial.max_virtserial_ports); vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32) * sizeof(vser->ports_map[0])); /* From d64ccb91adda46197702ec3a8b6d9c85c03b3c80 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:49:49 +0200 Subject: [PATCH 34/37] virtio-9p: use virtio wrappers to access headers Note that st*_raw and ld*_raw are effectively replaced by st*_p and ld*_p. Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/9pfs/virtio-9p-device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 653762af1a..2572747629 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -19,6 +19,7 @@ #include "fsdev/qemu-fsdev.h" #include "virtio-9p-xattr.h" #include "virtio-9p-coth.h" +#include "hw/virtio/virtio-access.h" static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) { @@ -34,7 +35,7 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) len = strlen(s->tag); cfg = g_malloc0(sizeof(struct virtio_9p_config) + len); - stw_p(&cfg->tag_len, len); + virtio_stw_p(vdev, &cfg->tag_len, len); /* We don't copy the terminating null to config space */ memcpy(cfg->tag, s->tag, len); memcpy(config, cfg, s->config_size); From 7826c2b2a46988c278fbea5e1e376cf783f8bc46 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:51:19 +0200 Subject: [PATCH 35/37] target-ppc: enable virtio endian ambivalent support The device endianness is the cpu endianness at device reset time. Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- target-ppc/cpu.h | 2 ++ target-ppc/translate_init.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h index 08ae527cb6..b64c65295f 100644 --- a/target-ppc/cpu.h +++ b/target-ppc/cpu.h @@ -29,6 +29,8 @@ #define TARGET_LONG_BITS 64 #define TARGET_PAGE_BITS 12 +#define TARGET_IS_BIENDIAN 1 + /* Note that the official physical address space bits is 62-M where M is implementation dependent. I've not looked up M for the set of cpus we emulate at the system level. */ diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index a3bb336e16..2ab281069c 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -9597,6 +9597,18 @@ static void ppc_cpu_reset(CPUState *s) tlb_flush(s, 1); } +#ifndef CONFIG_USER_ONLY +static bool ppc_cpu_is_big_endian(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + cpu_synchronize_state(cs); + + return !msr_le; +} +#endif + static void ppc_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -9692,6 +9704,9 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #else cc->gdb_core_xml_file = "power-core.xml"; #endif +#ifndef CONFIG_USER_ONLY + cc->virtio_is_big_endian = ppc_cpu_is_big_endian; +#endif dc->fw_name = "PowerPC,UNKNOWN"; } From 371df9f5e0f175a1d8f2e9f2e86cf65f952b1c56 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:55:03 +0200 Subject: [PATCH 36/37] vhost-net: disable when cross-endian As of today, vhost assumes guest and host have the same endianness. This is definitely not compatible with modern PPC64 and ARM that can change endianness at runtime. Let's disable vhost-net and print an error message when we detect such a case: qemu-system-ppc64: vhost-net does not support cross-endian qemu-system-ppc64: unable to start vhost net: 38: falling back on userspace virtio This way users can continue to run VMs without changing their setup and have a chance to know that performance will be impacted. Suggested-by: Michael S. Tsirkin Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/vhost_net.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 7ac7c21bdb..f87c79824b 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -275,6 +275,19 @@ static void vhost_net_stop_one(struct vhost_net *net, vhost_dev_disable_notifiers(&net->dev, dev); } +static bool vhost_net_device_endian_ok(VirtIODevice *vdev) +{ +#ifdef TARGET_IS_BIENDIAN +#ifdef HOST_WORDS_BIGENDIAN + return virtio_is_big_endian(vdev); +#else + return !virtio_is_big_endian(vdev); +#endif +#else + return true; +#endif +} + int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues) { @@ -283,6 +296,12 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int r, i = 0; + if (!vhost_net_device_endian_ok(dev)) { + error_report("vhost-net does not support cross-endian"); + r = -ENOSYS; + goto err; + } + if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); r = -ENOSYS; From b4900c0e8a606bed834bd610fbae0fdf1d697ff9 Mon Sep 17 00:00:00 2001 From: Hu Tao Date: Fri, 20 Jun 2014 13:55:43 +0800 Subject: [PATCH 37/37] tests: add human format test for string output visitor Signed-off-by: Hu Tao Acked-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/test-string-output-visitor.c | 109 ++++++++++++++++++++++++----- 1 file changed, 90 insertions(+), 19 deletions(-) diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c index 28e7359a2a..e89e43c3ef 100644 --- a/tests/test-string-output-visitor.c +++ b/tests/test-string-output-visitor.c @@ -21,12 +21,25 @@ typedef struct TestOutputVisitorData { StringOutputVisitor *sov; Visitor *ov; + bool human; } TestOutputVisitorData; static void visitor_output_setup(TestOutputVisitorData *data, const void *unused) { - data->sov = string_output_visitor_new(false); + data->human = false; + data->sov = string_output_visitor_new(data->human); + g_assert(data->sov != NULL); + + data->ov = string_output_get_visitor(data->sov); + g_assert(data->ov != NULL); +} + +static void visitor_output_setup_human(TestOutputVisitorData *data, + const void *unused) +{ + data->human = true; + data->sov = string_output_visitor_new(data->human); g_assert(data->sov != NULL); data->ov = string_output_get_visitor(data->sov); @@ -53,7 +66,11 @@ static void test_visitor_out_int(TestOutputVisitorData *data, str = string_output_get_string(data->sov); g_assert(str != NULL); - g_assert_cmpstr(str, ==, "42"); + if (data->human) { + g_assert_cmpstr(str, ==, "42 (0x2a)"); + } else { + g_assert_cmpstr(str, ==, "42"); + } g_free(str); } @@ -78,8 +95,15 @@ static void test_visitor_out_intList(TestOutputVisitorData *data, str = string_output_get_string(data->sov); g_assert(str != NULL); - g_assert_cmpstr(str, ==, - "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807"); + if (data->human) { + g_assert_cmpstr(str, ==, + "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807 " + "(0x0-0x1,0x3-0x6,0x9-0x10,0x15-0x16," + "0x7ffffffffffffffe-0x7fffffffffffffff)"); + } else { + g_assert_cmpstr(str, ==, + "0-1,3-6,9-16,21-22,9223372036854775806-9223372036854775807"); + } g_free(str); while (list) { intList *tmp2; @@ -125,6 +149,7 @@ static void test_visitor_out_string(TestOutputVisitorData *data, const void *unused) { char *string = (char *) "Q E M U"; + const char *string_human = "\"Q E M U\""; Error *err = NULL; char *str; @@ -133,7 +158,11 @@ static void test_visitor_out_string(TestOutputVisitorData *data, str = string_output_get_string(data->sov); g_assert(str != NULL); - g_assert_cmpstr(str, ==, string); + if (data->human) { + g_assert_cmpstr(str, ==, string_human); + } else { + g_assert_cmpstr(str, ==, string); + } g_free(str); } @@ -150,7 +179,11 @@ static void test_visitor_out_no_string(TestOutputVisitorData *data, str = string_output_get_string(data->sov); g_assert(str != NULL); - g_assert_cmpstr(str, ==, ""); + if (data->human) { + g_assert_cmpstr(str, ==, ""); + } else { + g_assert_cmpstr(str, ==, ""); + } g_free(str); } @@ -162,12 +195,26 @@ static void test_visitor_out_enum(TestOutputVisitorData *data, EnumOne i; for (i = 0; i < ENUM_ONE_MAX; i++) { + char *str_human; + int len; + visit_type_EnumOne(data->ov, &i, "unused", &err); g_assert(!err); + len = strlen(EnumOne_lookup[i]) + 2; + str_human = g_malloc0(len); + str_human[0] = '"'; + strncpy(str_human + 1, EnumOne_lookup[i], strlen(EnumOne_lookup[i])); + str_human[len - 1] = '"'; + str = string_output_get_string(data->sov); g_assert(str != NULL); - g_assert_cmpstr(str, ==, EnumOne_lookup[i]); + if (data->human) { + g_assert_cmpstr(str, ==, str_human); + } else { + g_assert_cmpstr(str, ==, EnumOne_lookup[i]); + } + g_free(str_human); g_free(str); } } @@ -186,11 +233,15 @@ static void test_visitor_out_enum_errors(TestOutputVisitorData *data, } } -static void output_visitor_test_add(const char *testpath, - TestOutputVisitorData *data, - void (*test_func)(TestOutputVisitorData *data, const void *user_data)) +static void +output_visitor_test_add(const char *testpath, + TestOutputVisitorData *data, + void (*test_func)(TestOutputVisitorData *data, + const void *user_data), + bool human) { - g_test_add(testpath, TestOutputVisitorData, data, visitor_output_setup, + g_test_add(testpath, TestOutputVisitorData, data, + human ? visitor_output_setup_human : visitor_output_setup, test_func, visitor_output_teardown); } @@ -201,21 +252,41 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); output_visitor_test_add("/string-visitor/output/int", - &out_visitor_data, test_visitor_out_int); + &out_visitor_data, test_visitor_out_int, false); + output_visitor_test_add("/string-visitor/output/int", + &out_visitor_data, test_visitor_out_int, true); output_visitor_test_add("/string-visitor/output/bool", - &out_visitor_data, test_visitor_out_bool); + &out_visitor_data, test_visitor_out_bool, false); + output_visitor_test_add("/string-visitor/output/bool", + &out_visitor_data, test_visitor_out_bool, true); output_visitor_test_add("/string-visitor/output/number", - &out_visitor_data, test_visitor_out_number); + &out_visitor_data, test_visitor_out_number, false); + output_visitor_test_add("/string-visitor/output/number", + &out_visitor_data, test_visitor_out_number, true); output_visitor_test_add("/string-visitor/output/string", - &out_visitor_data, test_visitor_out_string); + &out_visitor_data, test_visitor_out_string, false); + output_visitor_test_add("/string-visitor/output/string", + &out_visitor_data, test_visitor_out_string, true); output_visitor_test_add("/string-visitor/output/no-string", - &out_visitor_data, test_visitor_out_no_string); + &out_visitor_data, test_visitor_out_no_string, + false); + output_visitor_test_add("/string-visitor/output/no-string", + &out_visitor_data, test_visitor_out_no_string, + true); output_visitor_test_add("/string-visitor/output/enum", - &out_visitor_data, test_visitor_out_enum); + &out_visitor_data, test_visitor_out_enum, false); + output_visitor_test_add("/string-visitor/output/enum", + &out_visitor_data, test_visitor_out_enum, true); output_visitor_test_add("/string-visitor/output/enum-errors", - &out_visitor_data, test_visitor_out_enum_errors); + &out_visitor_data, test_visitor_out_enum_errors, + false); + output_visitor_test_add("/string-visitor/output/enum-errors", + &out_visitor_data, test_visitor_out_enum_errors, + true); output_visitor_test_add("/string-visitor/output/intList", - &out_visitor_data, test_visitor_out_intList); + &out_visitor_data, test_visitor_out_intList, false); + output_visitor_test_add("/string-visitor/output/intList", + &out_visitor_data, test_visitor_out_intList, true); g_test_run();