Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a48950cf39 | ||
|
a1c144d276 | ||
|
c700dd0ffc | ||
|
f4055dd206 | ||
|
7925752085 | ||
|
56715e285b | ||
|
1369de9828 | ||
|
87dd0b268a | ||
|
d9094d4c0b | ||
|
f182f276c2 | ||
|
f602c82d48 | ||
|
e976b467c3 | ||
|
7452f5d212 | ||
|
0d3567f1ae | ||
|
f3a92caa76 | ||
|
2e55f16ca5 | ||
|
05010a02f4 | ||
|
3fa1863e91 |
31
.gitignore
vendored
31
.gitignore
vendored
@ -1,4 +1,27 @@
|
|||||||
/.build*.log
|
qemu-kvm-0.13.0-b81fe95.tar.gz
|
||||||
/x86_64/
|
qemu-kvm-0.13.0-25fdf4a.tar.gz
|
||||||
/*.src.rpm
|
/qemu-kvm-0.13.0-rc1.tar.gz
|
||||||
/qemu-*.tar.xz
|
/qemu-kvm-0.13.0.tar.gz
|
||||||
|
/qemu-kvm-0.14.0-3593e6b.tar.gz
|
||||||
|
/qemu-kvm-0.14.0-7aa8c46.tar.gz
|
||||||
|
/qemu-kvm-0.15.0-525e3df.tar.gz
|
||||||
|
/qemu-kvm-0.15.0-fda1906.tar.gz
|
||||||
|
/qemu-kvm-0.15.0-59fadcc.tar.gz
|
||||||
|
/qemu-kvm-0.15.0-0af4922.tar.gz
|
||||||
|
/qemu-kvm-0.15.0.tar.gz
|
||||||
|
/qemu-kvm-0.15.1.tar.gz
|
||||||
|
/qemu-kvm-1.1.0.tar.gz
|
||||||
|
/qemu-kvm-1.1.1.tar.gz
|
||||||
|
/qemu-1.2-0.1.20120806git3e430569.fc18.src.rpm
|
||||||
|
/qemu-kvm-1.2-3e430569.tar.gz
|
||||||
|
/qemu-kvm-1.2.0-rc1.tar.gz
|
||||||
|
/qemu-kvm-1.2.0.tar.gz
|
||||||
|
/qemu-1.3.0.tar.bz2
|
||||||
|
/qemu-1.4.0.tar.bz2
|
||||||
|
/qemu-1.4.1.tar.bz2
|
||||||
|
/qemu-1.5.0.tar.bz2
|
||||||
|
/qemu-1.5.1.tar.bz2
|
||||||
|
/qemu-1.5.2.tar.bz2
|
||||||
|
/qemu-1.6.0.tar.bz2
|
||||||
|
/qemu-1.6.1.tar.bz2
|
||||||
|
/qemu-1.6.2.tar.bz2
|
||||||
|
211
0001-Fix-migration-from-qemu-kvm.patch
Normal file
211
0001-Fix-migration-from-qemu-kvm.patch
Normal file
@ -0,0 +1,211 @@
|
|||||||
|
From: Cole Robinson <crobinso@redhat.com>
|
||||||
|
Date: Fri, 16 Aug 2013 12:14:51 -0400
|
||||||
|
Subject: [PATCH] Fix migration from qemu-kvm
|
||||||
|
|
||||||
|
Details are in the code comments for each change. Just lumped this together
|
||||||
|
to ease patch maintenance.
|
||||||
|
|
||||||
|
Everything except the video memory bits can likely be dropped by Fedora 21
|
||||||
|
time frame. Need to figure out if there's anything to upstream for the
|
||||||
|
video memory bits.
|
||||||
|
---
|
||||||
|
hw/acpi/piix4.c | 8 ++++++-
|
||||||
|
hw/display/qxl.c | 9 ++++----
|
||||||
|
hw/i386/pc_piix.c | 61 +++++++++++++++++++++++++++++++++++++++++++++----
|
||||||
|
hw/timer/i8254_common.c | 7 +++++-
|
||||||
|
4 files changed, 74 insertions(+), 11 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c
|
||||||
|
index 3aaf18c..6fbe57c 100644
|
||||||
|
--- a/hw/acpi/piix4.c
|
||||||
|
+++ b/hw/acpi/piix4.c
|
||||||
|
@@ -289,7 +289,13 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id)
|
||||||
|
static const VMStateDescription vmstate_acpi = {
|
||||||
|
.name = "piix4_pm",
|
||||||
|
.version_id = 3,
|
||||||
|
- .minimum_version_id = 3,
|
||||||
|
+ /*
|
||||||
|
+ * qemu-kvm 1.2 uses qemu.git version 3 format, but advertised as 2.
|
||||||
|
+ * This allows incoming migration from qemu-kvm, but breaks incoming
|
||||||
|
+ * migration from qemu < 1.3.
|
||||||
|
+ */
|
||||||
|
+ //minimum_version_id = 3,
|
||||||
|
+ .minimum_version_id = 2,
|
||||||
|
.minimum_version_id_old = 1,
|
||||||
|
.load_state_old = acpi_load_old,
|
||||||
|
.post_load = vmstate_acpi_post_load,
|
||||||
|
diff --git a/hw/display/qxl.c b/hw/display/qxl.c
|
||||||
|
index c537057..7ef3eff 100644
|
||||||
|
--- a/hw/display/qxl.c
|
||||||
|
+++ b/hw/display/qxl.c
|
||||||
|
@@ -307,16 +307,14 @@ static inline uint32_t msb_mask(uint32_t val)
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static ram_addr_t qxl_rom_size(void)
|
||||||
|
+static void check_qxl_rom_size(PCIQXLDevice *d)
|
||||||
|
{
|
||||||
|
uint32_t required_rom_size = sizeof(QXLRom) + sizeof(QXLModes) +
|
||||||
|
sizeof(qxl_modes);
|
||||||
|
- uint32_t rom_size = 8192; /* two pages */
|
||||||
|
|
||||||
|
required_rom_size = MAX(required_rom_size, TARGET_PAGE_SIZE);
|
||||||
|
required_rom_size = msb_mask(required_rom_size * 2 - 1);
|
||||||
|
- assert(required_rom_size <= rom_size);
|
||||||
|
- return rom_size;
|
||||||
|
+ assert(required_rom_size <= d->rom_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_qxl_rom(PCIQXLDevice *d)
|
||||||
|
@@ -1981,7 +1979,7 @@ static int qxl_init_common(PCIQXLDevice *qxl)
|
||||||
|
pci_set_byte(&config[PCI_REVISION_ID], pci_device_rev);
|
||||||
|
pci_set_byte(&config[PCI_INTERRUPT_PIN], 1);
|
||||||
|
|
||||||
|
- qxl->rom_size = qxl_rom_size();
|
||||||
|
+ check_qxl_rom_size(qxl);
|
||||||
|
memory_region_init_ram(&qxl->rom_bar, OBJECT(qxl), "qxl.vrom",
|
||||||
|
qxl->rom_size);
|
||||||
|
vmstate_register_ram(&qxl->rom_bar, &qxl->pci.qdev);
|
||||||
|
@@ -2309,6 +2307,7 @@ static Property qxl_properties[] = {
|
||||||
|
DEFINE_PROP_UINT32("vram64_size_mb", PCIQXLDevice, vram_size_mb, -1),
|
||||||
|
DEFINE_PROP_UINT32("vgamem_mb", PCIQXLDevice, vgamem_size_mb, 16),
|
||||||
|
DEFINE_PROP_INT32("surfaces", PCIQXLDevice, ssd.num_surfaces, 1024),
|
||||||
|
+ DEFINE_PROP_UINT32("rom_size", PCIQXLDevice, rom_size, 8192),
|
||||||
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
|
};
|
||||||
|
|
||||||
|
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
|
||||||
|
index 3df2ff9..28216ee 100644
|
||||||
|
--- a/hw/i386/pc_piix.c
|
||||||
|
+++ b/hw/i386/pc_piix.c
|
||||||
|
@@ -377,6 +377,24 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
|
||||||
|
DEFAULT_MACHINE_OPTIONS,
|
||||||
|
};
|
||||||
|
|
||||||
|
+/*
|
||||||
|
+ * Commit 038c1879a00153b14bce113315b693e8c2944fa9 changed the qxl rom
|
||||||
|
+ * size to 8192, which fixes incoming migration from qemu 1.0. However
|
||||||
|
+ * from qemu 1.2 and 1.3 had rom size 16384, so incoming migration
|
||||||
|
+ * from those versions is now broken.
|
||||||
|
+ *
|
||||||
|
+ * Add a rom_size compat property. 1.2 and 1.3 get 16384, everything
|
||||||
|
+ * else is 8192.
|
||||||
|
+ *
|
||||||
|
+ * This isn't actually fool proof, since rom_size can be dependent on
|
||||||
|
+ * the version of spice qemu is built against:
|
||||||
|
+ *
|
||||||
|
+ * https://lists.gnu.org/archive/html/qemu-devel/2013-02/msg03154.html
|
||||||
|
+ *
|
||||||
|
+ * However these sizes match what native Fedora packages get, so it's
|
||||||
|
+ * good enough for now.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
#define PC_COMPAT_1_3 \
|
||||||
|
PC_COMPAT_1_4, \
|
||||||
|
{\
|
||||||
|
@@ -395,8 +413,17 @@ static QEMUMachine pc_i440fx_machine_v1_4 = {
|
||||||
|
.driver = "e1000",\
|
||||||
|
.property = "autonegotiation",\
|
||||||
|
.value = "off",\
|
||||||
|
+ },{ \
|
||||||
|
+ .driver = "qxl", \
|
||||||
|
+ .property = "rom_size", \
|
||||||
|
+ .value = stringify(16384), \
|
||||||
|
+ },{\
|
||||||
|
+ .driver = "qxl-vga", \
|
||||||
|
+ .property = "rom_size", \
|
||||||
|
+ .value = stringify(16384), \
|
||||||
|
}
|
||||||
|
|
||||||
|
+
|
||||||
|
static QEMUMachine pc_machine_v1_3 = {
|
||||||
|
.name = "pc-1.3",
|
||||||
|
.desc = "Standard PC",
|
||||||
|
@@ -409,6 +436,19 @@ static QEMUMachine pc_machine_v1_3 = {
|
||||||
|
DEFAULT_MACHINE_OPTIONS,
|
||||||
|
};
|
||||||
|
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
+ * https://lists.gnu.org/archive/html/qemu-devel/2013-01/msg02540.html
|
||||||
|
+ *
|
||||||
|
+ * qemu-kvm defaulted to vgamem=16MB since at least 0.15, while qemu used
|
||||||
|
+ * 8MB. For qemu 1.2, the default was changed to 16MB for all devices
|
||||||
|
+ * except cirrus.
|
||||||
|
+ *
|
||||||
|
+ * Make sure cirrus uses 16MB for <= pc-1.2 (the qemu-kvm merge),
|
||||||
|
+ * and 16MB always for all others. This will break incoming qemu
|
||||||
|
+ * migration for qemu < 1.3.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
#define PC_COMPAT_1_2 \
|
||||||
|
PC_COMPAT_1_3,\
|
||||||
|
{\
|
||||||
|
@@ -432,6 +472,10 @@ static QEMUMachine pc_machine_v1_3 = {
|
||||||
|
.property = "revision",\
|
||||||
|
.value = stringify(3),\
|
||||||
|
},{\
|
||||||
|
+ .driver = "cirrus-vga",\
|
||||||
|
+ .property = "vgamem_mb",\
|
||||||
|
+ .value = stringify(16),\
|
||||||
|
+ },{\
|
||||||
|
.driver = "VGA",\
|
||||||
|
.property = "mmio",\
|
||||||
|
.value = "off",\
|
||||||
|
@@ -462,25 +506,34 @@ static QEMUMachine pc_machine_v1_2 = {
|
||||||
|
},{\
|
||||||
|
.driver = "VGA",\
|
||||||
|
.property = "vgamem_mb",\
|
||||||
|
- .value = stringify(8),\
|
||||||
|
+ .value = stringify(16),\
|
||||||
|
},{\
|
||||||
|
.driver = "vmware-svga",\
|
||||||
|
.property = "vgamem_mb",\
|
||||||
|
- .value = stringify(8),\
|
||||||
|
+ .value = stringify(16),\
|
||||||
|
},{\
|
||||||
|
.driver = "qxl-vga",\
|
||||||
|
.property = "vgamem_mb",\
|
||||||
|
- .value = stringify(8),\
|
||||||
|
+ .value = stringify(16),\
|
||||||
|
},{\
|
||||||
|
.driver = "qxl",\
|
||||||
|
.property = "vgamem_mb",\
|
||||||
|
- .value = stringify(8),\
|
||||||
|
+ .value = stringify(16),\
|
||||||
|
},{\
|
||||||
|
.driver = "virtio-blk-pci",\
|
||||||
|
.property = "config-wce",\
|
||||||
|
.value = "off",\
|
||||||
|
+ },{ \
|
||||||
|
+ .driver = "qxl", \
|
||||||
|
+ .property = "rom_size", \
|
||||||
|
+ .value = stringify(8192), \
|
||||||
|
+ },{\
|
||||||
|
+ .driver = "qxl-vga", \
|
||||||
|
+ .property = "rom_size", \
|
||||||
|
+ .value = stringify(8192), \
|
||||||
|
}
|
||||||
|
|
||||||
|
+
|
||||||
|
static QEMUMachine pc_machine_v1_1 = {
|
||||||
|
.name = "pc-1.1",
|
||||||
|
.desc = "Standard PC",
|
||||||
|
diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c
|
||||||
|
index 4e5bf0b..cbc00a0 100644
|
||||||
|
--- a/hw/timer/i8254_common.c
|
||||||
|
+++ b/hw/timer/i8254_common.c
|
||||||
|
@@ -267,7 +267,12 @@ static const VMStateDescription vmstate_pit_common = {
|
||||||
|
.pre_save = pit_dispatch_pre_save,
|
||||||
|
.post_load = pit_dispatch_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
- VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
|
||||||
|
+ /* qemu-kvm version_id=2 had 'flags' here which is equivalent
|
||||||
|
+ * This fixes incoming migration from qemu-kvm 1.0, but breaks
|
||||||
|
+ * incoming migration from qemu < 1.1
|
||||||
|
+ */
|
||||||
|
+ //VMSTATE_UINT32_V(channels[0].irq_disabled, PITCommonState, 3),
|
||||||
|
+ VMSTATE_UINT32(channels[0].irq_disabled, PITCommonState),
|
||||||
|
VMSTATE_STRUCT_ARRAY(channels, PITCommonState, 3, 2,
|
||||||
|
vmstate_pit_channel, PITChannelState),
|
||||||
|
VMSTATE_INT64(channels[0].next_transition_time,
|
35
0002-isapc-disable-kvmvapic.patch
Normal file
35
0002-isapc-disable-kvmvapic.patch
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Tue, 13 Aug 2013 00:02:18 +0200
|
||||||
|
Subject: [PATCH] isapc: disable kvmvapic
|
||||||
|
|
||||||
|
vapic requires the VAPIC ROM to be mapped into RAM. This is not
|
||||||
|
possible without PAM hardware. This fixes a segmentation fault
|
||||||
|
running with -M isapc.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
|
||||||
|
(crobinso: s/kvmvapic/vapic/g)
|
||||||
|
|
||||||
|
Signed-off-by: Cole Robinson <crobinso@redhat.com>
|
||||||
|
---
|
||||||
|
hw/i386/pc_piix.c | 6 +++++-
|
||||||
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
|
||||||
|
index 28216ee..2f2cb4d 100644
|
||||||
|
--- a/hw/i386/pc_piix.c
|
||||||
|
+++ b/hw/i386/pc_piix.c
|
||||||
|
@@ -795,7 +795,11 @@ static QEMUMachine isapc_machine = {
|
||||||
|
.init = pc_init_isa,
|
||||||
|
.max_cpus = 1,
|
||||||
|
.compat_props = (GlobalProperty[]) {
|
||||||
|
- { /* end of list */ }
|
||||||
|
+ {
|
||||||
|
+ .driver = "apic-common",
|
||||||
|
+ .property = "vapic",
|
||||||
|
+ .value = "off",
|
||||||
|
+ },
|
||||||
|
},
|
||||||
|
DEFAULT_MACHINE_OPTIONS,
|
||||||
|
};
|
71
0003-pci-do-not-export-pci_bus_reset.patch
Normal file
71
0003-pci-do-not-export-pci_bus_reset.patch
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Thu, 2 May 2013 11:38:37 +0200
|
||||||
|
Subject: [PATCH] pci: do not export pci_bus_reset
|
||||||
|
|
||||||
|
qbus_reset_all can be used instead. There is no semantic change
|
||||||
|
because pcibus_reset returns 1 and takes care of the device
|
||||||
|
tree traversal.
|
||||||
|
|
||||||
|
This will be necessary once the traversal is done always in
|
||||||
|
qbus_reset_all *before* invoking pcibus_reset itself.
|
||||||
|
|
||||||
|
Tested-by: Claudio Bley <cbley@av-test.de>
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/pci/pci.c | 8 ++------
|
||||||
|
hw/pci/pci_bridge.c | 2 +-
|
||||||
|
include/hw/pci/pci.h | 1 -
|
||||||
|
3 files changed, 3 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
|
||||||
|
index dc5b788..3a0c4a7 100644
|
||||||
|
--- a/hw/pci/pci.c
|
||||||
|
+++ b/hw/pci/pci.c
|
||||||
|
@@ -212,8 +212,9 @@ void pci_device_reset(PCIDevice *dev)
|
||||||
|
* Trigger pci bus reset under a given bus.
|
||||||
|
* To be called on RST# assert.
|
||||||
|
*/
|
||||||
|
-void pci_bus_reset(PCIBus *bus)
|
||||||
|
+static int pcibus_reset(BusState *qbus)
|
||||||
|
{
|
||||||
|
+ PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < bus->nirq; i++) {
|
||||||
|
@@ -224,11 +225,6 @@ void pci_bus_reset(PCIBus *bus)
|
||||||
|
pci_device_reset(bus->devices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static int pcibus_reset(BusState *qbus)
|
||||||
|
-{
|
||||||
|
- pci_bus_reset(DO_UPCAST(PCIBus, qbus, qbus));
|
||||||
|
|
||||||
|
/* topology traverse is done by pci_bus_reset().
|
||||||
|
Tell qbus/qdev walker not to traverse the tree */
|
||||||
|
diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c
|
||||||
|
index a90671d..5d0e5ff 100644
|
||||||
|
--- a/hw/pci/pci_bridge.c
|
||||||
|
+++ b/hw/pci/pci_bridge.c
|
||||||
|
@@ -268,7 +268,7 @@ void pci_bridge_write_config(PCIDevice *d,
|
||||||
|
newctl = pci_get_word(d->config + PCI_BRIDGE_CONTROL);
|
||||||
|
if (~oldctl & newctl & PCI_BRIDGE_CTL_BUS_RESET) {
|
||||||
|
/* Trigger hot reset on 0->1 transition. */
|
||||||
|
- pci_bus_reset(&s->sec_bus);
|
||||||
|
+ qbus_reset_all(&s->sec_bus.qbus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
|
||||||
|
index ccec2ba..32f1419 100644
|
||||||
|
--- a/include/hw/pci/pci.h
|
||||||
|
+++ b/include/hw/pci/pci.h
|
||||||
|
@@ -376,7 +376,6 @@ void pci_bus_fire_intx_routing_notifier(PCIBus *bus);
|
||||||
|
void pci_device_set_intx_routing_notifier(PCIDevice *dev,
|
||||||
|
PCIINTxRoutingNotifier notifier);
|
||||||
|
void pci_device_reset(PCIDevice *dev);
|
||||||
|
-void pci_bus_reset(PCIBus *bus);
|
||||||
|
|
||||||
|
PCIDevice *pci_nic_init(NICInfo *nd, PCIBus *rootbus,
|
||||||
|
const char *default_model,
|
140
0004-qdev-allow-both-pre-and-post-order-vists-in-qdev-wal.patch
Normal file
140
0004-qdev-allow-both-pre-and-post-order-vists-in-qdev-wal.patch
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Thu, 2 May 2013 11:38:38 +0200
|
||||||
|
Subject: [PATCH] qdev: allow both pre- and post-order vists in qdev walking
|
||||||
|
functions
|
||||||
|
|
||||||
|
Resetting should be done in post-order, not pre-order. However,
|
||||||
|
qdev_walk_children and qbus_walk_children do not allow this. Fix
|
||||||
|
it by adding two extra arguments to the functions.
|
||||||
|
|
||||||
|
Tested-by: Claudio Bley <cbley@av-test.de>
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/core/qdev.c | 45 +++++++++++++++++++++++++++++++++------------
|
||||||
|
include/hw/qdev-core.h | 13 +++++++++----
|
||||||
|
2 files changed, 42 insertions(+), 16 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
|
||||||
|
index 9190a7e..842804f 100644
|
||||||
|
--- a/hw/core/qdev.c
|
||||||
|
+++ b/hw/core/qdev.c
|
||||||
|
@@ -240,12 +240,12 @@ static int qbus_reset_one(BusState *bus, void *opaque)
|
||||||
|
|
||||||
|
void qdev_reset_all(DeviceState *dev)
|
||||||
|
{
|
||||||
|
- qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL);
|
||||||
|
+ qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qbus_reset_all(BusState *bus)
|
||||||
|
{
|
||||||
|
- qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL);
|
||||||
|
+ qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qbus_reset_all_fn(void *opaque)
|
||||||
|
@@ -343,49 +343,70 @@ BusState *qdev_get_child_bus(DeviceState *dev, const char *name)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
|
||||||
|
- qbus_walkerfn *busfn, void *opaque)
|
||||||
|
+int qbus_walk_children(BusState *bus,
|
||||||
|
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
|
||||||
|
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
|
||||||
|
+ void *opaque)
|
||||||
|
{
|
||||||
|
BusChild *kid;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
- if (busfn) {
|
||||||
|
- err = busfn(bus, opaque);
|
||||||
|
+ if (pre_busfn) {
|
||||||
|
+ err = pre_busfn(bus, opaque);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||||
|
- err = qdev_walk_children(kid->child, devfn, busfn, opaque);
|
||||||
|
+ err = qdev_walk_children(kid->child,
|
||||||
|
+ pre_devfn, pre_busfn,
|
||||||
|
+ post_devfn, post_busfn, opaque);
|
||||||
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (post_busfn) {
|
||||||
|
+ err = post_busfn(bus, opaque);
|
||||||
|
+ if (err) {
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
|
||||||
|
- qbus_walkerfn *busfn, void *opaque)
|
||||||
|
+int qdev_walk_children(DeviceState *dev,
|
||||||
|
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
|
||||||
|
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
|
||||||
|
+ void *opaque)
|
||||||
|
{
|
||||||
|
BusState *bus;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
- if (devfn) {
|
||||||
|
- err = devfn(dev, opaque);
|
||||||
|
+ if (pre_devfn) {
|
||||||
|
+ err = pre_devfn(dev, opaque);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||||
|
- err = qbus_walk_children(bus, devfn, busfn, opaque);
|
||||||
|
+ err = qbus_walk_children(bus, pre_devfn, pre_busfn,
|
||||||
|
+ post_devfn, post_busfn, opaque);
|
||||||
|
if (err < 0) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (post_devfn) {
|
||||||
|
+ err = post_devfn(dev, opaque);
|
||||||
|
+ if (err) {
|
||||||
|
+ return err;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
|
||||||
|
index 46972f4..c6c9b14 100644
|
||||||
|
--- a/include/hw/qdev-core.h
|
||||||
|
+++ b/include/hw/qdev-core.h
|
||||||
|
@@ -270,10 +270,15 @@ BusState *qbus_create(const char *typename, DeviceState *parent, const char *nam
|
||||||
|
/* Returns > 0 if either devfn or busfn skip walk somewhere in cursion,
|
||||||
|
* < 0 if either devfn or busfn terminate walk somewhere in cursion,
|
||||||
|
* 0 otherwise. */
|
||||||
|
-int qbus_walk_children(BusState *bus, qdev_walkerfn *devfn,
|
||||||
|
- qbus_walkerfn *busfn, void *opaque);
|
||||||
|
-int qdev_walk_children(DeviceState *dev, qdev_walkerfn *devfn,
|
||||||
|
- qbus_walkerfn *busfn, void *opaque);
|
||||||
|
+int qbus_walk_children(BusState *bus,
|
||||||
|
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
|
||||||
|
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
|
||||||
|
+ void *opaque);
|
||||||
|
+int qdev_walk_children(DeviceState *dev,
|
||||||
|
+ qdev_walkerfn *pre_devfn, qbus_walkerfn *pre_busfn,
|
||||||
|
+ qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
|
||||||
|
+ void *opaque);
|
||||||
|
+
|
||||||
|
void qdev_reset_all(DeviceState *dev);
|
||||||
|
|
||||||
|
/**
|
142
0005-qdev-switch-reset-to-post-order.patch
Normal file
142
0005-qdev-switch-reset-to-post-order.patch
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Thu, 2 May 2013 11:38:39 +0200
|
||||||
|
Subject: [PATCH] qdev: switch reset to post-order
|
||||||
|
|
||||||
|
Post-order is the only sensible direction for the reset signals.
|
||||||
|
For example, suppose pre-order is used and the parent has some data
|
||||||
|
structures that cache children state (for example a list of active
|
||||||
|
requests). When the reset method is invoked on the parent, these caches
|
||||||
|
could be in any state.
|
||||||
|
|
||||||
|
If post-order is used, on the other hand, these will be in a known state
|
||||||
|
when the reset method is invoked on the parent.
|
||||||
|
|
||||||
|
This change means that it is no longer possible to block the visit of
|
||||||
|
the devices, so the callback is changed to return void. This is not
|
||||||
|
a problem, because PCI was returning 1 exactly in order to achieve the
|
||||||
|
same ordering that this patch implements.
|
||||||
|
|
||||||
|
PCI can then rely on the qdev core having sent a "reset signal"
|
||||||
|
(whatever that means) to the device, and only do the PCI-specific
|
||||||
|
initialization with the new function pci_do_device_reset, extracted
|
||||||
|
from pci_device_reset. There is no change in the operation of FLR,
|
||||||
|
which used and still uses pci_device_reset.
|
||||||
|
|
||||||
|
Tested-by: Claudio Bley <cbley@av-test.de>
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/core/qdev.c | 6 +++---
|
||||||
|
hw/pci/pci.c | 31 ++++++++++++++++---------------
|
||||||
|
include/hw/qdev-core.h | 2 +-
|
||||||
|
3 files changed, 20 insertions(+), 19 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
|
||||||
|
index 842804f..87d7e1e 100644
|
||||||
|
--- a/hw/core/qdev.c
|
||||||
|
+++ b/hw/core/qdev.c
|
||||||
|
@@ -233,19 +233,19 @@ static int qbus_reset_one(BusState *bus, void *opaque)
|
||||||
|
{
|
||||||
|
BusClass *bc = BUS_GET_CLASS(bus);
|
||||||
|
if (bc->reset) {
|
||||||
|
- return bc->reset(bus);
|
||||||
|
+ bc->reset(bus);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qdev_reset_all(DeviceState *dev)
|
||||||
|
{
|
||||||
|
- qdev_walk_children(dev, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
|
||||||
|
+ qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qbus_reset_all(BusState *bus)
|
||||||
|
{
|
||||||
|
- qbus_walk_children(bus, qdev_reset_one, qbus_reset_one, NULL, NULL, NULL);
|
||||||
|
+ qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qbus_reset_all_fn(void *opaque)
|
||||||
|
diff --git a/hw/pci/pci.c b/hw/pci/pci.c
|
||||||
|
index 3a0c4a7..129cdb7 100644
|
||||||
|
--- a/hw/pci/pci.c
|
||||||
|
+++ b/hw/pci/pci.c
|
||||||
|
@@ -46,7 +46,7 @@
|
||||||
|
static void pcibus_dev_print(Monitor *mon, DeviceState *dev, int indent);
|
||||||
|
static char *pcibus_get_dev_path(DeviceState *dev);
|
||||||
|
static char *pcibus_get_fw_dev_path(DeviceState *dev);
|
||||||
|
-static int pcibus_reset(BusState *qbus);
|
||||||
|
+static void pcibus_reset(BusState *qbus);
|
||||||
|
static void pci_bus_finalize(Object *obj);
|
||||||
|
|
||||||
|
static Property pci_props[] = {
|
||||||
|
@@ -167,16 +167,10 @@ void pci_device_deassert_intx(PCIDevice *dev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-/*
|
||||||
|
- * This function is called on #RST and FLR.
|
||||||
|
- * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
|
||||||
|
- */
|
||||||
|
-void pci_device_reset(PCIDevice *dev)
|
||||||
|
+static void pci_do_device_reset(PCIDevice *dev)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
- qdev_reset_all(&dev->qdev);
|
||||||
|
-
|
||||||
|
dev->irq_state = 0;
|
||||||
|
pci_update_irq_status(dev);
|
||||||
|
pci_device_deassert_intx(dev);
|
||||||
|
@@ -209,10 +203,21 @@ void pci_device_reset(PCIDevice *dev)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
+ * This function is called on #RST and FLR.
|
||||||
|
+ * FLR if PCI_EXP_DEVCTL_BCR_FLR is set
|
||||||
|
+ */
|
||||||
|
+void pci_device_reset(PCIDevice *dev)
|
||||||
|
+{
|
||||||
|
+ qdev_reset_all(&dev->qdev);
|
||||||
|
+ pci_do_device_reset(dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*
|
||||||
|
* Trigger pci bus reset under a given bus.
|
||||||
|
- * To be called on RST# assert.
|
||||||
|
+ * Called via qbus_reset_all on RST# assert, after the devices
|
||||||
|
+ * have been reset qdev_reset_all-ed already.
|
||||||
|
*/
|
||||||
|
-static int pcibus_reset(BusState *qbus)
|
||||||
|
+static void pcibus_reset(BusState *qbus)
|
||||||
|
{
|
||||||
|
PCIBus *bus = DO_UPCAST(PCIBus, qbus, qbus);
|
||||||
|
int i;
|
||||||
|
@@ -222,13 +227,9 @@ static int pcibus_reset(BusState *qbus)
|
||||||
|
}
|
||||||
|
for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) {
|
||||||
|
if (bus->devices[i]) {
|
||||||
|
- pci_device_reset(bus->devices[i]);
|
||||||
|
+ pci_do_device_reset(bus->devices[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
-
|
||||||
|
- /* topology traverse is done by pci_bus_reset().
|
||||||
|
- Tell qbus/qdev walker not to traverse the tree */
|
||||||
|
- return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pci_host_bus_register(PCIBus *bus, DeviceState *parent)
|
||||||
|
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
|
||||||
|
index c6c9b14..89dcbad 100644
|
||||||
|
--- a/include/hw/qdev-core.h
|
||||||
|
+++ b/include/hw/qdev-core.h
|
||||||
|
@@ -174,7 +174,7 @@ struct BusClass {
|
||||||
|
* bindings can be found at http://playground.sun.com/1275/bindings/.
|
||||||
|
*/
|
||||||
|
char *(*get_fw_dev_path)(DeviceState *dev);
|
||||||
|
- int (*reset)(BusState *bus);
|
||||||
|
+ void (*reset)(BusState *bus);
|
||||||
|
/* maximum devices allowed on the bus, 0: no limit. */
|
||||||
|
int max_dev;
|
||||||
|
};
|
250
0006-virtio-bus-remove-vdev-field.patch
Normal file
250
0006-virtio-bus-remove-vdev-field.patch
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:50 +0200
|
||||||
|
Subject: [PATCH] virtio-bus: remove vdev field
|
||||||
|
|
||||||
|
The vdev field is complicated to synchronize. Just access the
|
||||||
|
BusState's list of children.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/virtio/virtio-bus.c | 67 ++++++++++++++++++++++++------------------
|
||||||
|
hw/virtio/virtio-mmio.c | 9 +++---
|
||||||
|
hw/virtio/virtio-pci.c | 2 +-
|
||||||
|
include/hw/virtio/virtio-bus.h | 16 +++++++---
|
||||||
|
4 files changed, 57 insertions(+), 37 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
|
||||||
|
index 6849a01..669ce38 100644
|
||||||
|
--- a/hw/virtio/virtio-bus.c
|
||||||
|
+++ b/hw/virtio/virtio-bus.c
|
||||||
|
@@ -46,8 +46,6 @@ int virtio_bus_plug_device(VirtIODevice *vdev)
|
||||||
|
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
|
||||||
|
DPRINTF("%s: plug device.\n", qbus->name);
|
||||||
|
|
||||||
|
- bus->vdev = vdev;
|
||||||
|
-
|
||||||
|
if (klass->device_plugged != NULL) {
|
||||||
|
klass->device_plugged(qbus->parent);
|
||||||
|
}
|
||||||
|
@@ -58,75 +56,84 @@ int virtio_bus_plug_device(VirtIODevice *vdev)
|
||||||
|
/* Reset the virtio_bus */
|
||||||
|
void virtio_bus_reset(VirtioBusState *bus)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
+
|
||||||
|
DPRINTF("%s: reset device.\n", qbus->name);
|
||||||
|
- if (bus->vdev != NULL) {
|
||||||
|
- virtio_reset(bus->vdev);
|
||||||
|
+ if (vdev != NULL) {
|
||||||
|
+ virtio_reset(vdev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Destroy the VirtIODevice */
|
||||||
|
void virtio_bus_destroy_device(VirtioBusState *bus)
|
||||||
|
{
|
||||||
|
- DeviceState *qdev;
|
||||||
|
BusState *qbus = BUS(bus);
|
||||||
|
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
+
|
||||||
|
DPRINTF("%s: remove device.\n", qbus->name);
|
||||||
|
|
||||||
|
- if (bus->vdev != NULL) {
|
||||||
|
+ if (vdev != NULL) {
|
||||||
|
if (klass->device_unplug != NULL) {
|
||||||
|
klass->device_unplug(qbus->parent);
|
||||||
|
}
|
||||||
|
- qdev = DEVICE(bus->vdev);
|
||||||
|
- qdev_free(qdev);
|
||||||
|
- bus->vdev = NULL;
|
||||||
|
+ qdev_free(DEVICE(vdev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the device id of the plugged device. */
|
||||||
|
uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus)
|
||||||
|
{
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- return bus->vdev->device_id;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ return vdev->device_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the config_len field of the plugged device. */
|
||||||
|
size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus)
|
||||||
|
{
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- return bus->vdev->config_len;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ return vdev->config_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the features of the plugged device. */
|
||||||
|
uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus,
|
||||||
|
uint32_t requested_features)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
VirtioDeviceClass *k;
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
|
||||||
|
+
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
assert(k->get_features != NULL);
|
||||||
|
- return k->get_features(bus->vdev, requested_features);
|
||||||
|
+ return k->get_features(vdev, requested_features);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the features of the plugged device. */
|
||||||
|
void virtio_bus_set_vdev_features(VirtioBusState *bus,
|
||||||
|
uint32_t requested_features)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
VirtioDeviceClass *k;
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
|
||||||
|
+
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
if (k->set_features != NULL) {
|
||||||
|
- k->set_features(bus->vdev, requested_features);
|
||||||
|
+ k->set_features(vdev, requested_features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get bad features of the plugged device. */
|
||||||
|
uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
VirtioDeviceClass *k;
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
|
||||||
|
+
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
if (k->bad_features != NULL) {
|
||||||
|
- return k->bad_features(bus->vdev);
|
||||||
|
+ return k->bad_features(vdev);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -135,22 +142,26 @@ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus)
|
||||||
|
/* Get config of the plugged device. */
|
||||||
|
void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
VirtioDeviceClass *k;
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
|
||||||
|
+
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
if (k->get_config != NULL) {
|
||||||
|
- k->get_config(bus->vdev, config);
|
||||||
|
+ k->get_config(vdev, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set config of the plugged device. */
|
||||||
|
void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
VirtioDeviceClass *k;
|
||||||
|
- assert(bus->vdev != NULL);
|
||||||
|
- k = VIRTIO_DEVICE_GET_CLASS(bus->vdev);
|
||||||
|
+
|
||||||
|
+ assert(vdev != NULL);
|
||||||
|
+ k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
if (k->set_config != NULL) {
|
||||||
|
- k->set_config(bus->vdev, config);
|
||||||
|
+ k->set_config(vdev, config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c
|
||||||
|
index 4bd2953..8f7b764 100644
|
||||||
|
--- a/hw/virtio/virtio-mmio.c
|
||||||
|
+++ b/hw/virtio/virtio-mmio.c
|
||||||
|
@@ -94,7 +94,7 @@ static void virtio_mmio_bus_new(VirtioBusState *bus, VirtIOMMIOProxy *dev);
|
||||||
|
static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
|
||||||
|
{
|
||||||
|
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
|
||||||
|
- VirtIODevice *vdev = proxy->bus.vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
|
||||||
|
DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset);
|
||||||
|
|
||||||
|
@@ -184,7 +184,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
|
||||||
|
- VirtIODevice *vdev = proxy->bus.vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
|
||||||
|
DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n",
|
||||||
|
(int)offset, value);
|
||||||
|
@@ -297,12 +297,13 @@ static const MemoryRegionOps virtio_mem_ops = {
|
||||||
|
static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
|
||||||
|
{
|
||||||
|
VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
int level;
|
||||||
|
|
||||||
|
- if (!proxy->bus.vdev) {
|
||||||
|
+ if (!vdev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
- level = (proxy->bus.vdev->isr != 0);
|
||||||
|
+ level = (vdev->isr != 0);
|
||||||
|
DPRINTF("virtio_mmio setting IRQ %d\n", level);
|
||||||
|
qemu_set_irq(proxy->irq, level);
|
||||||
|
}
|
||||||
|
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
|
||||||
|
index 41b96ce..55617a6 100644
|
||||||
|
--- a/hw/virtio/virtio-pci.c
|
||||||
|
+++ b/hw/virtio/virtio-pci.c
|
||||||
|
@@ -942,7 +942,7 @@ static void virtio_pci_device_plugged(DeviceState *d)
|
||||||
|
uint8_t *config;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
- proxy->vdev = bus->vdev;
|
||||||
|
+ proxy->vdev = virtio_bus_get_device(bus);
|
||||||
|
|
||||||
|
config = proxy->pci_dev.config;
|
||||||
|
if (proxy->class_code) {
|
||||||
|
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
|
||||||
|
index 9217f85..ba0f86a 100644
|
||||||
|
--- a/include/hw/virtio/virtio-bus.h
|
||||||
|
+++ b/include/hw/virtio/virtio-bus.h
|
||||||
|
@@ -72,10 +72,6 @@ typedef struct VirtioBusClass {
|
||||||
|
|
||||||
|
struct VirtioBusState {
|
||||||
|
BusState parent_obj;
|
||||||
|
- /*
|
||||||
|
- * Only one VirtIODevice can be plugged on the bus.
|
||||||
|
- */
|
||||||
|
- VirtIODevice *vdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
int virtio_bus_plug_device(VirtIODevice *vdev);
|
||||||
|
@@ -98,4 +94,16 @@ void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config);
|
||||||
|
/* Set config of the plugged device. */
|
||||||
|
void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config);
|
||||||
|
|
||||||
|
+static inline VirtIODevice *virtio_bus_get_device(VirtioBusState *bus)
|
||||||
|
+{
|
||||||
|
+ BusState *qbus = &bus->parent_obj;
|
||||||
|
+ BusChild *kid = QTAILQ_FIRST(&qbus->children);
|
||||||
|
+ DeviceState *qdev = kid ? kid->child : NULL;
|
||||||
|
+
|
||||||
|
+ /* This is used on the data path, the cast is guaranteed
|
||||||
|
+ * to succeed by the qdev machinery.
|
||||||
|
+ */
|
||||||
|
+ return (VirtIODevice *)qdev;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#endif /* VIRTIO_BUS_H */
|
446
0007-virtio-pci-remove-vdev-field.patch
Normal file
446
0007-virtio-pci-remove-vdev-field.patch
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:51 +0200
|
||||||
|
Subject: [PATCH] virtio-pci: remove vdev field
|
||||||
|
|
||||||
|
The vdev field is complicated to synchronize. Just access the
|
||||||
|
BusState's list of children.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/s390x/virtio-ccw.h | 1 -
|
||||||
|
hw/virtio/virtio-pci.c | 107 +++++++++++++++++++++++++++++--------------------
|
||||||
|
hw/virtio/virtio-pci.h | 1 -
|
||||||
|
3 files changed, 63 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h
|
||||||
|
index 96d6f5d..00932c7 100644
|
||||||
|
--- a/hw/s390x/virtio-ccw.h
|
||||||
|
+++ b/hw/s390x/virtio-ccw.h
|
||||||
|
@@ -77,7 +77,6 @@ typedef struct VirtIOCCWDeviceClass {
|
||||||
|
struct VirtioCcwDevice {
|
||||||
|
DeviceState parent_obj;
|
||||||
|
SubchDev *sch;
|
||||||
|
- VirtIODevice *vdev;
|
||||||
|
char *bus_id;
|
||||||
|
uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE];
|
||||||
|
VirtioBusState bus;
|
||||||
|
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
|
||||||
|
index 55617a6..6fd6d6d 100644
|
||||||
|
--- a/hw/virtio/virtio-pci.c
|
||||||
|
+++ b/hw/virtio/virtio-pci.c
|
||||||
|
@@ -112,31 +112,39 @@ static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d)
|
||||||
|
static void virtio_pci_notify(DeviceState *d, uint16_t vector)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+
|
||||||
|
if (msix_enabled(&proxy->pci_dev))
|
||||||
|
msix_notify(&proxy->pci_dev, vector);
|
||||||
|
else
|
||||||
|
- qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1);
|
||||||
|
+ qemu_set_irq(proxy->pci_dev.irq[0], vdev->isr & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pci_save_config(DeviceState *d, QEMUFile *f)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+
|
||||||
|
pci_device_save(&proxy->pci_dev, f);
|
||||||
|
msix_save(&proxy->pci_dev, f);
|
||||||
|
if (msix_present(&proxy->pci_dev))
|
||||||
|
- qemu_put_be16(f, proxy->vdev->config_vector);
|
||||||
|
+ qemu_put_be16(f, vdev->config_vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+
|
||||||
|
if (msix_present(&proxy->pci_dev))
|
||||||
|
- qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n));
|
||||||
|
+ qemu_put_be16(f, virtio_queue_vector(vdev, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+
|
||||||
|
int ret;
|
||||||
|
ret = pci_device_load(&proxy->pci_dev, f);
|
||||||
|
if (ret) {
|
||||||
|
@@ -145,12 +153,12 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
|
||||||
|
msix_unuse_all_vectors(&proxy->pci_dev);
|
||||||
|
msix_load(&proxy->pci_dev, f);
|
||||||
|
if (msix_present(&proxy->pci_dev)) {
|
||||||
|
- qemu_get_be16s(f, &proxy->vdev->config_vector);
|
||||||
|
+ qemu_get_be16s(f, &vdev->config_vector);
|
||||||
|
} else {
|
||||||
|
- proxy->vdev->config_vector = VIRTIO_NO_VECTOR;
|
||||||
|
+ vdev->config_vector = VIRTIO_NO_VECTOR;
|
||||||
|
}
|
||||||
|
- if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
|
||||||
|
- return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
|
||||||
|
+ if (vdev->config_vector != VIRTIO_NO_VECTOR) {
|
||||||
|
+ return msix_vector_use(&proxy->pci_dev, vdev->config_vector);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -158,13 +166,15 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f)
|
||||||
|
static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+
|
||||||
|
uint16_t vector;
|
||||||
|
if (msix_present(&proxy->pci_dev)) {
|
||||||
|
qemu_get_be16s(f, &vector);
|
||||||
|
} else {
|
||||||
|
vector = VIRTIO_NO_VECTOR;
|
||||||
|
}
|
||||||
|
- virtio_queue_set_vector(proxy->vdev, n, vector);
|
||||||
|
+ virtio_queue_set_vector(vdev, n, vector);
|
||||||
|
if (vector != VIRTIO_NO_VECTOR) {
|
||||||
|
return msix_vector_use(&proxy->pci_dev, vector);
|
||||||
|
}
|
||||||
|
@@ -174,7 +184,8 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f)
|
||||||
|
static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
|
||||||
|
int n, bool assign, bool set_handler)
|
||||||
|
{
|
||||||
|
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||||
|
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
|
||||||
|
int r = 0;
|
||||||
|
|
||||||
|
@@ -199,6 +210,7 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy,
|
||||||
|
|
||||||
|
static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
int n, r;
|
||||||
|
|
||||||
|
if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) ||
|
||||||
|
@@ -208,7 +220,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
|
||||||
|
- if (!virtio_queue_get_num(proxy->vdev, n)) {
|
||||||
|
+ if (!virtio_queue_get_num(vdev, n)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -222,7 +234,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy)
|
||||||
|
|
||||||
|
assign_error:
|
||||||
|
while (--n >= 0) {
|
||||||
|
- if (!virtio_queue_get_num(proxy->vdev, n)) {
|
||||||
|
+ if (!virtio_queue_get_num(vdev, n)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -235,6 +247,7 @@ assign_error:
|
||||||
|
|
||||||
|
static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
int r;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
@@ -243,7 +256,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
|
||||||
|
- if (!virtio_queue_get_num(proxy->vdev, n)) {
|
||||||
|
+ if (!virtio_queue_get_num(vdev, n)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -256,7 +269,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy)
|
||||||
|
static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = opaque;
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
hwaddr pa;
|
||||||
|
|
||||||
|
switch (addr) {
|
||||||
|
@@ -271,7 +284,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT;
|
||||||
|
if (pa == 0) {
|
||||||
|
virtio_pci_stop_ioeventfd(proxy);
|
||||||
|
- virtio_reset(proxy->vdev);
|
||||||
|
+ virtio_reset(vdev);
|
||||||
|
msix_unuse_all_vectors(&proxy->pci_dev);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
@@ -298,7 +311,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vdev->status == 0) {
|
||||||
|
- virtio_reset(proxy->vdev);
|
||||||
|
+ virtio_reset(vdev);
|
||||||
|
msix_unuse_all_vectors(&proxy->pci_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -334,7 +347,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
|
||||||
|
|
||||||
|
static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)
|
||||||
|
{
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
uint32_t ret = 0xFFFFFFFF;
|
||||||
|
|
||||||
|
switch (addr) {
|
||||||
|
@@ -380,6 +393,7 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
|
||||||
|
unsigned size)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = opaque;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
|
||||||
|
uint64_t val = 0;
|
||||||
|
if (addr < config) {
|
||||||
|
@@ -389,16 +403,16 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr,
|
||||||
|
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
- val = virtio_config_readb(proxy->vdev, addr);
|
||||||
|
+ val = virtio_config_readb(vdev, addr);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
- val = virtio_config_readw(proxy->vdev, addr);
|
||||||
|
+ val = virtio_config_readw(vdev, addr);
|
||||||
|
if (virtio_is_big_endian()) {
|
||||||
|
val = bswap16(val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
- val = virtio_config_readl(proxy->vdev, addr);
|
||||||
|
+ val = virtio_config_readl(vdev, addr);
|
||||||
|
if (virtio_is_big_endian()) {
|
||||||
|
val = bswap32(val);
|
||||||
|
}
|
||||||
|
@@ -412,6 +426,7 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr,
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = opaque;
|
||||||
|
uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
if (addr < config) {
|
||||||
|
virtio_ioport_write(proxy, addr, val);
|
||||||
|
return;
|
||||||
|
@@ -423,19 +438,19 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr,
|
||||||
|
*/
|
||||||
|
switch (size) {
|
||||||
|
case 1:
|
||||||
|
- virtio_config_writeb(proxy->vdev, addr, val);
|
||||||
|
+ virtio_config_writeb(vdev, addr, val);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (virtio_is_big_endian()) {
|
||||||
|
val = bswap16(val);
|
||||||
|
}
|
||||||
|
- virtio_config_writew(proxy->vdev, addr, val);
|
||||||
|
+ virtio_config_writew(vdev, addr, val);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
if (virtio_is_big_endian()) {
|
||||||
|
val = bswap32(val);
|
||||||
|
}
|
||||||
|
- virtio_config_writel(proxy->vdev, addr, val);
|
||||||
|
+ virtio_config_writel(vdev, addr, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -454,6 +469,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
|
||||||
|
uint32_t val, int len)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
|
||||||
|
pci_default_write_config(pci_dev, address, val, len);
|
||||||
|
|
||||||
|
@@ -461,8 +477,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
|
||||||
|
!(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) &&
|
||||||
|
!(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) {
|
||||||
|
virtio_pci_stop_ioeventfd(proxy);
|
||||||
|
- virtio_set_status(proxy->vdev,
|
||||||
|
- proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
|
||||||
|
+ virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -505,7 +520,8 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy,
|
||||||
|
unsigned int vector)
|
||||||
|
{
|
||||||
|
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
|
||||||
|
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
|
||||||
|
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
|
||||||
|
int ret;
|
||||||
|
ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, irqfd->virq);
|
||||||
|
@@ -516,7 +532,8 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
|
||||||
|
unsigned int queue_no,
|
||||||
|
unsigned int vector)
|
||||||
|
{
|
||||||
|
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
|
||||||
|
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
|
||||||
|
VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector];
|
||||||
|
int ret;
|
||||||
|
@@ -528,7 +545,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy,
|
||||||
|
static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs)
|
||||||
|
{
|
||||||
|
PCIDevice *dev = &proxy->pci_dev;
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
unsigned int vector;
|
||||||
|
int ret, queue_no;
|
||||||
|
@@ -577,7 +594,7 @@ undo:
|
||||||
|
static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs)
|
||||||
|
{
|
||||||
|
PCIDevice *dev = &proxy->pci_dev;
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
unsigned int vector;
|
||||||
|
int queue_no;
|
||||||
|
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
@@ -605,8 +622,9 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
|
||||||
|
unsigned int vector,
|
||||||
|
MSIMessage msg)
|
||||||
|
{
|
||||||
|
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
|
||||||
|
- VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, queue_no);
|
||||||
|
EventNotifier *n = virtio_queue_get_guest_notifier(vq);
|
||||||
|
VirtIOIRQFD *irqfd;
|
||||||
|
int ret = 0;
|
||||||
|
@@ -625,10 +643,10 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy,
|
||||||
|
* Otherwise, set it up now.
|
||||||
|
*/
|
||||||
|
if (k->guest_notifier_mask) {
|
||||||
|
- k->guest_notifier_mask(proxy->vdev, queue_no, false);
|
||||||
|
+ k->guest_notifier_mask(vdev, queue_no, false);
|
||||||
|
/* Test after unmasking to avoid losing events. */
|
||||||
|
if (k->guest_notifier_pending &&
|
||||||
|
- k->guest_notifier_pending(proxy->vdev, queue_no)) {
|
||||||
|
+ k->guest_notifier_pending(vdev, queue_no)) {
|
||||||
|
event_notifier_set(n);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
@@ -641,13 +659,14 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy,
|
||||||
|
unsigned int queue_no,
|
||||||
|
unsigned int vector)
|
||||||
|
{
|
||||||
|
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
|
||||||
|
/* If guest supports masking, keep irqfd but mask it.
|
||||||
|
* Otherwise, clean it up now.
|
||||||
|
*/
|
||||||
|
if (k->guest_notifier_mask) {
|
||||||
|
- k->guest_notifier_mask(proxy->vdev, queue_no, true);
|
||||||
|
+ k->guest_notifier_mask(vdev, queue_no, true);
|
||||||
|
} else {
|
||||||
|
kvm_virtio_pci_irqfd_release(proxy, queue_no, vector);
|
||||||
|
}
|
||||||
|
@@ -657,7 +676,7 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector,
|
||||||
|
MSIMessage msg)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
int ret, queue_no;
|
||||||
|
|
||||||
|
for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
|
||||||
|
@@ -687,7 +706,7 @@ undo:
|
||||||
|
static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
int queue_no;
|
||||||
|
|
||||||
|
for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) {
|
||||||
|
@@ -706,7 +725,7 @@ static void virtio_pci_vector_poll(PCIDevice *dev,
|
||||||
|
unsigned int vector_end)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev);
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
int queue_no;
|
||||||
|
unsigned int vector;
|
||||||
|
@@ -738,8 +757,9 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
|
||||||
|
bool with_irqfd)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
- VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(proxy->vdev);
|
||||||
|
- VirtQueue *vq = virtio_get_queue(proxy->vdev, n);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||||
|
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||||
|
|
||||||
|
if (assign) {
|
||||||
|
@@ -754,7 +774,7 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) {
|
||||||
|
- vdc->guest_notifier_mask(proxy->vdev, n, !assign);
|
||||||
|
+ vdc->guest_notifier_mask(vdev, n, !assign);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
@@ -769,7 +789,7 @@ static bool virtio_pci_query_guest_notifiers(DeviceState *d)
|
||||||
|
static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
- VirtIODevice *vdev = proxy->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
int r, n;
|
||||||
|
bool with_irqfd = msix_enabled(&proxy->pci_dev) &&
|
||||||
|
@@ -863,11 +883,12 @@ static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign)
|
||||||
|
static void virtio_pci_vmstate_change(DeviceState *d, bool running)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
|
||||||
|
|
||||||
|
if (running) {
|
||||||
|
/* Try to find out if the guest has bus master disabled, but is
|
||||||
|
in ready state. Then we have a buggy guest OS. */
|
||||||
|
- if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
|
||||||
|
+ if ((vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
|
||||||
|
!(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
|
||||||
|
proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG;
|
||||||
|
}
|
||||||
|
@@ -942,8 +963,6 @@ static void virtio_pci_device_plugged(DeviceState *d)
|
||||||
|
uint8_t *config;
|
||||||
|
uint32_t size;
|
||||||
|
|
||||||
|
- proxy->vdev = virtio_bus_get_device(bus);
|
||||||
|
-
|
||||||
|
config = proxy->pci_dev.config;
|
||||||
|
if (proxy->class_code) {
|
||||||
|
pci_config_set_class(config, proxy->class_code);
|
||||||
|
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
|
||||||
|
index 917bcc5..dc332ae 100644
|
||||||
|
--- a/hw/virtio/virtio-pci.h
|
||||||
|
+++ b/hw/virtio/virtio-pci.h
|
||||||
|
@@ -82,7 +82,6 @@ typedef struct VirtioPCIClass {
|
||||||
|
|
||||||
|
struct VirtIOPCIProxy {
|
||||||
|
PCIDevice pci_dev;
|
||||||
|
- VirtIODevice *vdev;
|
||||||
|
MemoryRegion bar;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t class_code;
|
292
0008-virtio-ccw-remove-vdev-field.patch
Normal file
292
0008-virtio-ccw-remove-vdev-field.patch
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:52 +0200
|
||||||
|
Subject: [PATCH] virtio-ccw: remove vdev field
|
||||||
|
|
||||||
|
The vdev field is complicated to synchronize. Just access the
|
||||||
|
BusState's list of children.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/s390x/virtio-ccw.c | 80 ++++++++++++++++++++++++++++-----------------------
|
||||||
|
1 file changed, 44 insertions(+), 36 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
|
||||||
|
index 8835bd4..0fc7387 100644
|
||||||
|
--- a/hw/s390x/virtio-ccw.c
|
||||||
|
+++ b/hw/s390x/virtio-ccw.c
|
||||||
|
@@ -56,9 +56,10 @@ static const TypeInfo virtual_css_bus_info = {
|
||||||
|
VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = NULL;
|
||||||
|
+ VirtioCcwDevice *dev = sch->driver_data;
|
||||||
|
|
||||||
|
- if (sch->driver_data) {
|
||||||
|
- vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev;
|
||||||
|
+ if (dev) {
|
||||||
|
+ vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
}
|
||||||
|
return vdev;
|
||||||
|
}
|
||||||
|
@@ -66,7 +67,8 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch)
|
||||||
|
static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
|
||||||
|
bool assign, bool set_handler)
|
||||||
|
{
|
||||||
|
- VirtQueue *vq = virtio_get_queue(dev->vdev, n);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||||
|
EventNotifier *notifier = virtio_queue_get_host_notifier(vq);
|
||||||
|
int r = 0;
|
||||||
|
SubchDev *sch = dev->sch;
|
||||||
|
@@ -96,6 +98,7 @@ static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n,
|
||||||
|
|
||||||
|
static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev;
|
||||||
|
int n, r;
|
||||||
|
|
||||||
|
if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) ||
|
||||||
|
@@ -103,8 +106,9 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
|
||||||
|
dev->ioeventfd_started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
+ vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
|
||||||
|
- if (!virtio_queue_get_num(dev->vdev, n)) {
|
||||||
|
+ if (!virtio_queue_get_num(vdev, n)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
r = virtio_ccw_set_guest2host_notifier(dev, n, true, true);
|
||||||
|
@@ -117,7 +121,7 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
|
||||||
|
|
||||||
|
assign_error:
|
||||||
|
while (--n >= 0) {
|
||||||
|
- if (!virtio_queue_get_num(dev->vdev, n)) {
|
||||||
|
+ if (!virtio_queue_get_num(vdev, n)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
|
||||||
|
@@ -131,13 +135,15 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev)
|
||||||
|
|
||||||
|
static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev)
|
||||||
|
{
|
||||||
|
+ VirtIODevice *vdev;
|
||||||
|
int n, r;
|
||||||
|
|
||||||
|
if (!dev->ioeventfd_started) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
+ vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) {
|
||||||
|
- if (!virtio_queue_get_num(dev->vdev, n)) {
|
||||||
|
+ if (!virtio_queue_get_num(vdev, n)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
r = virtio_ccw_set_guest2host_notifier(dev, n, false, false);
|
||||||
|
@@ -188,7 +194,7 @@ typedef struct VirtioFeatDesc {
|
||||||
|
static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
|
||||||
|
uint16_t index, uint16_t num)
|
||||||
|
{
|
||||||
|
- VirtioCcwDevice *dev = sch->driver_data;
|
||||||
|
+ VirtIODevice *vdev = virtio_ccw_get_vdev(sch);
|
||||||
|
|
||||||
|
if (index > VIRTIO_PCI_QUEUE_MAX) {
|
||||||
|
return -EINVAL;
|
||||||
|
@@ -199,23 +205,23 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align,
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (!dev) {
|
||||||
|
+ if (!vdev) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- virtio_queue_set_addr(dev->vdev, index, addr);
|
||||||
|
+ virtio_queue_set_addr(vdev, index, addr);
|
||||||
|
if (!addr) {
|
||||||
|
- virtio_queue_set_vector(dev->vdev, index, 0);
|
||||||
|
+ virtio_queue_set_vector(vdev, index, 0);
|
||||||
|
} else {
|
||||||
|
/* Fail if we don't have a big enough queue. */
|
||||||
|
/* TODO: Add interface to handle vring.num changing */
|
||||||
|
- if (virtio_queue_get_num(dev->vdev, index) > num) {
|
||||||
|
+ if (virtio_queue_get_num(vdev, index) > num) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
- virtio_queue_set_vector(dev->vdev, index, index);
|
||||||
|
+ virtio_queue_set_vector(vdev, index, index);
|
||||||
|
}
|
||||||
|
/* tell notify handler in case of config change */
|
||||||
|
- dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
|
||||||
|
+ vdev->config_vector = VIRTIO_PCI_QUEUE_MAX;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -229,6 +235,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
hwaddr indicators;
|
||||||
|
VqConfigBlock vq_config;
|
||||||
|
VirtioCcwDevice *dev = sch->driver_data;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
bool check_len;
|
||||||
|
int len;
|
||||||
|
hwaddr hw_len;
|
||||||
|
@@ -271,7 +278,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
break;
|
||||||
|
case CCW_CMD_VDEV_RESET:
|
||||||
|
virtio_ccw_stop_ioeventfd(dev);
|
||||||
|
- virtio_reset(dev->vdev);
|
||||||
|
+ virtio_reset(vdev);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case CCW_CMD_READ_FEAT:
|
||||||
|
@@ -318,7 +325,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
features.features = ldl_le_phys(ccw.cda);
|
||||||
|
if (features.index < ARRAY_SIZE(dev->host_features)) {
|
||||||
|
virtio_bus_set_vdev_features(&dev->bus, features.features);
|
||||||
|
- dev->vdev->guest_features = features.features;
|
||||||
|
+ vdev->guest_features = features.features;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* If the guest supports more feature bits, assert that it
|
||||||
|
@@ -336,30 +343,30 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
break;
|
||||||
|
case CCW_CMD_READ_CONF:
|
||||||
|
if (check_len) {
|
||||||
|
- if (ccw.count > dev->vdev->config_len) {
|
||||||
|
+ if (ccw.count > vdev->config_len) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- len = MIN(ccw.count, dev->vdev->config_len);
|
||||||
|
+ len = MIN(ccw.count, vdev->config_len);
|
||||||
|
if (!ccw.cda) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
} else {
|
||||||
|
- virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config);
|
||||||
|
+ virtio_bus_get_vdev_config(&dev->bus, vdev->config);
|
||||||
|
/* XXX config space endianness */
|
||||||
|
- cpu_physical_memory_write(ccw.cda, dev->vdev->config, len);
|
||||||
|
+ cpu_physical_memory_write(ccw.cda, vdev->config, len);
|
||||||
|
sch->curr_status.scsw.count = ccw.count - len;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CCW_CMD_WRITE_CONF:
|
||||||
|
if (check_len) {
|
||||||
|
- if (ccw.count > dev->vdev->config_len) {
|
||||||
|
+ if (ccw.count > vdev->config_len) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- len = MIN(ccw.count, dev->vdev->config_len);
|
||||||
|
+ len = MIN(ccw.count, vdev->config_len);
|
||||||
|
hw_len = len;
|
||||||
|
if (!ccw.cda) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
@@ -370,9 +377,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
} else {
|
||||||
|
len = hw_len;
|
||||||
|
/* XXX config space endianness */
|
||||||
|
- memcpy(dev->vdev->config, config, len);
|
||||||
|
+ memcpy(vdev->config, config, len);
|
||||||
|
cpu_physical_memory_unmap(config, hw_len, 0, hw_len);
|
||||||
|
- virtio_bus_set_vdev_config(&dev->bus, dev->vdev->config);
|
||||||
|
+ virtio_bus_set_vdev_config(&dev->bus, vdev->config);
|
||||||
|
sch->curr_status.scsw.count = ccw.count - len;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
@@ -396,9 +403,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) {
|
||||||
|
virtio_ccw_stop_ioeventfd(dev);
|
||||||
|
}
|
||||||
|
- virtio_set_status(dev->vdev, status);
|
||||||
|
- if (dev->vdev->status == 0) {
|
||||||
|
- virtio_reset(dev->vdev);
|
||||||
|
+ virtio_set_status(vdev, status);
|
||||||
|
+ if (vdev->status == 0) {
|
||||||
|
+ virtio_reset(vdev);
|
||||||
|
}
|
||||||
|
if (status & VIRTIO_CONFIG_S_DRIVER_OK) {
|
||||||
|
virtio_ccw_start_ioeventfd(dev);
|
||||||
|
@@ -462,7 +469,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
|
||||||
|
ret = -EFAULT;
|
||||||
|
} else {
|
||||||
|
vq_config.index = lduw_phys(ccw.cda);
|
||||||
|
- vq_config.num_max = virtio_queue_get_num(dev->vdev,
|
||||||
|
+ vq_config.num_max = virtio_queue_get_num(vdev,
|
||||||
|
vq_config.index);
|
||||||
|
stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max);
|
||||||
|
sch->curr_status.scsw.count = ccw.count - sizeof(vq_config);
|
||||||
|
@@ -494,7 +501,6 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
|
||||||
|
sch->driver_data = dev;
|
||||||
|
dev->sch = sch;
|
||||||
|
|
||||||
|
- dev->vdev = vdev;
|
||||||
|
dev->indicators = 0;
|
||||||
|
|
||||||
|
/* Initialize subchannel structure. */
|
||||||
|
@@ -607,7 +613,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
|
||||||
|
memset(&sch->id, 0, sizeof(SenseId));
|
||||||
|
sch->id.reserved = 0xff;
|
||||||
|
sch->id.cu_type = VIRTIO_CCW_CU_TYPE;
|
||||||
|
- sch->id.cu_model = dev->vdev->device_id;
|
||||||
|
+ sch->id.cu_model = vdev->device_id;
|
||||||
|
|
||||||
|
/* Only the first 32 feature bits are used. */
|
||||||
|
dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus,
|
||||||
|
@@ -891,9 +897,10 @@ static unsigned virtio_ccw_get_features(DeviceState *d)
|
||||||
|
static void virtio_ccw_reset(DeviceState *d)
|
||||||
|
{
|
||||||
|
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
|
||||||
|
virtio_ccw_stop_ioeventfd(dev);
|
||||||
|
- virtio_reset(dev->vdev);
|
||||||
|
+ virtio_reset(vdev);
|
||||||
|
css_reset_sch(dev->sch);
|
||||||
|
dev->indicators = 0;
|
||||||
|
dev->indicators2 = 0;
|
||||||
|
@@ -933,9 +940,10 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
|
||||||
|
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||||
|
bool assign, bool with_irqfd)
|
||||||
|
{
|
||||||
|
- VirtQueue *vq = virtio_get_queue(dev->vdev, n);
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
+ VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||||
|
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||||
|
- VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(dev->vdev);
|
||||||
|
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
|
||||||
|
|
||||||
|
if (assign) {
|
||||||
|
int r = event_notifier_init(notifier, 0);
|
||||||
|
@@ -951,16 +959,16 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||||
|
* land in qemu (and only the irq fd) in this code.
|
||||||
|
*/
|
||||||
|
if (k->guest_notifier_mask) {
|
||||||
|
- k->guest_notifier_mask(dev->vdev, n, false);
|
||||||
|
+ k->guest_notifier_mask(vdev, n, false);
|
||||||
|
}
|
||||||
|
/* get lost events and re-inject */
|
||||||
|
if (k->guest_notifier_pending &&
|
||||||
|
- k->guest_notifier_pending(dev->vdev, n)) {
|
||||||
|
+ k->guest_notifier_pending(vdev, n)) {
|
||||||
|
event_notifier_set(notifier);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (k->guest_notifier_mask) {
|
||||||
|
- k->guest_notifier_mask(dev->vdev, n, true);
|
||||||
|
+ k->guest_notifier_mask(vdev, n, true);
|
||||||
|
}
|
||||||
|
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
|
||||||
|
event_notifier_cleanup(notifier);
|
||||||
|
@@ -972,7 +980,7 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
|
||||||
|
bool assigned)
|
||||||
|
{
|
||||||
|
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||||
|
- VirtIODevice *vdev = dev->vdev;
|
||||||
|
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||||
|
int r, n;
|
||||||
|
|
||||||
|
for (n = 0; n < nvqs; n++) {
|
147
0009-virtio-bus-cleanup-plug-unplug-interface.patch
Normal file
147
0009-virtio-bus-cleanup-plug-unplug-interface.patch
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:53 +0200
|
||||||
|
Subject: [PATCH] virtio-bus: cleanup plug/unplug interface
|
||||||
|
|
||||||
|
Right now we have these pairs:
|
||||||
|
|
||||||
|
- virtio_bus_plug_device/virtio_bus_destroy_device. The first
|
||||||
|
takes a VirtIODevice, the second takes a VirtioBusState
|
||||||
|
|
||||||
|
- device_plugged/device_unplug callbacks in the VirtioBusClass
|
||||||
|
(here it's just the naming that is inconsistent)
|
||||||
|
|
||||||
|
- virtio_bus_destroy_device is not called by anyone (and since
|
||||||
|
it calls qdev_free, it would be called by the proxies---but
|
||||||
|
then the callback is useless since the proxies can do whatever
|
||||||
|
they want before calling virtio_bus_destroy_device)
|
||||||
|
|
||||||
|
And there is a k->init but no k->exit, hence virtio_device_exit is
|
||||||
|
overwritten by subclasses (except virtio-9p). This cleans it up by:
|
||||||
|
|
||||||
|
- renaming the device_unplug callback to device_unplugged
|
||||||
|
|
||||||
|
- renaming virtio_bus_plug_device to virtio_bus_device_plugged,
|
||||||
|
matching the callback name
|
||||||
|
|
||||||
|
- renaming virtio_bus_destroy_device to virtio_bus_device_unplugged,
|
||||||
|
removing the qdev_free, making it take a VirtIODevice and calling it
|
||||||
|
from virtio_device_exit
|
||||||
|
|
||||||
|
- adding a k->exit callback
|
||||||
|
|
||||||
|
virtio_device_exit is still overwritten, the next patches will fix that.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/virtio/virtio-bus.c | 18 +++++++++---------
|
||||||
|
hw/virtio/virtio.c | 7 ++++++-
|
||||||
|
include/hw/virtio/virtio-bus.h | 6 +++---
|
||||||
|
include/hw/virtio/virtio.h | 1 +
|
||||||
|
4 files changed, 19 insertions(+), 13 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c
|
||||||
|
index 669ce38..7aed6a4 100644
|
||||||
|
--- a/hw/virtio/virtio-bus.c
|
||||||
|
+++ b/hw/virtio/virtio-bus.c
|
||||||
|
@@ -37,8 +37,8 @@ do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0)
|
||||||
|
#define DPRINTF(fmt, ...) do { } while (0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
-/* Plug the VirtIODevice */
|
||||||
|
-int virtio_bus_plug_device(VirtIODevice *vdev)
|
||||||
|
+/* A VirtIODevice is being plugged */
|
||||||
|
+int virtio_bus_device_plugged(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
DeviceState *qdev = DEVICE(vdev);
|
||||||
|
BusState *qbus = BUS(qdev_get_parent_bus(qdev));
|
||||||
|
@@ -64,20 +64,20 @@ void virtio_bus_reset(VirtioBusState *bus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-/* Destroy the VirtIODevice */
|
||||||
|
-void virtio_bus_destroy_device(VirtioBusState *bus)
|
||||||
|
+/* A VirtIODevice is being unplugged */
|
||||||
|
+void virtio_bus_device_unplugged(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- BusState *qbus = BUS(bus);
|
||||||
|
+ DeviceState *qdev = DEVICE(vdev);
|
||||||
|
+ BusState *qbus = BUS(qdev_get_parent_bus(qdev));
|
||||||
|
+ VirtioBusState *bus = VIRTIO_BUS(qbus);
|
||||||
|
VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus);
|
||||||
|
- VirtIODevice *vdev = virtio_bus_get_device(bus);
|
||||||
|
|
||||||
|
DPRINTF("%s: remove device.\n", qbus->name);
|
||||||
|
|
||||||
|
if (vdev != NULL) {
|
||||||
|
- if (klass->device_unplug != NULL) {
|
||||||
|
- klass->device_unplug(qbus->parent);
|
||||||
|
+ if (klass->device_unplugged != NULL) {
|
||||||
|
+ klass->device_unplugged(qbus->parent);
|
||||||
|
}
|
||||||
|
- qdev_free(DEVICE(vdev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
|
||||||
|
index 2f1e73b..965b2c0 100644
|
||||||
|
--- a/hw/virtio/virtio.c
|
||||||
|
+++ b/hw/virtio/virtio.c
|
||||||
|
@@ -1158,14 +1158,19 @@ static int virtio_device_init(DeviceState *qdev)
|
||||||
|
if (k->init(vdev) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
- virtio_bus_plug_device(vdev);
|
||||||
|
+ virtio_bus_device_plugged(vdev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int virtio_device_exit(DeviceState *qdev)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||||
|
+ VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(qdev);
|
||||||
|
|
||||||
|
+ virtio_bus_device_unplugged(vdev);
|
||||||
|
+ if (k->exit) {
|
||||||
|
+ k->exit(vdev);
|
||||||
|
+ }
|
||||||
|
if (vdev->bus_name) {
|
||||||
|
g_free(vdev->bus_name);
|
||||||
|
vdev->bus_name = NULL;
|
||||||
|
diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h
|
||||||
|
index ba0f86a..0756545 100644
|
||||||
|
--- a/include/hw/virtio/virtio-bus.h
|
||||||
|
+++ b/include/hw/virtio/virtio-bus.h
|
||||||
|
@@ -61,7 +61,7 @@ typedef struct VirtioBusClass {
|
||||||
|
* transport independent exit function.
|
||||||
|
* This is called by virtio-bus just before the device is unplugged.
|
||||||
|
*/
|
||||||
|
- void (*device_unplug)(DeviceState *d);
|
||||||
|
+ void (*device_unplugged)(DeviceState *d);
|
||||||
|
/*
|
||||||
|
* Does the transport have variable vring alignment?
|
||||||
|
* (ie can it ever call virtio_queue_set_align()?)
|
||||||
|
@@ -74,9 +74,9 @@ struct VirtioBusState {
|
||||||
|
BusState parent_obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
-int virtio_bus_plug_device(VirtIODevice *vdev);
|
||||||
|
+int virtio_bus_device_plugged(VirtIODevice *vdev);
|
||||||
|
void virtio_bus_reset(VirtioBusState *bus);
|
||||||
|
-void virtio_bus_destroy_device(VirtioBusState *bus);
|
||||||
|
+void virtio_bus_device_unplugged(VirtIODevice *bus);
|
||||||
|
/* Get the device id of the plugged device. */
|
||||||
|
uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus);
|
||||||
|
/* Get the config_len field of the plugged device. */
|
||||||
|
diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h
|
||||||
|
index a90522d..59756c2 100644
|
||||||
|
--- a/include/hw/virtio/virtio.h
|
||||||
|
+++ b/include/hw/virtio/virtio.h
|
||||||
|
@@ -127,6 +127,7 @@ typedef struct VirtioDeviceClass {
|
||||||
|
/* This is what a VirtioDevice must implement */
|
||||||
|
DeviceClass parent;
|
||||||
|
int (*init)(VirtIODevice *vdev);
|
||||||
|
+ void (*exit)(VirtIODevice *vdev);
|
||||||
|
uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features);
|
||||||
|
uint32_t (*bad_features)(VirtIODevice *vdev);
|
||||||
|
void (*set_features)(VirtIODevice *vdev, uint32_t val);
|
@ -0,0 +1,52 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:54 +0200
|
||||||
|
Subject: [PATCH] virtio-blk: switch exit callback to VirtioDeviceClass
|
||||||
|
|
||||||
|
This ensures hot-unplug is handled properly by the proxy.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/block/virtio-blk.c | 10 ++++------
|
||||||
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
|
||||||
|
index 49a23c3..aa37cc9 100644
|
||||||
|
--- a/hw/block/virtio-blk.c
|
||||||
|
+++ b/hw/block/virtio-blk.c
|
||||||
|
@@ -729,20 +729,18 @@ static int virtio_blk_device_init(VirtIODevice *vdev)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int virtio_blk_device_exit(DeviceState *dev)
|
||||||
|
+static void virtio_blk_device_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
|
- VirtIOBlock *s = VIRTIO_BLK(dev);
|
||||||
|
+ VirtIOBlock *s = VIRTIO_BLK(vdev);
|
||||||
|
#ifdef CONFIG_VIRTIO_BLK_DATA_PLANE
|
||||||
|
remove_migration_state_change_notifier(&s->migration_state_notifier);
|
||||||
|
virtio_blk_data_plane_destroy(s->dataplane);
|
||||||
|
s->dataplane = NULL;
|
||||||
|
#endif
|
||||||
|
qemu_del_vm_change_state_handler(s->change);
|
||||||
|
- unregister_savevm(dev, "virtio-blk", s);
|
||||||
|
+ unregister_savevm(DEVICE(vdev), "virtio-blk", s);
|
||||||
|
blockdev_mark_auto_del(s->bs);
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property virtio_blk_properties[] = {
|
||||||
|
@@ -754,10 +752,10 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = virtio_blk_device_exit;
|
||||||
|
dc->props = virtio_blk_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||||
|
vdc->init = virtio_blk_device_init;
|
||||||
|
+ vdc->exit = virtio_blk_device_exit;
|
||||||
|
vdc->get_config = virtio_blk_update_config;
|
||||||
|
vdc->set_config = virtio_blk_set_config;
|
||||||
|
vdc->get_features = virtio_blk_get_features;
|
@ -0,0 +1,52 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:55 +0200
|
||||||
|
Subject: [PATCH] virtio-serial: switch exit callback to VirtioDeviceClass
|
||||||
|
|
||||||
|
This ensures hot-unplug is handled properly by the proxy.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/char/virtio-serial-bus.c | 10 ++++------
|
||||||
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c
|
||||||
|
index da417c7..57dd070 100644
|
||||||
|
--- a/hw/char/virtio-serial-bus.c
|
||||||
|
+++ b/hw/char/virtio-serial-bus.c
|
||||||
|
@@ -987,12 +987,11 @@ static const TypeInfo virtio_serial_port_type_info = {
|
||||||
|
.class_init = virtio_serial_port_class_init,
|
||||||
|
};
|
||||||
|
|
||||||
|
-static int virtio_serial_device_exit(DeviceState *dev)
|
||||||
|
+static void virtio_serial_device_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIOSerial *vser = VIRTIO_SERIAL(dev);
|
||||||
|
- VirtIODevice *vdev = VIRTIO_DEVICE(dev);
|
||||||
|
+ VirtIOSerial *vser = VIRTIO_SERIAL(vdev);
|
||||||
|
|
||||||
|
- unregister_savevm(dev, "virtio-console", vser);
|
||||||
|
+ unregister_savevm(DEVICE(vdev), "virtio-console", vser);
|
||||||
|
|
||||||
|
g_free(vser->ivqs);
|
||||||
|
g_free(vser->ovqs);
|
||||||
|
@@ -1004,7 +1003,6 @@ static int virtio_serial_device_exit(DeviceState *dev)
|
||||||
|
g_free(vser->post_load);
|
||||||
|
}
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property virtio_serial_properties[] = {
|
||||||
|
@@ -1016,10 +1014,10 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = virtio_serial_device_exit;
|
||||||
|
dc->props = virtio_serial_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
|
||||||
|
vdc->init = virtio_serial_device_init;
|
||||||
|
+ vdc->exit = virtio_serial_device_exit;
|
||||||
|
vdc->get_features = get_features;
|
||||||
|
vdc->get_config = get_config;
|
||||||
|
vdc->set_config = set_config;
|
@ -0,0 +1,57 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:56 +0200
|
||||||
|
Subject: [PATCH] virtio-net: switch exit callback to VirtioDeviceClass
|
||||||
|
|
||||||
|
This ensures hot-unplug is handled properly by the proxy.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/net/virtio-net.c | 11 ++++-------
|
||||||
|
1 file changed, 4 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
|
||||||
|
index 5320aab..060b900 100644
|
||||||
|
--- a/hw/net/virtio-net.c
|
||||||
|
+++ b/hw/net/virtio-net.c
|
||||||
|
@@ -1568,16 +1568,15 @@ static int virtio_net_device_init(VirtIODevice *vdev)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int virtio_net_device_exit(DeviceState *qdev)
|
||||||
|
+static void virtio_net_device_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIONet *n = VIRTIO_NET(qdev);
|
||||||
|
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||||
|
+ VirtIONet *n = VIRTIO_NET(vdev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* This will stop vhost backend if appropriate. */
|
||||||
|
virtio_net_set_status(vdev, 0);
|
||||||
|
|
||||||
|
- unregister_savevm(qdev, "virtio-net", n);
|
||||||
|
+ unregister_savevm(DEVICE(vdev), "virtio-net", n);
|
||||||
|
|
||||||
|
if (n->netclient_name) {
|
||||||
|
g_free(n->netclient_name);
|
||||||
|
@@ -1608,8 +1607,6 @@ static int virtio_net_device_exit(DeviceState *qdev)
|
||||||
|
g_free(n->vqs);
|
||||||
|
qemu_del_nic(n->nic);
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
-
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_net_instance_init(Object *obj)
|
||||||
|
@@ -1636,10 +1633,10 @@ static void virtio_net_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = virtio_net_device_exit;
|
||||||
|
dc->props = virtio_net_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_NETWORK, dc->categories);
|
||||||
|
vdc->init = virtio_net_device_init;
|
||||||
|
+ vdc->exit = virtio_net_device_exit;
|
||||||
|
vdc->get_config = virtio_net_get_config;
|
||||||
|
vdc->set_config = virtio_net_set_config;
|
||||||
|
vdc->get_features = virtio_net_get_features;
|
111
0013-virtio-scsi-switch-exit-callback-to-VirtioDeviceClas.patch
Normal file
111
0013-virtio-scsi-switch-exit-callback-to-VirtioDeviceClas.patch
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:57 +0200
|
||||||
|
Subject: [PATCH] virtio-scsi: switch exit callback to VirtioDeviceClass
|
||||||
|
|
||||||
|
This ensures hot-unplug is handled properly by the proxy.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/scsi/vhost-scsi.c | 11 +++++------
|
||||||
|
hw/scsi/virtio-scsi.c | 15 +++++++--------
|
||||||
|
include/hw/virtio/virtio-scsi.h | 2 +-
|
||||||
|
3 files changed, 13 insertions(+), 15 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c
|
||||||
|
index 9e770fb..5e3cc61 100644
|
||||||
|
--- a/hw/scsi/vhost-scsi.c
|
||||||
|
+++ b/hw/scsi/vhost-scsi.c
|
||||||
|
@@ -240,11 +240,10 @@ static int vhost_scsi_init(VirtIODevice *vdev)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int vhost_scsi_exit(DeviceState *qdev)
|
||||||
|
+static void vhost_scsi_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||||
|
- VHostSCSI *s = VHOST_SCSI(qdev);
|
||||||
|
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
|
||||||
|
+ VHostSCSI *s = VHOST_SCSI(vdev);
|
||||||
|
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||||
|
|
||||||
|
migrate_del_blocker(s->migration_blocker);
|
||||||
|
error_free(s->migration_blocker);
|
||||||
|
@@ -253,7 +252,7 @@ static int vhost_scsi_exit(DeviceState *qdev)
|
||||||
|
vhost_scsi_set_status(vdev, 0);
|
||||||
|
|
||||||
|
g_free(s->dev.vqs);
|
||||||
|
- return virtio_scsi_common_exit(vs);
|
||||||
|
+ virtio_scsi_common_exit(vs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property vhost_scsi_properties[] = {
|
||||||
|
@@ -265,10 +264,10 @@ static void vhost_scsi_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = vhost_scsi_exit;
|
||||||
|
dc->props = vhost_scsi_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||||
|
vdc->init = vhost_scsi_init;
|
||||||
|
+ vdc->exit = vhost_scsi_exit;
|
||||||
|
vdc->get_features = vhost_scsi_get_features;
|
||||||
|
vdc->set_config = vhost_scsi_set_config;
|
||||||
|
vdc->set_status = vhost_scsi_set_status;
|
||||||
|
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
|
||||||
|
index 05da56b..5545993 100644
|
||||||
|
--- a/hw/scsi/virtio-scsi.c
|
||||||
|
+++ b/hw/scsi/virtio-scsi.c
|
||||||
|
@@ -643,22 +643,21 @@ static int virtio_scsi_device_init(VirtIODevice *vdev)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-int virtio_scsi_common_exit(VirtIOSCSICommon *vs)
|
||||||
|
+void virtio_scsi_common_exit(VirtIOSCSICommon *vs)
|
||||||
|
{
|
||||||
|
VirtIODevice *vdev = VIRTIO_DEVICE(vs);
|
||||||
|
|
||||||
|
g_free(vs->cmd_vqs);
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int virtio_scsi_device_exit(DeviceState *qdev)
|
||||||
|
+static void virtio_scsi_device_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIOSCSI *s = VIRTIO_SCSI(qdev);
|
||||||
|
- VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev);
|
||||||
|
+ VirtIOSCSI *s = VIRTIO_SCSI(vdev);
|
||||||
|
+ VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev);
|
||||||
|
|
||||||
|
- unregister_savevm(qdev, "virtio-scsi", s);
|
||||||
|
- return virtio_scsi_common_exit(vs);
|
||||||
|
+ unregister_savevm(DEVICE(vdev), "virtio-scsi", s);
|
||||||
|
+ virtio_scsi_common_exit(vs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property virtio_scsi_properties[] = {
|
||||||
|
@@ -679,10 +678,10 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = virtio_scsi_device_exit;
|
||||||
|
dc->props = virtio_scsi_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
|
||||||
|
vdc->init = virtio_scsi_device_init;
|
||||||
|
+ vdc->exit = virtio_scsi_device_exit;
|
||||||
|
vdc->set_config = virtio_scsi_set_config;
|
||||||
|
vdc->get_features = virtio_scsi_get_features;
|
||||||
|
vdc->reset = virtio_scsi_reset;
|
||||||
|
diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h
|
||||||
|
index 9a98540..206c61d 100644
|
||||||
|
--- a/include/hw/virtio/virtio-scsi.h
|
||||||
|
+++ b/include/hw/virtio/virtio-scsi.h
|
||||||
|
@@ -187,6 +187,6 @@ typedef struct {
|
||||||
|
VIRTIO_SCSI_F_CHANGE, true)
|
||||||
|
|
||||||
|
int virtio_scsi_common_init(VirtIOSCSICommon *vs);
|
||||||
|
-int virtio_scsi_common_exit(VirtIOSCSICommon *vs);
|
||||||
|
+void virtio_scsi_common_exit(VirtIOSCSICommon *vs);
|
||||||
|
|
||||||
|
#endif /* _QEMU_VIRTIO_SCSI_H */
|
@ -0,0 +1,48 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:58 +0200
|
||||||
|
Subject: [PATCH] virtio-balloon: switch exit callback to VirtioDeviceClass
|
||||||
|
|
||||||
|
This ensures hot-unplug is handled properly by the proxy.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/virtio/virtio-balloon.c | 10 ++++------
|
||||||
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
|
||||||
|
index aac7f83..c23facb 100644
|
||||||
|
--- a/hw/virtio/virtio-balloon.c
|
||||||
|
+++ b/hw/virtio/virtio-balloon.c
|
||||||
|
@@ -370,16 +370,14 @@ static int virtio_balloon_device_init(VirtIODevice *vdev)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int virtio_balloon_device_exit(DeviceState *qdev)
|
||||||
|
+static void virtio_balloon_device_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIOBalloon *s = VIRTIO_BALLOON(qdev);
|
||||||
|
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||||
|
+ VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
|
||||||
|
|
||||||
|
balloon_stats_destroy_timer(s);
|
||||||
|
qemu_remove_balloon_handler(s);
|
||||||
|
- unregister_savevm(qdev, "virtio-balloon", s);
|
||||||
|
+ unregister_savevm(DEVICE(vdev), "virtio-balloon", s);
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property virtio_balloon_properties[] = {
|
||||||
|
@@ -390,10 +388,10 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = virtio_balloon_device_exit;
|
||||||
|
dc->props = virtio_balloon_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
|
vdc->init = virtio_balloon_device_init;
|
||||||
|
+ vdc->exit = virtio_balloon_device_exit;
|
||||||
|
vdc->get_config = virtio_balloon_get_config;
|
||||||
|
vdc->set_config = virtio_balloon_set_config;
|
||||||
|
vdc->get_features = virtio_balloon_get_features;
|
@ -0,0 +1,48 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:57:59 +0200
|
||||||
|
Subject: [PATCH] virtio-rng: switch exit callback to VirtioDeviceClass
|
||||||
|
|
||||||
|
This ensures hot-unplug is handled properly by the proxy.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/virtio/virtio-rng.c | 10 ++++------
|
||||||
|
1 file changed, 4 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c
|
||||||
|
index bac8421..6895146 100644
|
||||||
|
--- a/hw/virtio/virtio-rng.c
|
||||||
|
+++ b/hw/virtio/virtio-rng.c
|
||||||
|
@@ -184,16 +184,14 @@ static int virtio_rng_device_init(VirtIODevice *vdev)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int virtio_rng_device_exit(DeviceState *qdev)
|
||||||
|
+static void virtio_rng_device_exit(VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
- VirtIORNG *vrng = VIRTIO_RNG(qdev);
|
||||||
|
- VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
|
||||||
|
+ VirtIORNG *vrng = VIRTIO_RNG(vdev);
|
||||||
|
|
||||||
|
qemu_del_timer(vrng->rate_limit_timer);
|
||||||
|
qemu_free_timer(vrng->rate_limit_timer);
|
||||||
|
- unregister_savevm(qdev, "virtio-rng", vrng);
|
||||||
|
+ unregister_savevm(DEVICE(vdev), "virtio-rng", vrng);
|
||||||
|
virtio_cleanup(vdev);
|
||||||
|
- return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Property virtio_rng_properties[] = {
|
||||||
|
@@ -205,10 +203,10 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data)
|
||||||
|
{
|
||||||
|
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||||
|
VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
|
||||||
|
- dc->exit = virtio_rng_device_exit;
|
||||||
|
dc->props = virtio_rng_properties;
|
||||||
|
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
|
||||||
|
vdc->init = virtio_rng_device_init;
|
||||||
|
+ vdc->exit = virtio_rng_device_exit;
|
||||||
|
vdc->get_features = get_features;
|
||||||
|
}
|
||||||
|
|
58
0016-virtio-pci-add-device_unplugged-callback.patch
Normal file
58
0016-virtio-pci-add-device_unplugged-callback.patch
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Fri, 20 Sep 2013 16:58:00 +0200
|
||||||
|
Subject: [PATCH] virtio-pci: add device_unplugged callback
|
||||||
|
|
||||||
|
This fixes a crash in hot-unplug of virtio-pci devices behind a PCIe
|
||||||
|
switch. The crash happens because the ioeventfd is still set whent the
|
||||||
|
child is destroyed (destruction happens in postorder). Then the proxy
|
||||||
|
tries to unset to ioeventfd, but the virtqueue structure that holds the
|
||||||
|
EventNotifier has been trashed in the meanwhile. kvm_set_ioeventfd_pio
|
||||||
|
does not expect failure and aborts.
|
||||||
|
|
||||||
|
The fix is simply to move parts of uninitialization to a new
|
||||||
|
device_unplugged callback, which is called before the child is destroyed.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
---
|
||||||
|
hw/virtio/virtio-pci.c | 12 ++++++++++--
|
||||||
|
1 file changed, 10 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
|
||||||
|
index 6fd6d6d..242ec3e 100644
|
||||||
|
--- a/hw/virtio/virtio-pci.c
|
||||||
|
+++ b/hw/virtio/virtio-pci.c
|
||||||
|
@@ -1000,6 +1000,15 @@ static void virtio_pci_device_plugged(DeviceState *d)
|
||||||
|
proxy->host_features);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void virtio_pci_device_unplugged(DeviceState *d)
|
||||||
|
+{
|
||||||
|
+ PCIDevice *pci_dev = PCI_DEVICE(d);
|
||||||
|
+ VirtIOPCIProxy *proxy = VIRTIO_PCI(d);
|
||||||
|
+
|
||||||
|
+ virtio_pci_stop_ioeventfd(proxy);
|
||||||
|
+ msix_uninit_exclusive_bar(pci_dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int virtio_pci_init(PCIDevice *pci_dev)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev);
|
||||||
|
@@ -1014,9 +1023,7 @@ static int virtio_pci_init(PCIDevice *pci_dev)
|
||||||
|
static void virtio_pci_exit(PCIDevice *pci_dev)
|
||||||
|
{
|
||||||
|
VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev);
|
||||||
|
- virtio_pci_stop_ioeventfd(proxy);
|
||||||
|
memory_region_destroy(&proxy->bar);
|
||||||
|
- msix_uninit_exclusive_bar(pci_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void virtio_pci_reset(DeviceState *qdev)
|
||||||
|
@@ -1550,6 +1557,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data)
|
||||||
|
k->set_guest_notifiers = virtio_pci_set_guest_notifiers;
|
||||||
|
k->vmstate_change = virtio_pci_vmstate_change;
|
||||||
|
k->device_plugged = virtio_pci_device_plugged;
|
||||||
|
+ k->device_unplugged = virtio_pci_device_unplugged;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const TypeInfo virtio_pci_bus_info = {
|
80
0101-qcow2-Pass-discard-type-to-qcow2_discard_clusters.patch
Normal file
80
0101-qcow2-Pass-discard-type-to-qcow2_discard_clusters.patch
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Fri, 6 Sep 2013 12:32:25 +0200
|
||||||
|
Subject: [PATCH] qcow2: Pass discard type to qcow2_discard_clusters()
|
||||||
|
|
||||||
|
The function will be used internally instead of only being called for
|
||||||
|
guest discard requests.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
---
|
||||||
|
block/qcow2-cluster.c | 8 ++++----
|
||||||
|
block/qcow2.c | 2 +-
|
||||||
|
block/qcow2.h | 2 +-
|
||||||
|
3 files changed, 6 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
|
||||||
|
index b558eb0..09abbf0 100644
|
||||||
|
--- a/block/qcow2-cluster.c
|
||||||
|
+++ b/block/qcow2-cluster.c
|
||||||
|
@@ -1320,7 +1320,7 @@ int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset)
|
||||||
|
* clusters.
|
||||||
|
*/
|
||||||
|
static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
|
- unsigned int nb_clusters)
|
||||||
|
+ unsigned int nb_clusters, enum qcow2_discard_type type)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
uint64_t *l2_table;
|
||||||
|
@@ -1349,7 +1349,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
|
l2_table[l2_index + i] = cpu_to_be64(0);
|
||||||
|
|
||||||
|
/* Then decrease the refcount */
|
||||||
|
- qcow2_free_any_clusters(bs, old_offset, 1, QCOW2_DISCARD_REQUEST);
|
||||||
|
+ qcow2_free_any_clusters(bs, old_offset, 1, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
|
||||||
|
@@ -1361,7 +1361,7 @@ static int discard_single_l2(BlockDriverState *bs, uint64_t offset,
|
||||||
|
}
|
||||||
|
|
||||||
|
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||||
|
- int nb_sectors)
|
||||||
|
+ int nb_sectors, enum qcow2_discard_type type)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
uint64_t end_offset;
|
||||||
|
@@ -1384,7 +1384,7 @@ int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||||
|
|
||||||
|
/* Each L2 table is handled by its own loop iteration */
|
||||||
|
while (nb_clusters > 0) {
|
||||||
|
- ret = discard_single_l2(bs, offset, nb_clusters);
|
||||||
|
+ ret = discard_single_l2(bs, offset, nb_clusters, type);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 3bf932b..f87e6e3 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -1510,7 +1510,7 @@ static coroutine_fn int qcow2_co_discard(BlockDriverState *bs,
|
||||||
|
|
||||||
|
qemu_co_mutex_lock(&s->lock);
|
||||||
|
ret = qcow2_discard_clusters(bs, sector_num << BDRV_SECTOR_BITS,
|
||||||
|
- nb_sectors);
|
||||||
|
+ nb_sectors, QCOW2_DISCARD_REQUEST);
|
||||||
|
qemu_co_mutex_unlock(&s->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index dba9771..52cf193 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -405,7 +405,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
|
||||||
|
|
||||||
|
int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m);
|
||||||
|
int qcow2_discard_clusters(BlockDriverState *bs, uint64_t offset,
|
||||||
|
- int nb_sectors);
|
||||||
|
+ int nb_sectors, enum qcow2_discard_type type);
|
||||||
|
int qcow2_zero_clusters(BlockDriverState *bs, uint64_t offset, int nb_sectors);
|
||||||
|
|
||||||
|
/* qcow2-snapshot.c functions */
|
@ -0,0 +1,72 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Fri, 6 Sep 2013 12:32:26 +0200
|
||||||
|
Subject: [PATCH] qcow2: Discard VM state in active L1 after creating snapshot
|
||||||
|
|
||||||
|
During savevm, the VM state is written to the active L1 of the image and
|
||||||
|
then a snapshot is taken. After that, the VM state isn't needed any more
|
||||||
|
in the active L1 and should be discarded. This is implemented by this
|
||||||
|
patch.
|
||||||
|
|
||||||
|
The impact of not discarding the VM state is that a snapshot can never
|
||||||
|
become smaller than any previous snapshot (because it would be padded
|
||||||
|
with old VM state), and more importantly that future savevm operations
|
||||||
|
cause unnecessary COWs (with associated flushes), which makes subsequent
|
||||||
|
snapshots much slower.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
---
|
||||||
|
block/qcow2-snapshot.c | 7 +++++++
|
||||||
|
block/qcow2.c | 5 -----
|
||||||
|
block/qcow2.h | 5 +++++
|
||||||
|
3 files changed, 12 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
|
||||||
|
index 0caac90..ae33b45 100644
|
||||||
|
--- a/block/qcow2-snapshot.c
|
||||||
|
+++ b/block/qcow2-snapshot.c
|
||||||
|
@@ -401,6 +401,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||||
|
|
||||||
|
g_free(old_snapshot_list);
|
||||||
|
|
||||||
|
+ /* The VM state isn't needed any more in the active L1 table; in fact, it
|
||||||
|
+ * hurts by causing expensive COW for the next snapshot. */
|
||||||
|
+ qcow2_discard_clusters(bs, qcow2_vm_state_offset(s),
|
||||||
|
+ align_offset(sn->vm_state_size, s->cluster_size)
|
||||||
|
+ >> BDRV_SECTOR_BITS,
|
||||||
|
+ QCOW2_DISCARD_NEVER);
|
||||||
|
+
|
||||||
|
#ifdef DEBUG_ALLOC
|
||||||
|
{
|
||||||
|
BdrvCheckResult result = {0};
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index f87e6e3..44161b2 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -1670,11 +1670,6 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static int64_t qcow2_vm_state_offset(BDRVQcowState *s)
|
||||||
|
-{
|
||||||
|
- return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index 52cf193..da61d18 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -324,6 +324,11 @@ static inline int64_t align_offset(int64_t offset, int n)
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
|
||||||
|
+{
|
||||||
|
+ return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||||
|
{
|
||||||
|
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
67
0103-hw-9pfs-Fix-errno-value-for-xattr-functions.patch
Normal file
67
0103-hw-9pfs-Fix-errno-value-for-xattr-functions.patch
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
From: "Daniel P. Berrange" <berrange@redhat.com>
|
||||||
|
Date: Tue, 1 Oct 2013 12:28:17 +0100
|
||||||
|
Subject: [PATCH] hw/9pfs: Fix errno value for xattr functions
|
||||||
|
|
||||||
|
If there is no operation driver for the xattr type the
|
||||||
|
functions return '-1' and set errno to '-EOPNOTSUPP'.
|
||||||
|
When the calling code sets 'ret = -errno' this turns
|
||||||
|
into a large positive number.
|
||||||
|
|
||||||
|
In Linux 3.11, the kernel has switched to using 9p
|
||||||
|
version 9p2000.L, instead of 9p2000.u, which enables
|
||||||
|
support for xattr operations. This on its own is harmless,
|
||||||
|
but for another change which makes it request the xattr
|
||||||
|
with a name 'security.capability'.
|
||||||
|
|
||||||
|
The result is that the guest sees a succesful return
|
||||||
|
of 95 bytes of data, instead of a failure with errno
|
||||||
|
set to 95. Since the kernel expects a maximum of 20
|
||||||
|
bytes for an xattr return this gets translated to the
|
||||||
|
unexpected errno ERANGE.
|
||||||
|
|
||||||
|
This all means that when running a binary off a 9p fs
|
||||||
|
in 3.11 kernels you get a fun result of:
|
||||||
|
|
||||||
|
# ./date
|
||||||
|
sh: ./date: Numerical result out of range
|
||||||
|
|
||||||
|
The only workaround is to pass 'version=9p2000.u' when
|
||||||
|
mounting the 9p fs in the guest, to disable all use of
|
||||||
|
xattrs.
|
||||||
|
|
||||||
|
Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
|
||||||
|
---
|
||||||
|
hw/9pfs/virtio-9p-xattr.c | 6 +++---
|
||||||
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/9pfs/virtio-9p-xattr.c b/hw/9pfs/virtio-9p-xattr.c
|
||||||
|
index 90ae565..3fae557 100644
|
||||||
|
--- a/hw/9pfs/virtio-9p-xattr.c
|
||||||
|
+++ b/hw/9pfs/virtio-9p-xattr.c
|
||||||
|
@@ -36,7 +36,7 @@ ssize_t v9fs_get_xattr(FsContext *ctx, const char *path,
|
||||||
|
if (xops) {
|
||||||
|
return xops->getxattr(ctx, path, name, value, size);
|
||||||
|
}
|
||||||
|
- errno = -EOPNOTSUPP;
|
||||||
|
+ errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -123,7 +123,7 @@ int v9fs_set_xattr(FsContext *ctx, const char *path, const char *name,
|
||||||
|
if (xops) {
|
||||||
|
return xops->setxattr(ctx, path, name, value, size, flags);
|
||||||
|
}
|
||||||
|
- errno = -EOPNOTSUPP;
|
||||||
|
+ errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
@@ -135,7 +135,7 @@ int v9fs_remove_xattr(FsContext *ctx,
|
||||||
|
if (xops) {
|
||||||
|
return xops->removexattr(ctx, path, name);
|
||||||
|
}
|
||||||
|
- errno = -EOPNOTSUPP;
|
||||||
|
+ errno = EOPNOTSUPP;
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
180
0104-qmp-access-the-local-QemuOptsLists-for-drive-option.patch
Normal file
180
0104-qmp-access-the-local-QemuOptsLists-for-drive-option.patch
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
From: Amos Kong <akong@redhat.com>
|
||||||
|
Date: Fri, 15 Nov 2013 18:53:14 +0100
|
||||||
|
Subject: [PATCH] qmp: access the local QemuOptsLists for drive option
|
||||||
|
|
||||||
|
Currently we have three QemuOptsList (qemu_common_drive_opts,
|
||||||
|
qemu_legacy_drive_opts, and qemu_drive_opts), only qemu_drive_opts
|
||||||
|
is added to vm_config_groups[].
|
||||||
|
|
||||||
|
This patch changes query-command-line-options to access three local
|
||||||
|
QemuOptsLists for drive option, and merge the description items
|
||||||
|
together.
|
||||||
|
|
||||||
|
Signed-off-by: Amos Kong <akong@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
---
|
||||||
|
blockdev.c | 1 -
|
||||||
|
include/qemu/config-file.h | 1 +
|
||||||
|
include/sysemu/sysemu.h | 1 +
|
||||||
|
util/qemu-config.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-
|
||||||
|
vl.c | 2 ++
|
||||||
|
5 files changed, 80 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/blockdev.c b/blockdev.c
|
||||||
|
index 097932c..1a6892e 100644
|
||||||
|
--- a/blockdev.c
|
||||||
|
+++ b/blockdev.c
|
||||||
|
@@ -45,7 +45,6 @@
|
||||||
|
#include "sysemu/arch_init.h"
|
||||||
|
|
||||||
|
static QTAILQ_HEAD(drivelist, DriveInfo) drives = QTAILQ_HEAD_INITIALIZER(drives);
|
||||||
|
-extern QemuOptsList qemu_common_drive_opts;
|
||||||
|
extern QemuOptsList qemu_old_drive_opts;
|
||||||
|
|
||||||
|
static const char *const if_name[IF_COUNT] = {
|
||||||
|
diff --git a/include/qemu/config-file.h b/include/qemu/config-file.h
|
||||||
|
index ad4a9e5..508428f 100644
|
||||||
|
--- a/include/qemu/config-file.h
|
||||||
|
+++ b/include/qemu/config-file.h
|
||||||
|
@@ -8,6 +8,7 @@
|
||||||
|
QemuOptsList *qemu_find_opts(const char *group);
|
||||||
|
QemuOptsList *qemu_find_opts_err(const char *group, Error **errp);
|
||||||
|
void qemu_add_opts(QemuOptsList *list);
|
||||||
|
+void qemu_add_drive_opts(QemuOptsList *list);
|
||||||
|
int qemu_set_option(const char *str);
|
||||||
|
int qemu_global_option(const char *str);
|
||||||
|
void qemu_add_globals(void);
|
||||||
|
diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h
|
||||||
|
index 1a77c99..4962cef 100644
|
||||||
|
--- a/include/sysemu/sysemu.h
|
||||||
|
+++ b/include/sysemu/sysemu.h
|
||||||
|
@@ -190,6 +190,7 @@ QemuOpts *qemu_get_machine_opts(void);
|
||||||
|
|
||||||
|
bool usb_enabled(bool default_usb);
|
||||||
|
|
||||||
|
+extern QemuOptsList qemu_common_drive_opts;
|
||||||
|
extern QemuOptsList qemu_drive_opts;
|
||||||
|
extern QemuOptsList qemu_chardev_opts;
|
||||||
|
extern QemuOptsList qemu_device_opts;
|
||||||
|
diff --git a/util/qemu-config.c b/util/qemu-config.c
|
||||||
|
index a59568d..04da942 100644
|
||||||
|
--- a/util/qemu-config.c
|
||||||
|
+++ b/util/qemu-config.c
|
||||||
|
@@ -8,6 +8,7 @@
|
||||||
|
#include "qmp-commands.h"
|
||||||
|
|
||||||
|
static QemuOptsList *vm_config_groups[32];
|
||||||
|
+static QemuOptsList *drive_config_groups[4];
|
||||||
|
|
||||||
|
static QemuOptsList *find_list(QemuOptsList **lists, const char *group,
|
||||||
|
Error **errp)
|
||||||
|
@@ -77,6 +78,59 @@ static CommandLineParameterInfoList *query_option_descs(const QemuOptDesc *desc)
|
||||||
|
return param_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
+/* remove repeated entry from the info list */
|
||||||
|
+static void cleanup_infolist(CommandLineParameterInfoList *head)
|
||||||
|
+{
|
||||||
|
+ CommandLineParameterInfoList *pre_entry, *cur, *del_entry;
|
||||||
|
+
|
||||||
|
+ cur = head;
|
||||||
|
+ while (cur->next) {
|
||||||
|
+ pre_entry = head;
|
||||||
|
+ while (pre_entry != cur->next) {
|
||||||
|
+ if (!strcmp(pre_entry->value->name, cur->next->value->name)) {
|
||||||
|
+ del_entry = cur->next;
|
||||||
|
+ cur->next = cur->next->next;
|
||||||
|
+ g_free(del_entry);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ pre_entry = pre_entry->next;
|
||||||
|
+ }
|
||||||
|
+ cur = cur->next;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* merge the description items of two parameter infolists */
|
||||||
|
+static void connect_infolist(CommandLineParameterInfoList *head,
|
||||||
|
+ CommandLineParameterInfoList *new)
|
||||||
|
+{
|
||||||
|
+ CommandLineParameterInfoList *cur;
|
||||||
|
+
|
||||||
|
+ cur = head;
|
||||||
|
+ while (cur->next) {
|
||||||
|
+ cur = cur->next;
|
||||||
|
+ }
|
||||||
|
+ cur->next = new;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/* access all the local QemuOptsLists for drive option */
|
||||||
|
+static CommandLineParameterInfoList *get_drive_infolist(void)
|
||||||
|
+{
|
||||||
|
+ CommandLineParameterInfoList *head = NULL, *cur;
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
+ for (i = 0; drive_config_groups[i] != NULL; i++) {
|
||||||
|
+ if (!head) {
|
||||||
|
+ head = query_option_descs(drive_config_groups[i]->desc);
|
||||||
|
+ } else {
|
||||||
|
+ cur = query_option_descs(drive_config_groups[i]->desc);
|
||||||
|
+ connect_infolist(head, cur);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ cleanup_infolist(head);
|
||||||
|
+
|
||||||
|
+ return head;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option,
|
||||||
|
const char *option,
|
||||||
|
Error **errp)
|
||||||
|
@@ -89,7 +143,12 @@ CommandLineOptionInfoList *qmp_query_command_line_options(bool has_option,
|
||||||
|
if (!has_option || !strcmp(option, vm_config_groups[i]->name)) {
|
||||||
|
info = g_malloc0(sizeof(*info));
|
||||||
|
info->option = g_strdup(vm_config_groups[i]->name);
|
||||||
|
- info->parameters = query_option_descs(vm_config_groups[i]->desc);
|
||||||
|
+ if (!strcmp("drive", vm_config_groups[i]->name)) {
|
||||||
|
+ info->parameters = get_drive_infolist();
|
||||||
|
+ } else {
|
||||||
|
+ info->parameters =
|
||||||
|
+ query_option_descs(vm_config_groups[i]->desc);
|
||||||
|
+ }
|
||||||
|
entry = g_malloc0(sizeof(*entry));
|
||||||
|
entry->value = info;
|
||||||
|
entry->next = conf_list;
|
||||||
|
@@ -109,6 +168,22 @@ QemuOptsList *qemu_find_opts_err(const char *group, Error **errp)
|
||||||
|
return find_list(vm_config_groups, group, errp);
|
||||||
|
}
|
||||||
|
|
||||||
|
+void qemu_add_drive_opts(QemuOptsList *list)
|
||||||
|
+{
|
||||||
|
+ int entries, i;
|
||||||
|
+
|
||||||
|
+ entries = ARRAY_SIZE(drive_config_groups);
|
||||||
|
+ entries--; /* keep list NULL terminated */
|
||||||
|
+ for (i = 0; i < entries; i++) {
|
||||||
|
+ if (drive_config_groups[i] == NULL) {
|
||||||
|
+ drive_config_groups[i] = list;
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ fprintf(stderr, "ran out of space in drive_config_groups");
|
||||||
|
+ abort();
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void qemu_add_opts(QemuOptsList *list)
|
||||||
|
{
|
||||||
|
int entries, i;
|
||||||
|
diff --git a/vl.c b/vl.c
|
||||||
|
index 2160933..63ecf16 100644
|
||||||
|
--- a/vl.c
|
||||||
|
+++ b/vl.c
|
||||||
|
@@ -2942,6 +2942,8 @@ int main(int argc, char **argv, char **envp)
|
||||||
|
module_call_init(MODULE_INIT_QOM);
|
||||||
|
|
||||||
|
qemu_add_opts(&qemu_drive_opts);
|
||||||
|
+ qemu_add_drive_opts(&qemu_common_drive_opts);
|
||||||
|
+ qemu_add_drive_opts(&qemu_drive_opts);
|
||||||
|
qemu_add_opts(&qemu_chardev_opts);
|
||||||
|
qemu_add_opts(&qemu_device_opts);
|
||||||
|
qemu_add_opts(&qemu_netdev_opts);
|
27
0105-seccomp-fine-tuning-whitelist-by-adding-times.patch
Normal file
27
0105-seccomp-fine-tuning-whitelist-by-adding-times.patch
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
From: Eduardo Otubo <otubo@linux.vnet.ibm.com>
|
||||||
|
Date: Tue, 24 Sep 2013 14:50:44 -0300
|
||||||
|
Subject: [PATCH] seccomp: fine tuning whitelist by adding times()
|
||||||
|
|
||||||
|
This was causing Qemu process to hang when using -sandbox on as
|
||||||
|
discribed on RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1004175
|
||||||
|
|
||||||
|
Signed-off-by: Eduardo Otubo <otubo@linux.vnet.ibm.com>
|
||||||
|
Tested-by: Paul Moore <pmoore@redhat.com>
|
||||||
|
Acked-by: Paul Moore <pmoore@redhat.com>
|
||||||
|
(cherry picked from commit c236f4519c9838801798f3705c17dce9ab9e3b9d)
|
||||||
|
---
|
||||||
|
qemu-seccomp.c | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/qemu-seccomp.c b/qemu-seccomp.c
|
||||||
|
index fb3cbfd..cf07869 100644
|
||||||
|
--- a/qemu-seccomp.c
|
||||||
|
+++ b/qemu-seccomp.c
|
||||||
|
@@ -90,6 +90,7 @@ static const struct QemuSeccompSyscall seccomp_whitelist[] = {
|
||||||
|
{ SCMP_SYS(getuid), 245 },
|
||||||
|
{ SCMP_SYS(geteuid), 245 },
|
||||||
|
{ SCMP_SYS(timer_create), 245 },
|
||||||
|
+ { SCMP_SYS(times), 245 },
|
||||||
|
{ SCMP_SYS(exit), 245 },
|
||||||
|
{ SCMP_SYS(clock_gettime), 245 },
|
||||||
|
{ SCMP_SYS(time), 245 },
|
32
0106-spice-flip-streaming-video-mode-to-off-by-default.patch
Normal file
32
0106-spice-flip-streaming-video-mode-to-off-by-default.patch
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
From: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
|
Date: Mon, 2 Dec 2013 11:17:04 +0100
|
||||||
|
Subject: [PATCH] spice: flip streaming video mode to off by default
|
||||||
|
|
||||||
|
Video streaming detection heuristics in spice-server have problems
|
||||||
|
keeping modern desktop animations (as done by gnome shell) and real
|
||||||
|
video playback apart. This leads to jpeg compression artefacts on
|
||||||
|
your desktop, due to spice using mjpeg to send what it thinks is
|
||||||
|
a video stream.
|
||||||
|
|
||||||
|
Turn off video detection by default to avoid these artifacts.
|
||||||
|
|
||||||
|
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
|
Reviewed-by: Alon Levy <alevy@redhat.com>
|
||||||
|
(cherry picked from commit f1d3e586f069e17f83b669842bc02d60d509daca)
|
||||||
|
---
|
||||||
|
ui/spice-core.c | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/ui/spice-core.c b/ui/spice-core.c
|
||||||
|
index bd7a248..3960fa0 100644
|
||||||
|
--- a/ui/spice-core.c
|
||||||
|
+++ b/ui/spice-core.c
|
||||||
|
@@ -778,6 +778,8 @@ void qemu_spice_init(void)
|
||||||
|
if (str) {
|
||||||
|
int streaming_video = parse_stream_video(str);
|
||||||
|
spice_server_set_streaming_video(spice_server, streaming_video);
|
||||||
|
+ } else {
|
||||||
|
+ spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
spice_server_set_agent_mouse
|
@ -0,0 +1,58 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Thu, 28 Nov 2013 11:01:13 +0100
|
||||||
|
Subject: [PATCH] scsi-bus: fix transfer length and direction for VERIFY
|
||||||
|
command
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
The amount of bytes to transfer depends on the BYTCHK field.
|
||||||
|
If any data is transferred, it is sent to the device.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Tested-by: Hervé Poussineau <hpoussin@reactos.org>
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
(cherry picked from commit d12ad44cc4cc9142179e64295608611f118b8ad8)
|
||||||
|
---
|
||||||
|
hw/scsi/scsi-bus.c | 14 +++++++++++++-
|
||||||
|
1 file changed, 13 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
|
||||||
|
index d352da7..e2fcd60 100644
|
||||||
|
--- a/hw/scsi/scsi-bus.c
|
||||||
|
+++ b/hw/scsi/scsi-bus.c
|
||||||
|
@@ -886,7 +886,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
||||||
|
case RELEASE:
|
||||||
|
case ERASE:
|
||||||
|
case ALLOW_MEDIUM_REMOVAL:
|
||||||
|
- case VERIFY_10:
|
||||||
|
case SEEK_10:
|
||||||
|
case SYNCHRONIZE_CACHE:
|
||||||
|
case SYNCHRONIZE_CACHE_16:
|
||||||
|
@@ -903,6 +902,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf)
|
||||||
|
case ALLOW_OVERWRITE:
|
||||||
|
cmd->xfer = 0;
|
||||||
|
break;
|
||||||
|
+ case VERIFY_10:
|
||||||
|
+ case VERIFY_12:
|
||||||
|
+ case VERIFY_16:
|
||||||
|
+ if ((buf[1] & 2) == 0) {
|
||||||
|
+ cmd->xfer = 0;
|
||||||
|
+ } else if ((buf[1] & 4) == 1) {
|
||||||
|
+ cmd->xfer = 1;
|
||||||
|
+ }
|
||||||
|
+ cmd->xfer *= dev->blocksize;
|
||||||
|
+ break;
|
||||||
|
case MODE_SENSE:
|
||||||
|
break;
|
||||||
|
case WRITE_SAME_10:
|
||||||
|
@@ -1100,6 +1109,9 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd)
|
||||||
|
case WRITE_VERIFY_12:
|
||||||
|
case WRITE_16:
|
||||||
|
case WRITE_VERIFY_16:
|
||||||
|
+ case VERIFY_10:
|
||||||
|
+ case VERIFY_12:
|
||||||
|
+ case VERIFY_16:
|
||||||
|
case COPY:
|
||||||
|
case COPY_VERIFY:
|
||||||
|
case COMPARE:
|
89
0108-scsi-disk-fix-VERIFY-emulation.patch
Normal file
89
0108-scsi-disk-fix-VERIFY-emulation.patch
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Thu, 28 Nov 2013 11:18:56 +0100
|
||||||
|
Subject: [PATCH] scsi-disk: fix VERIFY emulation
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
VERIFY emulation was completely botched (and remained botched through
|
||||||
|
all the refactorings). The command must be emulated both in check-medium
|
||||||
|
mode (BYTCHK=00, which we implement by doing nothing) and in check-bytes
|
||||||
|
mode (which we do not implement yet). Unlike WRITE AND VERIFY (which we
|
||||||
|
treat simply as WRITE with FUA bit set), VERIFY cannot be handled like
|
||||||
|
READ. In fact the device is _receiving_ data for VERIFY, not _sending_
|
||||||
|
it like READ.
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Tested-by: Hervé Poussineau <hpoussin@reactos.org>
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
(cherry picked from commit d97e7730816094a71cd1f19a56d7a73f77cdbf96)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
hw/scsi/scsi-disk.c
|
||||||
|
---
|
||||||
|
hw/scsi/scsi-disk.c | 26 +++++++++++++++++++-------
|
||||||
|
1 file changed, 19 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
|
||||||
|
index 74e6a14..1fd1c26 100644
|
||||||
|
--- a/hw/scsi/scsi-disk.c
|
||||||
|
+++ b/hw/scsi/scsi-disk.c
|
||||||
|
@@ -1597,6 +1597,14 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req)
|
||||||
|
scsi_disk_emulate_unmap(r, r->iov.iov_base);
|
||||||
|
break;
|
||||||
|
|
||||||
|
+ case VERIFY_10:
|
||||||
|
+ case VERIFY_12:
|
||||||
|
+ case VERIFY_16:
|
||||||
|
+ if (r->req.status == -1) {
|
||||||
|
+ scsi_check_condition(r, SENSE_CODE(INVALID_FIELD));
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
@@ -1837,6 +1845,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf)
|
||||||
|
case UNMAP:
|
||||||
|
DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer);
|
||||||
|
break;
|
||||||
|
+ case VERIFY_10:
|
||||||
|
+ case VERIFY_12:
|
||||||
|
+ case VERIFY_16:
|
||||||
|
+ DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3);
|
||||||
|
+ if (req->cmd.buf[1] & 6) {
|
||||||
|
+ goto illegal_request;
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
case WRITE_SAME_10:
|
||||||
|
case WRITE_SAME_16:
|
||||||
|
nb_sectors = scsi_data_cdb_length(r->req.cmd.buf);
|
||||||
|
@@ -1936,10 +1952,6 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf)
|
||||||
|
scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
- /* fallthrough */
|
||||||
|
- case VERIFY_10:
|
||||||
|
- case VERIFY_12:
|
||||||
|
- case VERIFY_16:
|
||||||
|
DPRINTF("Write %s(sector %" PRId64 ", count %u)\n",
|
||||||
|
(command & 0xe) == 0xe ? "And Verify " : "",
|
||||||
|
r->req.cmd.lba, len);
|
||||||
|
@@ -2207,14 +2219,14 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = {
|
||||||
|
[UNMAP] = &scsi_disk_emulate_reqops,
|
||||||
|
[WRITE_SAME_10] = &scsi_disk_emulate_reqops,
|
||||||
|
[WRITE_SAME_16] = &scsi_disk_emulate_reqops,
|
||||||
|
+ [VERIFY_10] = &scsi_disk_emulate_reqops,
|
||||||
|
+ [VERIFY_12] = &scsi_disk_emulate_reqops,
|
||||||
|
+ [VERIFY_16] = &scsi_disk_emulate_reqops,
|
||||||
|
|
||||||
|
[READ_6] = &scsi_disk_dma_reqops,
|
||||||
|
[READ_10] = &scsi_disk_dma_reqops,
|
||||||
|
[READ_12] = &scsi_disk_dma_reqops,
|
||||||
|
[READ_16] = &scsi_disk_dma_reqops,
|
||||||
|
- [VERIFY_10] = &scsi_disk_dma_reqops,
|
||||||
|
- [VERIFY_12] = &scsi_disk_dma_reqops,
|
||||||
|
- [VERIFY_16] = &scsi_disk_dma_reqops,
|
||||||
|
[WRITE_6] = &scsi_disk_dma_reqops,
|
||||||
|
[WRITE_10] = &scsi_disk_dma_reqops,
|
||||||
|
[WRITE_12] = &scsi_disk_dma_reqops,
|
@ -0,0 +1,46 @@
|
|||||||
|
From: Peter Lieven <pl@kamp.de>
|
||||||
|
Date: Thu, 24 Oct 2013 09:21:29 +0200
|
||||||
|
Subject: [PATCH] migration: drop MADVISE_DONT_NEED for incoming zero pages
|
||||||
|
|
||||||
|
The madvise for zeroed out pages was introduced when every transferred
|
||||||
|
zero page was memset to zero and thus allocated. Since commit
|
||||||
|
211ea740 we check for zeroness of a target page before we memset
|
||||||
|
it to zero. Additionally we memmap target memory so it is essentially
|
||||||
|
zero initialized (except for e.g. option roms and bios which are loaded
|
||||||
|
into target memory although they shouldn't).
|
||||||
|
|
||||||
|
It was reported recently that this madvise causes a performance degradation
|
||||||
|
in some situations. As the madvise should only be called rarely and if it's called
|
||||||
|
it is likely on a busy page (it was non-zero and changed to zero during migration)
|
||||||
|
drop it completely.
|
||||||
|
|
||||||
|
Reported-By: Zhang Haoyu <haoyu.zhang@huawei.com>
|
||||||
|
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Signed-off-by: Peter Lieven <pl@kamp.de>
|
||||||
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
||||||
|
(cherry picked from commit fc1c4a5d32e15a4c40c47945da85ef9c1e0c1b54)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
arch_init.c
|
||||||
|
---
|
||||||
|
arch_init.c | 7 -------
|
||||||
|
1 file changed, 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/arch_init.c b/arch_init.c
|
||||||
|
index 68a7ab7..23151b3 100644
|
||||||
|
--- a/arch_init.c
|
||||||
|
+++ b/arch_init.c
|
||||||
|
@@ -845,13 +845,6 @@ void ram_handle_compressed(void *host, uint8_t ch, uint64_t size)
|
||||||
|
{
|
||||||
|
if (ch != 0 || !is_zero_page(host)) {
|
||||||
|
memset(host, ch, size);
|
||||||
|
-#ifndef _WIN32
|
||||||
|
- if (ch == 0 &&
|
||||||
|
- (!kvm_enabled() || kvm_has_sync_mmu()) &&
|
||||||
|
- getpagesize() <= TARGET_PAGE_SIZE) {
|
||||||
|
- qemu_madvise(host, TARGET_PAGE_SIZE, QEMU_MADV_DONTNEED);
|
||||||
|
- }
|
||||||
|
-#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
|||||||
|
From: Christophe Fergeau <cfergeau@redhat.com>
|
||||||
|
Date: Thu, 30 Jan 2014 14:56:49 +0100
|
||||||
|
Subject: [PATCH] libcacard: Don't link with all libraries QEMU links to
|
||||||
|
|
||||||
|
As described in https://bugzilla.redhat.com/show_bug.cgi?id=987441 ,
|
||||||
|
libcacard currently links to all the libraries QEMU is linking to,
|
||||||
|
including glusterfs libraries, libiscsi, ... libcacard does not need all of
|
||||||
|
these. This patch ensures it's only linked with the libraries it needs.
|
||||||
|
|
||||||
|
Signed-off-by: Christophe Fergeau <cfergeau@redhat.com>
|
||||||
|
Signed-off-by: Alon Levy <alevy@redhat.com>
|
||||||
|
(cherry picked from commit 73db416ae7941f8ffeabc060ec87402b97314b6d)
|
||||||
|
---
|
||||||
|
libcacard/Makefile | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/libcacard/Makefile b/libcacard/Makefile
|
||||||
|
index 47827a0..9fa297c 100644
|
||||||
|
--- a/libcacard/Makefile
|
||||||
|
+++ b/libcacard/Makefile
|
||||||
|
@@ -24,7 +24,7 @@ vscclient$(EXESUF): libcacard/vscclient.o libcacard.la
|
||||||
|
|
||||||
|
libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \
|
||||||
|
-export-syms $(SRC_PATH)/libcacard/libcacard.syms
|
||||||
|
-libcacard.la: LIBS += $(libcacard_libs)
|
||||||
|
+libcacard.la: LIBS = $(libcacard_libs)
|
||||||
|
libcacard.la: $(libcacard-lobj-y)
|
||||||
|
$(call LINK,$^)
|
||||||
|
|
32
0111-gtk-Fix-mouse-warping-with-gtk3.patch
Normal file
32
0111-gtk-Fix-mouse-warping-with-gtk3.patch
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
From: Cole Robinson <crobinso@redhat.com>
|
||||||
|
Date: Thu, 13 Mar 2014 15:30:23 -0400
|
||||||
|
Subject: [PATCH] gtk: Fix mouse warping with gtk3
|
||||||
|
|
||||||
|
We were using the wrong coordinates, this fixes things to match the
|
||||||
|
original gtk2 implementation.
|
||||||
|
|
||||||
|
You can see this error in action by using -vga qxl, however even after this
|
||||||
|
patch the mouse warps in small increments up and to the left, -7x and -3y
|
||||||
|
pixels at a time, until the pointer is warped off the widget. I think it's
|
||||||
|
a qxl bug, but the next patch covers it up.
|
||||||
|
|
||||||
|
Signed-off-by: Cole Robinson <crobinso@redhat.com>
|
||||||
|
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
|
(cherry picked from commit 298526fe92d0b35ea343f8ddcc3a1d54cb422494)
|
||||||
|
---
|
||||||
|
ui/gtk.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ui/gtk.c b/ui/gtk.c
|
||||||
|
index c38146f..6c9d90a 100644
|
||||||
|
--- a/ui/gtk.c
|
||||||
|
+++ b/ui/gtk.c
|
||||||
|
@@ -355,7 +355,7 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
|
||||||
|
x, y, &x_root, &y_root);
|
||||||
|
gdk_device_warp(gdk_device_manager_get_client_pointer(mgr),
|
||||||
|
gtk_widget_get_screen(s->drawing_area),
|
||||||
|
- x, y);
|
||||||
|
+ x_root, y_root);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void gd_mouse_set(DisplayChangeListener *dcl,
|
41
0112-gtk-Don-t-warp-absolute-pointer.patch
Normal file
41
0112-gtk-Don-t-warp-absolute-pointer.patch
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
From: Cole Robinson <crobinso@redhat.com>
|
||||||
|
Date: Thu, 13 Mar 2014 15:30:24 -0400
|
||||||
|
Subject: [PATCH] gtk: Don't warp absolute pointer
|
||||||
|
|
||||||
|
This matches the behavior of SDL, and makes the mouse usable when
|
||||||
|
using -display gtk -vga qxl
|
||||||
|
|
||||||
|
https://bugzilla.redhat.com/show_bug.cgi?id=1051724
|
||||||
|
Signed-off-by: Cole Robinson <crobinso@redhat.com>
|
||||||
|
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
|
(cherry picked from commit 2bda66028b4962c36d4eabe2995edab12df93691)
|
||||||
|
---
|
||||||
|
ui/gtk.c | 8 ++++++++
|
||||||
|
1 file changed, 8 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/ui/gtk.c b/ui/gtk.c
|
||||||
|
index 6c9d90a..6ce9694 100644
|
||||||
|
--- a/ui/gtk.c
|
||||||
|
+++ b/ui/gtk.c
|
||||||
|
@@ -349,6 +349,10 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
|
||||||
|
GdkDeviceManager *mgr;
|
||||||
|
gint x_root, y_root;
|
||||||
|
|
||||||
|
+ if (kbd_mouse_is_absolute()) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
dpy = gtk_widget_get_display(s->drawing_area);
|
||||||
|
mgr = gdk_display_get_device_manager(dpy);
|
||||||
|
gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
|
||||||
|
@@ -364,6 +368,10 @@ static void gd_mouse_set(DisplayChangeListener *dcl,
|
||||||
|
GtkDisplayState *s = container_of(dcl, GtkDisplayState, dcl);
|
||||||
|
gint x_root, y_root;
|
||||||
|
|
||||||
|
+ if (kbd_mouse_is_absolute()) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
gdk_window_get_root_coords(gtk_widget_get_window(s->drawing_area),
|
||||||
|
x, y, &x_root, &y_root);
|
||||||
|
gdk_display_warp_pointer(gtk_widget_get_display(s->drawing_area),
|
@ -0,0 +1,36 @@
|
|||||||
|
From: Cole Robinson <crobinso@redhat.com>
|
||||||
|
Date: Wed, 19 Mar 2014 14:57:27 -0400
|
||||||
|
Subject: [PATCH] Change gtk quit accelerator to ctrl+shift+q (bz 1062393)
|
||||||
|
|
||||||
|
Similar patches queued for 2.1
|
||||||
|
---
|
||||||
|
ui/gtk.c | 7 +++----
|
||||||
|
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ui/gtk.c b/ui/gtk.c
|
||||||
|
index 6ce9694..8bc667d 100644
|
||||||
|
--- a/ui/gtk.c
|
||||||
|
+++ b/ui/gtk.c
|
||||||
|
@@ -1310,7 +1310,6 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce
|
||||||
|
{
|
||||||
|
GtkWidget *machine_menu;
|
||||||
|
GtkWidget *separator;
|
||||||
|
- GtkStockItem item;
|
||||||
|
|
||||||
|
machine_menu = gtk_menu_new();
|
||||||
|
gtk_menu_set_accel_group(GTK_MENU(machine_menu), accel_group);
|
||||||
|
@@ -1330,11 +1329,11 @@ static GtkWidget *gd_create_menu_machine(GtkDisplayState *s, GtkAccelGroup *acce
|
||||||
|
separator = gtk_separator_menu_item_new();
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), separator);
|
||||||
|
|
||||||
|
- s->quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL);
|
||||||
|
- gtk_stock_lookup(GTK_STOCK_QUIT, &item);
|
||||||
|
+ s->quit_item = gtk_menu_item_new_with_mnemonic(_("_Quit"));
|
||||||
|
gtk_menu_item_set_accel_path(GTK_MENU_ITEM(s->quit_item),
|
||||||
|
"<QEMU>/Machine/Quit");
|
||||||
|
- gtk_accel_map_add_entry("<QEMU>/Machine/Quit", item.keyval, item.modifier);
|
||||||
|
+ gtk_accel_map_add_entry("<QEMU>/Machine/Quit",
|
||||||
|
+ GDK_KEY_q, HOTKEY_MODIFIERS);
|
||||||
|
gtk_menu_shell_append(GTK_MENU_SHELL(machine_menu), s->quit_item);
|
||||||
|
|
||||||
|
return machine_menu;
|
@ -0,0 +1,30 @@
|
|||||||
|
From: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
Date: Wed, 15 Jan 2014 10:35:36 +0100
|
||||||
|
Subject: [PATCH] scsi: Assign cancel_io vector for scsi_disk_emulate_ops
|
||||||
|
|
||||||
|
Some emulated disk operations (MODE SELECT, UNMAP, WRITE SAME)
|
||||||
|
can trigger asynchronous I/Os. Provide the cancel_io callback
|
||||||
|
to ensure that AIOCBs are properly cleaned up.
|
||||||
|
|
||||||
|
Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
[Tweak commit message. - Paolo]
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
|
||||||
|
(cherry picked from commit 33325a53f15ab5370e1917b2a11cadffc77c5a52)
|
||||||
|
---
|
||||||
|
hw/scsi/scsi-disk.c | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
|
||||||
|
index 1fd1c26..ade5d4a 100644
|
||||||
|
--- a/hw/scsi/scsi-disk.c
|
||||||
|
+++ b/hw/scsi/scsi-disk.c
|
||||||
|
@@ -2181,6 +2181,7 @@ static const SCSIReqOps scsi_disk_emulate_reqops = {
|
||||||
|
.send_command = scsi_disk_emulate_command,
|
||||||
|
.read_data = scsi_disk_emulate_read_data,
|
||||||
|
.write_data = scsi_disk_emulate_write_data,
|
||||||
|
+ .cancel_io = scsi_cancel_io,
|
||||||
|
.get_buf = scsi_get_buf,
|
||||||
|
};
|
||||||
|
|
38
0115-virtio-scsi-Cleanup-of-I-Os-that-never-started.patch
Normal file
38
0115-virtio-scsi-Cleanup-of-I-Os-that-never-started.patch
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
From: Eric Farman <farman@linux.vnet.ibm.com>
|
||||||
|
Date: Tue, 14 Jan 2014 14:16:25 -0500
|
||||||
|
Subject: [PATCH] virtio-scsi: Cleanup of I/Os that never started
|
||||||
|
|
||||||
|
There is still a small window that occurs when a cancel I/O affects
|
||||||
|
an asynchronous I/O operation that hasn't started. In other words,
|
||||||
|
when the residual data length equals the expected data length.
|
||||||
|
|
||||||
|
Today, the routine virtio_scsi_command_complete fails because the
|
||||||
|
VirtIOSCSIReq pointer (from the hba_private field in SCSIRequest)
|
||||||
|
was cleared earlier when virtio_scsi_complete_req was called by
|
||||||
|
the virtio_scsi_request_cancelled routine. As a result, the
|
||||||
|
virtio_scsi_command_complete routine needs to simply return when
|
||||||
|
it is processing a SCSIRequest block that was marked canceled.
|
||||||
|
|
||||||
|
Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
(cherry picked from commit e9c0f0f58ad0a41c3c4b19e1911cfe095afc09ca)
|
||||||
|
---
|
||||||
|
hw/scsi/virtio-scsi.c | 4 ++++
|
||||||
|
1 file changed, 4 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
|
||||||
|
index 5545993..110827c 100644
|
||||||
|
--- a/hw/scsi/virtio-scsi.c
|
||||||
|
+++ b/hw/scsi/virtio-scsi.c
|
||||||
|
@@ -306,6 +306,10 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status,
|
||||||
|
VirtIOSCSIReq *req = r->hba_private;
|
||||||
|
uint32_t sense_len;
|
||||||
|
|
||||||
|
+ if (r->io_canceled) {
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
req->resp.cmd->response = VIRTIO_SCSI_S_OK;
|
||||||
|
req->resp.cmd->status = status;
|
||||||
|
if (req->resp.cmd->status == GOOD) {
|
29
0116-virtio-scsi-Prevent-assertion-on-missed-events.patch
Normal file
29
0116-virtio-scsi-Prevent-assertion-on-missed-events.patch
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
From: Eric Farman <farman@linux.vnet.ibm.com>
|
||||||
|
Date: Tue, 14 Jan 2014 14:16:26 -0500
|
||||||
|
Subject: [PATCH] virtio-scsi: Prevent assertion on missed events
|
||||||
|
|
||||||
|
In some cases, an unplug can cause events to be dropped, which
|
||||||
|
leads to an assertion failure when preparing to notify the guest
|
||||||
|
kernel.
|
||||||
|
|
||||||
|
Signed-off-by: Eric Farman <farman@linux.vnet.ibm.com>
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
|
||||||
|
(cherry picked from commit 49fb65c7f985baa56d2964e0a85c1f098e3e2a9d)
|
||||||
|
---
|
||||||
|
hw/scsi/virtio-scsi.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
|
||||||
|
index 110827c..15e40d9 100644
|
||||||
|
--- a/hw/scsi/virtio-scsi.c
|
||||||
|
+++ b/hw/scsi/virtio-scsi.c
|
||||||
|
@@ -520,7 +520,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev,
|
||||||
|
evt->event = event;
|
||||||
|
evt->reason = reason;
|
||||||
|
if (!dev) {
|
||||||
|
- assert(event == VIRTIO_SCSI_T_NO_EVENT);
|
||||||
|
+ assert(event == VIRTIO_SCSI_T_EVENTS_MISSED);
|
||||||
|
} else {
|
||||||
|
evt->lun[0] = 1;
|
||||||
|
evt->lun[1] = dev->id;
|
@ -0,0 +1,65 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:25 +0100
|
||||||
|
Subject: [PATCH] block/cloop: validate block_size header field (CVE-2014-0144)
|
||||||
|
|
||||||
|
Avoid unbounded s->uncompressed_block memory allocation by checking that
|
||||||
|
the block_size header field has a reasonable value. Also enforce the
|
||||||
|
assumption that the value is a non-zero multiple of 512.
|
||||||
|
|
||||||
|
These constraints conform to cloop 2.639's code so we accept existing
|
||||||
|
image files.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit d65f97a82c4ed48374a764c769d4ba1ea9724e97)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/075
|
||||||
|
tests/qemu-iotests/075.out
|
||||||
|
---
|
||||||
|
block/cloop.c | 23 +++++++++++++++++++++++
|
||||||
|
1 file changed, 23 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/cloop.c b/block/cloop.c
|
||||||
|
index 6ea7cf4..c2441b0 100644
|
||||||
|
--- a/block/cloop.c
|
||||||
|
+++ b/block/cloop.c
|
||||||
|
@@ -26,6 +26,9 @@
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
+/* Maximum compressed block size */
|
||||||
|
+#define MAX_BLOCK_SIZE (64 * 1024 * 1024)
|
||||||
|
+
|
||||||
|
typedef struct BDRVCloopState {
|
||||||
|
CoMutex lock;
|
||||||
|
uint32_t block_size;
|
||||||
|
@@ -67,6 +70,26 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
s->block_size = be32_to_cpu(s->block_size);
|
||||||
|
+ if (s->block_size % 512) {
|
||||||
|
+ fprintf(stderr, "block_size %u must be a multiple of 512",
|
||||||
|
+ s->block_size);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+ if (s->block_size == 0) {
|
||||||
|
+ fprintf(stderr, "block_size cannot be zero");
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but
|
||||||
|
+ * we can accept more. Prevent ridiculous values like 4 GB - 1 since we
|
||||||
|
+ * need a buffer this big.
|
||||||
|
+ */
|
||||||
|
+ if (s->block_size > MAX_BLOCK_SIZE) {
|
||||||
|
+ fprintf(stderr, "block_size %u must be %u MB or less",
|
||||||
|
+ s->block_size,
|
||||||
|
+ MAX_BLOCK_SIZE / (1024 * 1024));
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
|
||||||
|
if (ret < 0) {
|
@ -0,0 +1,62 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:26 +0100
|
||||||
|
Subject: [PATCH] block/cloop: prevent offsets_size integer overflow
|
||||||
|
(CVE-2014-0143)
|
||||||
|
|
||||||
|
The following integer overflow in offsets_size can lead to out-of-bounds
|
||||||
|
memory stores when n_blocks has a huge value:
|
||||||
|
|
||||||
|
uint32_t n_blocks, offsets_size;
|
||||||
|
[...]
|
||||||
|
ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4);
|
||||||
|
[...]
|
||||||
|
s->n_blocks = be32_to_cpu(s->n_blocks);
|
||||||
|
|
||||||
|
/* read offsets */
|
||||||
|
offsets_size = s->n_blocks * sizeof(uint64_t);
|
||||||
|
s->offsets = g_malloc(offsets_size);
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
for(i=0;i<s->n_blocks;i++) {
|
||||||
|
s->offsets[i] = be64_to_cpu(s->offsets[i]);
|
||||||
|
|
||||||
|
offsets_size can be smaller than n_blocks due to integer overflow.
|
||||||
|
Therefore s->offsets[] is too small when the for loop byteswaps offsets.
|
||||||
|
|
||||||
|
This patch refuses to open files if offsets_size would overflow.
|
||||||
|
|
||||||
|
Note that changing the type of offsets_size is not a fix since 32-bit
|
||||||
|
hosts still only have 32-bit size_t.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 509a41bab5306181044b5fff02eadf96d9c8676a)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/075
|
||||||
|
tests/qemu-iotests/075.out
|
||||||
|
---
|
||||||
|
block/cloop.c | 7 +++++++
|
||||||
|
1 file changed, 7 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/cloop.c b/block/cloop.c
|
||||||
|
index c2441b0..e20d0d8 100644
|
||||||
|
--- a/block/cloop.c
|
||||||
|
+++ b/block/cloop.c
|
||||||
|
@@ -98,6 +98,13 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->n_blocks = be32_to_cpu(s->n_blocks);
|
||||||
|
|
||||||
|
/* read offsets */
|
||||||
|
+ if (s->n_blocks > UINT32_MAX / sizeof(uint64_t)) {
|
||||||
|
+ /* Prevent integer overflow */
|
||||||
|
+ fprintf(stderr, "n_blocks %u must be %zu or less",
|
||||||
|
+ s->n_blocks,
|
||||||
|
+ UINT32_MAX / sizeof(uint64_t));
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
offsets_size = s->n_blocks * sizeof(uint64_t);
|
||||||
|
s->offsets = g_malloc(offsets_size);
|
||||||
|
|
@ -0,0 +1,46 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:27 +0100
|
||||||
|
Subject: [PATCH] block/cloop: refuse images with huge offsets arrays
|
||||||
|
(CVE-2014-0144)
|
||||||
|
|
||||||
|
Limit offsets_size to 512 MB so that:
|
||||||
|
|
||||||
|
1. g_malloc() does not abort due to an unreasonable size argument.
|
||||||
|
|
||||||
|
2. offsets_size does not overflow the bdrv_pread() int size argument.
|
||||||
|
|
||||||
|
This limit imposes a maximum image size of 16 TB at 256 KB block size.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 7b103b36d6ef3b11827c203d3a793bf7da50ecd6)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/075
|
||||||
|
tests/qemu-iotests/075.out
|
||||||
|
---
|
||||||
|
block/cloop.c | 9 +++++++++
|
||||||
|
1 file changed, 9 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/cloop.c b/block/cloop.c
|
||||||
|
index e20d0d8..cf81c61 100644
|
||||||
|
--- a/block/cloop.c
|
||||||
|
+++ b/block/cloop.c
|
||||||
|
@@ -106,6 +106,15 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
offsets_size = s->n_blocks * sizeof(uint64_t);
|
||||||
|
+ if (offsets_size > 512 * 1024 * 1024) {
|
||||||
|
+ /* Prevent ridiculous offsets_size which causes memory allocation to
|
||||||
|
+ * fail or overflows bdrv_pread() size. In practice the 512 MB
|
||||||
|
+ * offsets[] limit supports 16 TB images at 256 KB block size.
|
||||||
|
+ */
|
||||||
|
+ fprintf(stderr, "image requires too many offsets, "
|
||||||
|
+ "try increasing block size");
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
s->offsets = g_malloc(offsets_size);
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size);
|
@ -0,0 +1,70 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:28 +0100
|
||||||
|
Subject: [PATCH] block/cloop: refuse images with bogus offsets (CVE-2014-0144)
|
||||||
|
|
||||||
|
The offsets[] array allows efficient seeking and tells us the maximum
|
||||||
|
compressed data size. If the offsets are bogus the maximum compressed
|
||||||
|
data size will be unrealistic.
|
||||||
|
|
||||||
|
This could cause g_malloc() to abort and bogus offsets mean the image is
|
||||||
|
broken anyway. Therefore we should refuse such images.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit f56b9bc3ae20fc93815b34aa022be919941406ce)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/075
|
||||||
|
tests/qemu-iotests/075.out
|
||||||
|
---
|
||||||
|
block/cloop.c | 34 +++++++++++++++++++++++++++++-----
|
||||||
|
1 file changed, 29 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/cloop.c b/block/cloop.c
|
||||||
|
index cf81c61..5c9c085 100644
|
||||||
|
--- a/block/cloop.c
|
||||||
|
+++ b/block/cloop.c
|
||||||
|
@@ -123,12 +123,36 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(i=0;i<s->n_blocks;i++) {
|
||||||
|
+ uint64_t size;
|
||||||
|
+
|
||||||
|
s->offsets[i] = be64_to_cpu(s->offsets[i]);
|
||||||
|
- if (i > 0) {
|
||||||
|
- uint32_t size = s->offsets[i] - s->offsets[i - 1];
|
||||||
|
- if (size > max_compressed_block_size) {
|
||||||
|
- max_compressed_block_size = size;
|
||||||
|
- }
|
||||||
|
+ if (i == 0) {
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (s->offsets[i] < s->offsets[i - 1]) {
|
||||||
|
+ fprintf(stderr, "offsets not monotonically increasing at "
|
||||||
|
+ "index %u, image file is corrupt", i);
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ size = s->offsets[i] - s->offsets[i - 1];
|
||||||
|
+
|
||||||
|
+ /* Compressed blocks should be smaller than the uncompressed block size
|
||||||
|
+ * but maybe compression performed poorly so the compressed block is
|
||||||
|
+ * actually bigger. Clamp down on unrealistic values to prevent
|
||||||
|
+ * ridiculous s->compressed_block allocation.
|
||||||
|
+ */
|
||||||
|
+ if (size > 2 * MAX_BLOCK_SIZE) {
|
||||||
|
+ fprintf(stderr, "invalid compressed block size at index %u, "
|
||||||
|
+ "image file is corrupt", i);
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (size > max_compressed_block_size) {
|
||||||
|
+ max_compressed_block_size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
74
0121-block-cloop-fix-offsets-size-off-by-one.patch
Normal file
74
0121-block-cloop-fix-offsets-size-off-by-one.patch
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:29 +0100
|
||||||
|
Subject: [PATCH] block/cloop: fix offsets[] size off-by-one
|
||||||
|
|
||||||
|
cloop stores the number of compressed blocks in the n_blocks header
|
||||||
|
field. The file actually contains n_blocks + 1 offsets, where the extra
|
||||||
|
offset is the end-of-file offset.
|
||||||
|
|
||||||
|
The following line in cloop_read_block() results in an out-of-bounds
|
||||||
|
offsets[] access:
|
||||||
|
|
||||||
|
uint32_t bytes = s->offsets[block_num + 1] - s->offsets[block_num];
|
||||||
|
|
||||||
|
This patch allocates and loads the extra offset so that
|
||||||
|
cloop_read_block() works correctly when the last block is accessed.
|
||||||
|
|
||||||
|
Notice that we must free s->offsets[] unconditionally now since there is
|
||||||
|
always an end-of-file offset.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 42d43d35d907579179a39c924d169da924786f65)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/075
|
||||||
|
tests/qemu-iotests/075.out
|
||||||
|
---
|
||||||
|
block/cloop.c | 12 +++++-------
|
||||||
|
1 file changed, 5 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/cloop.c b/block/cloop.c
|
||||||
|
index 5c9c085..b28aae1 100644
|
||||||
|
--- a/block/cloop.c
|
||||||
|
+++ b/block/cloop.c
|
||||||
|
@@ -98,14 +98,14 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->n_blocks = be32_to_cpu(s->n_blocks);
|
||||||
|
|
||||||
|
/* read offsets */
|
||||||
|
- if (s->n_blocks > UINT32_MAX / sizeof(uint64_t)) {
|
||||||
|
+ if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) {
|
||||||
|
/* Prevent integer overflow */
|
||||||
|
fprintf(stderr, "n_blocks %u must be %zu or less",
|
||||||
|
s->n_blocks,
|
||||||
|
- UINT32_MAX / sizeof(uint64_t));
|
||||||
|
+ (UINT32_MAX - 1) / sizeof(uint64_t));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
- offsets_size = s->n_blocks * sizeof(uint64_t);
|
||||||
|
+ offsets_size = (s->n_blocks + 1) * sizeof(uint64_t);
|
||||||
|
if (offsets_size > 512 * 1024 * 1024) {
|
||||||
|
/* Prevent ridiculous offsets_size which causes memory allocation to
|
||||||
|
* fail or overflows bdrv_pread() size. In practice the 512 MB
|
||||||
|
@@ -122,7 +122,7 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- for(i=0;i<s->n_blocks;i++) {
|
||||||
|
+ for (i = 0; i < s->n_blocks + 1; i++) {
|
||||||
|
uint64_t size;
|
||||||
|
|
||||||
|
s->offsets[i] = be64_to_cpu(s->offsets[i]);
|
||||||
|
@@ -242,9 +242,7 @@ static coroutine_fn int cloop_co_read(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
static void cloop_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVCloopState *s = bs->opaque;
|
||||||
|
- if (s->n_blocks > 0) {
|
||||||
|
- g_free(s->offsets);
|
||||||
|
- }
|
||||||
|
+ g_free(s->offsets);
|
||||||
|
g_free(s->compressed_block);
|
||||||
|
g_free(s->uncompressed_block);
|
||||||
|
inflateEnd(&s->zstream);
|
133
0122-bochs-Unify-header-structs-and-make-them-QEMU_PACKED.patch
Normal file
133
0122-bochs-Unify-header-structs-and-make-them-QEMU_PACKED.patch
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:31 +0100
|
||||||
|
Subject: [PATCH] bochs: Unify header structs and make them QEMU_PACKED
|
||||||
|
|
||||||
|
This is an on-disk structure, so offsets must be accurate.
|
||||||
|
|
||||||
|
Before this patch, sizeof(bochs) != sizeof(header_v1), which makes the
|
||||||
|
memcpy() between both invalid. We're lucky enough that the destination
|
||||||
|
buffer happened to be the larger one, and the memcpy size to be taken
|
||||||
|
from the smaller one, so we didn't get a buffer overflow in practice.
|
||||||
|
|
||||||
|
This patch unifies the both structures, eliminating the need to do a
|
||||||
|
memcpy in the first place. The common fields are extracted to the top
|
||||||
|
level of the struct and the actually differing part gets a union of the
|
||||||
|
two versions.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 3dd8a6763bcc50dfc3de8da9279b741c0dea9fb1)
|
||||||
|
---
|
||||||
|
block/bochs.c | 67 ++++++++++++++++++++++-------------------------------------
|
||||||
|
1 file changed, 25 insertions(+), 42 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/bochs.c b/block/bochs.c
|
||||||
|
index d7078c0..d550ce1 100644
|
||||||
|
--- a/block/bochs.c
|
||||||
|
+++ b/block/bochs.c
|
||||||
|
@@ -39,45 +39,30 @@
|
||||||
|
// not allocated: 0xffffffff
|
||||||
|
|
||||||
|
// always little-endian
|
||||||
|
-struct bochs_header_v1 {
|
||||||
|
- char magic[32]; // "Bochs Virtual HD Image"
|
||||||
|
- char type[16]; // "Redolog"
|
||||||
|
- char subtype[16]; // "Undoable" / "Volatile" / "Growing"
|
||||||
|
- uint32_t version;
|
||||||
|
- uint32_t header; // size of header
|
||||||
|
-
|
||||||
|
- union {
|
||||||
|
- struct {
|
||||||
|
- uint32_t catalog; // num of entries
|
||||||
|
- uint32_t bitmap; // bitmap size
|
||||||
|
- uint32_t extent; // extent size
|
||||||
|
- uint64_t disk; // disk size
|
||||||
|
- char padding[HEADER_SIZE - 64 - 8 - 20];
|
||||||
|
- } redolog;
|
||||||
|
- char padding[HEADER_SIZE - 64 - 8];
|
||||||
|
- } extra;
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-// always little-endian
|
||||||
|
struct bochs_header {
|
||||||
|
- char magic[32]; // "Bochs Virtual HD Image"
|
||||||
|
- char type[16]; // "Redolog"
|
||||||
|
- char subtype[16]; // "Undoable" / "Volatile" / "Growing"
|
||||||
|
+ char magic[32]; /* "Bochs Virtual HD Image" */
|
||||||
|
+ char type[16]; /* "Redolog" */
|
||||||
|
+ char subtype[16]; /* "Undoable" / "Volatile" / "Growing" */
|
||||||
|
uint32_t version;
|
||||||
|
- uint32_t header; // size of header
|
||||||
|
+ uint32_t header; /* size of header */
|
||||||
|
+
|
||||||
|
+ uint32_t catalog; /* num of entries */
|
||||||
|
+ uint32_t bitmap; /* bitmap size */
|
||||||
|
+ uint32_t extent; /* extent size */
|
||||||
|
|
||||||
|
union {
|
||||||
|
- struct {
|
||||||
|
- uint32_t catalog; // num of entries
|
||||||
|
- uint32_t bitmap; // bitmap size
|
||||||
|
- uint32_t extent; // extent size
|
||||||
|
- uint32_t reserved; // for ???
|
||||||
|
- uint64_t disk; // disk size
|
||||||
|
- char padding[HEADER_SIZE - 64 - 8 - 24];
|
||||||
|
- } redolog;
|
||||||
|
- char padding[HEADER_SIZE - 64 - 8];
|
||||||
|
+ struct {
|
||||||
|
+ uint32_t reserved; /* for ??? */
|
||||||
|
+ uint64_t disk; /* disk size */
|
||||||
|
+ char padding[HEADER_SIZE - 64 - 20 - 12];
|
||||||
|
+ } QEMU_PACKED redolog;
|
||||||
|
+ struct {
|
||||||
|
+ uint64_t disk; /* disk size */
|
||||||
|
+ char padding[HEADER_SIZE - 64 - 20 - 8];
|
||||||
|
+ } QEMU_PACKED redolog_v1;
|
||||||
|
+ char padding[HEADER_SIZE - 64 - 20];
|
||||||
|
} extra;
|
||||||
|
-};
|
||||||
|
+} QEMU_PACKED;
|
||||||
|
|
||||||
|
typedef struct BDRVBochsState {
|
||||||
|
CoMutex lock;
|
||||||
|
@@ -113,7 +98,6 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
BDRVBochsState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
struct bochs_header bochs;
|
||||||
|
- struct bochs_header_v1 header_v1;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
bs->read_only = 1; // no write support yet
|
||||||
|
@@ -132,13 +116,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (le32_to_cpu(bochs.version) == HEADER_V1) {
|
||||||
|
- memcpy(&header_v1, &bochs, sizeof(bochs));
|
||||||
|
- bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512;
|
||||||
|
+ bs->total_sectors = le64_to_cpu(bochs.extra.redolog_v1.disk) / 512;
|
||||||
|
} else {
|
||||||
|
- bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
|
||||||
|
+ bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
- s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog);
|
||||||
|
+ s->catalog_size = le32_to_cpu(bochs.catalog);
|
||||||
|
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||||
|
@@ -152,10 +135,10 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
|
||||||
|
s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4);
|
||||||
|
|
||||||
|
- s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512;
|
||||||
|
- s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512;
|
||||||
|
+ s->bitmap_blocks = 1 + (le32_to_cpu(bochs.bitmap) - 1) / 512;
|
||||||
|
+ s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512;
|
||||||
|
|
||||||
|
- s->extent_size = le32_to_cpu(bochs.extra.redolog.extent);
|
||||||
|
+ s->extent_size = le32_to_cpu(bochs.extent);
|
||||||
|
|
||||||
|
qemu_co_mutex_init(&s->lock);
|
||||||
|
return 0;
|
@ -0,0 +1,64 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:32 +0100
|
||||||
|
Subject: [PATCH] bochs: Use unsigned variables for offsets and sizes
|
||||||
|
(CVE-2014-0147)
|
||||||
|
|
||||||
|
Gets us rid of integer overflows resulting in negative sizes which
|
||||||
|
aren't correctly checked.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 246f65838d19db6db55bfb41117c35645a2c4789)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/078
|
||||||
|
tests/qemu-iotests/078.out
|
||||||
|
---
|
||||||
|
block/bochs.c | 16 ++++++++--------
|
||||||
|
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/bochs.c b/block/bochs.c
|
||||||
|
index d550ce1..750cec0 100644
|
||||||
|
--- a/block/bochs.c
|
||||||
|
+++ b/block/bochs.c
|
||||||
|
@@ -67,13 +67,13 @@ struct bochs_header {
|
||||||
|
typedef struct BDRVBochsState {
|
||||||
|
CoMutex lock;
|
||||||
|
uint32_t *catalog_bitmap;
|
||||||
|
- int catalog_size;
|
||||||
|
+ uint32_t catalog_size;
|
||||||
|
|
||||||
|
- int data_offset;
|
||||||
|
+ uint32_t data_offset;
|
||||||
|
|
||||||
|
- int bitmap_blocks;
|
||||||
|
- int extent_blocks;
|
||||||
|
- int extent_size;
|
||||||
|
+ uint32_t bitmap_blocks;
|
||||||
|
+ uint32_t extent_blocks;
|
||||||
|
+ uint32_t extent_size;
|
||||||
|
} BDRVBochsState;
|
||||||
|
|
||||||
|
static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
|
@@ -96,7 +96,7 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
|
static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
{
|
||||||
|
BDRVBochsState *s = bs->opaque;
|
||||||
|
- int i;
|
||||||
|
+ uint32_t i;
|
||||||
|
struct bochs_header bochs;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
@@ -151,8 +151,8 @@ fail:
|
||||||
|
static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||||
|
{
|
||||||
|
BDRVBochsState *s = bs->opaque;
|
||||||
|
- int64_t offset = sector_num * 512;
|
||||||
|
- int64_t extent_index, extent_offset, bitmap_offset;
|
||||||
|
+ uint64_t offset = sector_num * 512;
|
||||||
|
+ uint64_t extent_index, extent_offset, bitmap_offset;
|
||||||
|
char bitmap_entry;
|
||||||
|
|
||||||
|
// seek to sector
|
@ -0,0 +1,53 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:33 +0100
|
||||||
|
Subject: [PATCH] bochs: Check catalog_size header field (CVE-2014-0143)
|
||||||
|
|
||||||
|
It should neither become negative nor allow unbounded memory
|
||||||
|
allocations. This fixes aborts in g_malloc() and an s->catalog_bitmap
|
||||||
|
buffer overflow on big endian hosts.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit e3737b820b45e54b059656dc3f914f895ac7a88b)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/078
|
||||||
|
tests/qemu-iotests/078.out
|
||||||
|
---
|
||||||
|
block/bochs.c | 13 +++++++++++++
|
||||||
|
1 file changed, 13 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/bochs.c b/block/bochs.c
|
||||||
|
index 750cec0..4393ecc 100644
|
||||||
|
--- a/block/bochs.c
|
||||||
|
+++ b/block/bochs.c
|
||||||
|
@@ -121,7 +121,14 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Limit to 1M entries to avoid unbounded allocation. This is what is
|
||||||
|
+ * needed for the largest image that bximage can create (~8 TB). */
|
||||||
|
s->catalog_size = le32_to_cpu(bochs.catalog);
|
||||||
|
+ if (s->catalog_size > 0x100000) {
|
||||||
|
+ fprintf(stderr, "Catalog size is too large");
|
||||||
|
+ return -EFBIG;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap,
|
||||||
|
@@ -140,6 +147,12 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
|
||||||
|
s->extent_size = le32_to_cpu(bochs.extent);
|
||||||
|
|
||||||
|
+ if (s->catalog_size < bs->total_sectors / s->extent_size) {
|
||||||
|
+ fprintf(stderr, "Catalog size is too small for this disk size");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
qemu_co_mutex_init(&s->lock);
|
||||||
|
return 0;
|
||||||
|
|
@ -0,0 +1,39 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:34 +0100
|
||||||
|
Subject: [PATCH] bochs: Check extent_size header field (CVE-2014-0142)
|
||||||
|
|
||||||
|
This fixes two possible division by zero crashes: In bochs_open() and in
|
||||||
|
seek_to_sector().
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 8e53abbc20d08ae3ec30c2054e1161314ad9501d)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/078
|
||||||
|
tests/qemu-iotests/078.out
|
||||||
|
---
|
||||||
|
block/bochs.c | 8 ++++++++
|
||||||
|
1 file changed, 8 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/bochs.c b/block/bochs.c
|
||||||
|
index 4393ecc..10fbd39 100644
|
||||||
|
--- a/block/bochs.c
|
||||||
|
+++ b/block/bochs.c
|
||||||
|
@@ -146,6 +146,14 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512;
|
||||||
|
|
||||||
|
s->extent_size = le32_to_cpu(bochs.extent);
|
||||||
|
+ if (s->extent_size == 0) {
|
||||||
|
+ fprintf(stderr, "Extent size may not be zero");
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ } else if (s->extent_size > 0x800000) {
|
||||||
|
+ fprintf(stderr, "Extent size %" PRIu32 " is too large",
|
||||||
|
+ s->extent_size);
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
if (s->catalog_size < bs->total_sectors / s->extent_size) {
|
||||||
|
fprintf(stderr, "Catalog size is too small for this disk size");
|
31
0126-bochs-Fix-bitmap-offset-calculation.patch
Normal file
31
0126-bochs-Fix-bitmap-offset-calculation.patch
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:35 +0100
|
||||||
|
Subject: [PATCH] bochs: Fix bitmap offset calculation
|
||||||
|
|
||||||
|
32 bit truncation could let us access the wrong offset in the image.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit a9ba36a45dfac645a810c31ce15ab393b69d820a)
|
||||||
|
---
|
||||||
|
block/bochs.c | 5 +++--
|
||||||
|
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/bochs.c b/block/bochs.c
|
||||||
|
index 10fbd39..6749a61 100644
|
||||||
|
--- a/block/bochs.c
|
||||||
|
+++ b/block/bochs.c
|
||||||
|
@@ -184,8 +184,9 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num)
|
||||||
|
return -1; /* not allocated */
|
||||||
|
}
|
||||||
|
|
||||||
|
- bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] *
|
||||||
|
- (s->extent_blocks + s->bitmap_blocks));
|
||||||
|
+ bitmap_offset = s->data_offset +
|
||||||
|
+ (512 * (uint64_t) s->catalog_bitmap[extent_index] *
|
||||||
|
+ (s->extent_blocks + s->bitmap_blocks));
|
||||||
|
|
||||||
|
/* read in bitmap for current extent */
|
||||||
|
if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8),
|
@ -0,0 +1,96 @@
|
|||||||
|
From: Jeff Cody <jcody@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:36 +0100
|
||||||
|
Subject: [PATCH] vpc/vhd: add bounds check for max_table_entries and
|
||||||
|
block_size (CVE-2014-0144)
|
||||||
|
|
||||||
|
This adds checks to make sure that max_table_entries and block_size
|
||||||
|
are in sane ranges. Memory is allocated based on max_table_entries,
|
||||||
|
and block_size is used to calculate indices into that allocated
|
||||||
|
memory, so if these values are incorrect that can lead to potential
|
||||||
|
unbounded memory allocation, or invalid memory accesses.
|
||||||
|
|
||||||
|
Also, the allocation of the pagetable is changed from g_malloc0()
|
||||||
|
to qemu_blockalign().
|
||||||
|
|
||||||
|
Signed-off-by: Jeff Cody <jcody@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 97f1c45c6f456572e5b504b8614e4a69e23b8e3a)
|
||||||
|
---
|
||||||
|
block/vpc.c | 27 +++++++++++++++++++++++----
|
||||||
|
1 file changed, 23 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/vpc.c b/block/vpc.c
|
||||||
|
index fe4f311..16c5acf 100644
|
||||||
|
--- a/block/vpc.c
|
||||||
|
+++ b/block/vpc.c
|
||||||
|
@@ -45,6 +45,8 @@ enum vhd_type {
|
||||||
|
// Seconds since Jan 1, 2000 0:00:00 (UTC)
|
||||||
|
#define VHD_TIMESTAMP_BASE 946684800
|
||||||
|
|
||||||
|
+#define VHD_MAX_SECTORS (65535LL * 255 * 255)
|
||||||
|
+
|
||||||
|
// always big-endian
|
||||||
|
struct vhd_footer {
|
||||||
|
char creator[8]; // "conectix"
|
||||||
|
@@ -163,6 +165,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
struct vhd_dyndisk_header* dyndisk_header;
|
||||||
|
uint8_t buf[HEADER_SIZE];
|
||||||
|
uint32_t checksum;
|
||||||
|
+ uint64_t computed_size;
|
||||||
|
int disk_type = VHD_DYNAMIC;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
@@ -211,7 +214,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
be16_to_cpu(footer->cyls) * footer->heads * footer->secs_per_cyl;
|
||||||
|
|
||||||
|
/* Allow a maximum disk size of approximately 2 TB */
|
||||||
|
- if (bs->total_sectors >= 65535LL * 255 * 255) {
|
||||||
|
+ if (bs->total_sectors >= VHD_MAX_SECTORS) {
|
||||||
|
ret = -EFBIG;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
@@ -234,7 +237,23 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
|
||||||
|
|
||||||
|
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
|
||||||
|
- s->pagetable = g_malloc(s->max_table_entries * 4);
|
||||||
|
+
|
||||||
|
+ if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) {
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) {
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ computed_size = (uint64_t) s->max_table_entries * s->block_size;
|
||||||
|
+ if (computed_size < bs->total_sectors * 512) {
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4);
|
||||||
|
|
||||||
|
s->bat_offset = be64_to_cpu(dyndisk_header->table_offset);
|
||||||
|
|
||||||
|
@@ -280,7 +299,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
- g_free(s->pagetable);
|
||||||
|
+ qemu_vfree(s->pagetable);
|
||||||
|
#ifdef CACHE
|
||||||
|
g_free(s->pageentry_u8);
|
||||||
|
#endif
|
||||||
|
@@ -801,7 +820,7 @@ static int vpc_has_zero_init(BlockDriverState *bs)
|
||||||
|
static void vpc_close(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVVPCState *s = bs->opaque;
|
||||||
|
- g_free(s->pagetable);
|
||||||
|
+ qemu_vfree(s->pagetable);
|
||||||
|
#ifdef CACHE
|
||||||
|
g_free(s->pageentry_u8);
|
||||||
|
#endif
|
33
0128-vpc-Validate-block-size-CVE-2014-0142.patch
Normal file
33
0128-vpc-Validate-block-size-CVE-2014-0142.patch
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:37 +0100
|
||||||
|
Subject: [PATCH] vpc: Validate block size (CVE-2014-0142)
|
||||||
|
|
||||||
|
This fixes some cases of division by zero crashes.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 5e71dfad763d67bb64be79e20e93411c0c30ad25)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/group
|
||||||
|
---
|
||||||
|
block/vpc.c | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/vpc.c b/block/vpc.c
|
||||||
|
index 16c5acf..a41a0ab 100644
|
||||||
|
--- a/block/vpc.c
|
||||||
|
+++ b/block/vpc.c
|
||||||
|
@@ -234,6 +234,11 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
s->block_size = be32_to_cpu(dyndisk_header->block_size);
|
||||||
|
+ if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) {
|
||||||
|
+ fprintf(stderr, "Invalid block size %" PRIu32, s->block_size);
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511;
|
||||||
|
|
||||||
|
s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries);
|
110
0129-vdi-add-bounds-checks-for-blocks_in_image-and-disk_s.patch
Normal file
110
0129-vdi-add-bounds-checks-for-blocks_in_image-and-disk_s.patch
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
From: Jeff Cody <jcody@redhat.com>
|
||||||
|
Date: Fri, 28 Mar 2014 11:42:24 -0400
|
||||||
|
Subject: [PATCH] vdi: add bounds checks for blocks_in_image and disk_size
|
||||||
|
header fields (CVE-2014-0144)
|
||||||
|
|
||||||
|
The maximum blocks_in_image is 0xffffffff / 4, which also limits the
|
||||||
|
maximum disk_size for a VDI image to 1024TB. Note that this is the maximum
|
||||||
|
size that QEMU will currently support with this driver, not necessarily the
|
||||||
|
maximum size allowed by the image format.
|
||||||
|
|
||||||
|
This also fixes an incorrect error message, a bug introduced by commit
|
||||||
|
5b7aa9b56d1bfc79916262f380c3fc7961becb50 (Reported by Stefan Weil)
|
||||||
|
|
||||||
|
Signed-off-by: Jeff Cody <jcody@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 63fa06dc978f3669dbfd9443b33cde9e2a7f4b41)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/vdi.c
|
||||||
|
---
|
||||||
|
block/vdi.c | 35 +++++++++++++++++++++++++++++++++--
|
||||||
|
1 file changed, 33 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/vdi.c b/block/vdi.c
|
||||||
|
index 8a91525..63d509e 100644
|
||||||
|
--- a/block/vdi.c
|
||||||
|
+++ b/block/vdi.c
|
||||||
|
@@ -120,6 +120,11 @@ typedef unsigned char uuid_t[16];
|
||||||
|
|
||||||
|
#define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED)
|
||||||
|
|
||||||
|
+/* max blocks in image is (0xffffffff / 4) */
|
||||||
|
+#define VDI_BLOCKS_IN_IMAGE_MAX 0x3fffffff
|
||||||
|
+#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
|
||||||
|
+ (uint64_t)DEFAULT_CLUSTER_SIZE)
|
||||||
|
+
|
||||||
|
#if !defined(CONFIG_UUID)
|
||||||
|
static inline void uuid_generate(uuid_t out)
|
||||||
|
{
|
||||||
|
@@ -383,6 +388,14 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
vdi_header_print(&header);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
+ if (header.disk_size > VDI_DISK_SIZE_MAX) {
|
||||||
|
+ fprintf(stderr, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||||
|
+ ", max supported is 0x%" PRIx64 ")",
|
||||||
|
+ header.disk_size, VDI_DISK_SIZE_MAX);
|
||||||
|
+ ret = -ENOTSUP;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (header.disk_size % SECTOR_SIZE != 0) {
|
||||||
|
/* 'VBoxManage convertfromraw' can create images with odd disk sizes.
|
||||||
|
We accept them but round the disk size to the next multiple of
|
||||||
|
@@ -415,8 +428,10 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
logout("unsupported sector size %u B\n", header.sector_size);
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto fail;
|
||||||
|
- } else if (header.block_size != 1 * MiB) {
|
||||||
|
logout("unsupported block size %u B\n", header.block_size);
|
||||||
|
+ } else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
|
||||||
|
+ logout("unsupported VDI image (block size %u is not %u)",
|
||||||
|
+ header.block_size, DEFAULT_CLUSTER_SIZE);
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto fail;
|
||||||
|
} else if (header.disk_size >
|
||||||
|
@@ -432,6 +447,12 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
logout("parent uuid != 0, unsupported\n");
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto fail;
|
||||||
|
+ } else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) {
|
||||||
|
+ fprintf(stderr, "unsupported VDI image "
|
||||||
|
+ "(too many blocks %u, max is %u)",
|
||||||
|
+ header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX);
|
||||||
|
+ ret = -ENOTSUP;
|
||||||
|
+ goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
bs->total_sectors = header.disk_size / SECTOR_SIZE;
|
||||||
|
@@ -668,11 +689,20 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
|
||||||
|
options++;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (bytes > VDI_DISK_SIZE_MAX) {
|
||||||
|
+ result = -ENOTSUP;
|
||||||
|
+ fprintf(stderr, "Unsupported VDI image size (size is 0x%" PRIx64
|
||||||
|
+ ", max supported is 0x%" PRIx64 ")",
|
||||||
|
+ bytes, VDI_DISK_SIZE_MAX);
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
fd = qemu_open(filename,
|
||||||
|
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE,
|
||||||
|
0644);
|
||||||
|
if (fd < 0) {
|
||||||
|
- return -errno;
|
||||||
|
+ result = -errno;
|
||||||
|
+ goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We need enough blocks to store the given disk size,
|
||||||
|
@@ -733,6 +763,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options)
|
||||||
|
result = -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
+exit:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
|||||||
|
From: Jeff Cody <jcody@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:39 +0100
|
||||||
|
Subject: [PATCH] vhdx: Bounds checking for block_size and logical_sector_size
|
||||||
|
(CVE-2014-0148)
|
||||||
|
|
||||||
|
Other variables (e.g. sectors_per_block) are calculated using these
|
||||||
|
variables, and if not range-checked illegal values could be obtained
|
||||||
|
causing infinite loops and other potential issues when calculating
|
||||||
|
BAT entries.
|
||||||
|
|
||||||
|
The 1.00 VHDX spec requires BlockSize to be min 1MB, max 256MB.
|
||||||
|
LogicalSectorSize is required to be either 512 or 4096 bytes.
|
||||||
|
|
||||||
|
Reported-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Signed-off-by: Jeff Cody <jcody@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 1d7678dec4761acdc43439da6ceda41a703ba1a6)
|
||||||
|
---
|
||||||
|
block/vhdx.c | 12 ++++++++++--
|
||||||
|
block/vhdx.h | 4 ++++
|
||||||
|
2 files changed, 14 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/vhdx.c b/block/vhdx.c
|
||||||
|
index e9704b1..36fc06c 100644
|
||||||
|
--- a/block/vhdx.c
|
||||||
|
+++ b/block/vhdx.c
|
||||||
|
@@ -627,12 +627,20 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
|
||||||
|
le32_to_cpus(&s->logical_sector_size);
|
||||||
|
le32_to_cpus(&s->physical_sector_size);
|
||||||
|
|
||||||
|
- if (s->logical_sector_size == 0 || s->params.block_size == 0) {
|
||||||
|
+ if (s->params.block_size < VHDX_BLOCK_SIZE_MIN ||
|
||||||
|
+ s->params.block_size > VHDX_BLOCK_SIZE_MAX) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* both block_size and sector_size are guaranteed powers of 2 */
|
||||||
|
+ /* only 2 supported sector sizes */
|
||||||
|
+ if (s->logical_sector_size != 512 && s->logical_sector_size != 4096) {
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Both block_size and sector_size are guaranteed powers of 2, below.
|
||||||
|
+ Due to range checks above, s->sectors_per_block can never be < 256 */
|
||||||
|
s->sectors_per_block = s->params.block_size / s->logical_sector_size;
|
||||||
|
s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) *
|
||||||
|
(uint64_t)s->logical_sector_size /
|
||||||
|
diff --git a/block/vhdx.h b/block/vhdx.h
|
||||||
|
index fb687ed..227ac99 100644
|
||||||
|
--- a/block/vhdx.h
|
||||||
|
+++ b/block/vhdx.h
|
||||||
|
@@ -280,6 +280,10 @@ typedef struct QEMU_PACKED VHDXPage83Data {
|
||||||
|
support page 0x83 */
|
||||||
|
} VHDXPage83Data;
|
||||||
|
|
||||||
|
+#define KiB (1 * 1024)
|
||||||
|
+#define MiB (KiB * 1024)
|
||||||
|
+#define VHDX_BLOCK_SIZE_MIN (1 * MiB)
|
||||||
|
+#define VHDX_BLOCK_SIZE_MAX (256 * MiB)
|
||||||
|
typedef struct QEMU_PACKED VHDXVirtualDiskLogicalSectorSize {
|
||||||
|
uint32_t logical_sector_size; /* virtual disk sector size (in bytes).
|
||||||
|
Can only be 512 or 4096 bytes */
|
@ -0,0 +1,36 @@
|
|||||||
|
From: Fam Zheng <famz@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:40 +0100
|
||||||
|
Subject: [PATCH] curl: check data size before memcpy to local buffer.
|
||||||
|
(CVE-2014-0144)
|
||||||
|
|
||||||
|
curl_read_cb is callback function for libcurl when data arrives. The
|
||||||
|
data size passed in here is not guaranteed to be within the range of
|
||||||
|
request we submitted, so we may overflow the guest IO buffer. Check the
|
||||||
|
real size we have before memcpy to buffer to avoid overflow.
|
||||||
|
|
||||||
|
Signed-off-by: Fam Zheng <famz@redhat.com>
|
||||||
|
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 6d4b9e55fc625514a38d27cff4b9933f617fa7dc)
|
||||||
|
---
|
||||||
|
block/curl.c | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/curl.c b/block/curl.c
|
||||||
|
index 82d39ff..14ae7e5 100644
|
||||||
|
--- a/block/curl.c
|
||||||
|
+++ b/block/curl.c
|
||||||
|
@@ -136,6 +136,11 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
|
||||||
|
if (!s || !s->orig_buf)
|
||||||
|
goto read_end;
|
||||||
|
|
||||||
|
+ if (s->buf_off >= s->buf_len) {
|
||||||
|
+ /* buffer full, read nothing */
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ realsize = MIN(realsize, s->buf_len - s->buf_off);
|
||||||
|
memcpy(s->orig_buf + s->buf_off, ptr, realsize);
|
||||||
|
s->buf_off += realsize;
|
||||||
|
|
83
0132-qcow2-Check-header_length-CVE-2014-0144.patch
Normal file
83
0132-qcow2-Check-header_length-CVE-2014-0144.patch
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:41 +0100
|
||||||
|
Subject: [PATCH] qcow2: Check header_length (CVE-2014-0144)
|
||||||
|
|
||||||
|
This fixes an unbounded allocation for s->unknown_header_fields.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 24342f2cae47d03911e346fe1e520b00dc2818e0)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/qcow2.c
|
||||||
|
tests/qemu-iotests/group
|
||||||
|
---
|
||||||
|
block/qcow2.c | 33 +++++++++++++++++++++++++--------
|
||||||
|
1 file changed, 25 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 44161b2..40867a1 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -355,6 +355,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
|
||||||
|
s->qcow_version = header.version;
|
||||||
|
|
||||||
|
+ /* Initialise cluster size */
|
||||||
|
+ if (header.cluster_bits < MIN_CLUSTER_BITS ||
|
||||||
|
+ header.cluster_bits > MAX_CLUSTER_BITS) {
|
||||||
|
+ fprintf(stderr, "Unsupported cluster size: 2^%i", header.cluster_bits);
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ s->cluster_bits = header.cluster_bits;
|
||||||
|
+ s->cluster_size = 1 << s->cluster_bits;
|
||||||
|
+ s->cluster_sectors = 1 << (s->cluster_bits - 9);
|
||||||
|
+
|
||||||
|
/* Initialise version 3 header fields */
|
||||||
|
if (header.version == 2) {
|
||||||
|
header.incompatible_features = 0;
|
||||||
|
@@ -368,6 +380,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
be64_to_cpus(&header.autoclear_features);
|
||||||
|
be32_to_cpus(&header.refcount_order);
|
||||||
|
be32_to_cpus(&header.header_length);
|
||||||
|
+
|
||||||
|
+ if (header.header_length < 104) {
|
||||||
|
+ fprintf(stderr, "qcow2 header too short");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (header.header_length > s->cluster_size) {
|
||||||
|
+ fprintf(stderr, "qcow2 header exceeds cluster size");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.header_length > sizeof(header)) {
|
||||||
|
@@ -410,11 +434,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (header.cluster_bits < MIN_CLUSTER_BITS ||
|
||||||
|
- header.cluster_bits > MAX_CLUSTER_BITS) {
|
||||||
|
- ret = -EINVAL;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
if (header.crypt_method > QCOW_CRYPT_AES) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
@@ -423,9 +442,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
if (s->crypt_method_header) {
|
||||||
|
bs->encrypted = 1;
|
||||||
|
}
|
||||||
|
- s->cluster_bits = header.cluster_bits;
|
||||||
|
- s->cluster_size = 1 << s->cluster_bits;
|
||||||
|
- s->cluster_sectors = 1 << (s->cluster_bits - 9);
|
||||||
|
+
|
||||||
|
s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
|
||||||
|
s->l2_size = 1 << s->l2_bits;
|
||||||
|
bs->total_sectors = header.size / 512;
|
38
0133-qcow2-Check-backing_file_offset-CVE-2014-0144.patch
Normal file
38
0133-qcow2-Check-backing_file_offset-CVE-2014-0144.patch
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:42 +0100
|
||||||
|
Subject: [PATCH] qcow2: Check backing_file_offset (CVE-2014-0144)
|
||||||
|
|
||||||
|
Header, header extension and the backing file name must all be stored in
|
||||||
|
the first cluster. Setting the backing file to a much higher value
|
||||||
|
allowed header extensions to become much bigger than we want them to be
|
||||||
|
(unbounded allocation).
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit a1b3955c9415b1e767c130a2f59fee6aa28e575b)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2.c | 6 ++++++
|
||||||
|
1 file changed, 6 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 40867a1..4392111 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -404,6 +404,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ if (header.backing_file_offset > s->cluster_size) {
|
||||||
|
+ fprintf(stderr, "Invalid backing file offset");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (header.backing_file_offset) {
|
||||||
|
ext_end = header.backing_file_offset;
|
||||||
|
} else {
|
61
0134-qcow2-Check-refcount-table-size-CVE-2014-0144.patch
Normal file
61
0134-qcow2-Check-refcount-table-size-CVE-2014-0144.patch
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:43 +0100
|
||||||
|
Subject: [PATCH] qcow2: Check refcount table size (CVE-2014-0144)
|
||||||
|
|
||||||
|
Limit the in-memory reference count table size to 8 MB, it's enough in
|
||||||
|
practice. This fixes an unbounded allocation as well as a buffer
|
||||||
|
overflow in qcow2_refcount_init().
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 5dab2faddc8eaa1fb1abdbe2f502001fc13a1b21)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 4 +++-
|
||||||
|
block/qcow2.c | 9 +++++++++
|
||||||
|
2 files changed, 12 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index 1244693..cd641c9 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -38,8 +38,10 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
|
int qcow2_refcount_init(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
- int ret, refcount_table_size2, i;
|
||||||
|
+ unsigned int refcount_table_size2, i;
|
||||||
|
+ int ret;
|
||||||
|
|
||||||
|
+ assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t));
|
||||||
|
refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t);
|
||||||
|
s->refcount_table = g_malloc(refcount_table_size2);
|
||||||
|
if (s->refcount_table_size > 0) {
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 4392111..884262b 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -455,10 +455,19 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->csize_shift = (62 - (s->cluster_bits - 8));
|
||||||
|
s->csize_mask = (1 << (s->cluster_bits - 8)) - 1;
|
||||||
|
s->cluster_offset_mask = (1LL << s->csize_shift) - 1;
|
||||||
|
+
|
||||||
|
s->refcount_table_offset = header.refcount_table_offset;
|
||||||
|
s->refcount_table_size =
|
||||||
|
header.refcount_table_clusters << (s->cluster_bits - 3);
|
||||||
|
|
||||||
|
+ if (header.refcount_table_clusters > (0x800000 >> s->cluster_bits)) {
|
||||||
|
+ /* 8 MB refcount table is enough for 2 PB images at 64k cluster size
|
||||||
|
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
+ fprintf(stderr, "Reference count table too large");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
s->snapshots_offset = header.snapshots_offset;
|
||||||
|
s->nb_snapshots = header.nb_snapshots;
|
||||||
|
|
74
0135-qcow2-Validate-refcount-table-offset.patch
Normal file
74
0135-qcow2-Validate-refcount-table-offset.patch
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:44 +0100
|
||||||
|
Subject: [PATCH] qcow2: Validate refcount table offset
|
||||||
|
|
||||||
|
The end of the refcount table must not exceed INT64_MAX so that integer
|
||||||
|
overflows are avoided.
|
||||||
|
|
||||||
|
Also check for misaligned refcount table. Such images are invalid and
|
||||||
|
probably the result of data corruption. Error out to avoid further
|
||||||
|
corruption.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 8c7de28305a514d7f879fdfc677ca11fbf60d2e9)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2.c | 33 +++++++++++++++++++++++++++++++++
|
||||||
|
1 file changed, 33 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 884262b..db35ffe 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -286,6 +286,32 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result,
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int validate_table_offset(BlockDriverState *bs, uint64_t offset,
|
||||||
|
+ uint64_t entries, size_t entry_len)
|
||||||
|
+{
|
||||||
|
+ BDRVQcowState *s = bs->opaque;
|
||||||
|
+ uint64_t size;
|
||||||
|
+
|
||||||
|
+ /* Use signed INT64_MAX as the maximum even for uint64_t header fields,
|
||||||
|
+ * because values will be passed to qemu functions taking int64_t. */
|
||||||
|
+ if (entries > INT64_MAX / entry_len) {
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ size = entries * entry_len;
|
||||||
|
+
|
||||||
|
+ if (INT64_MAX - size < offset) {
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Tables must be cluster aligned */
|
||||||
|
+ if (offset & (s->cluster_size - 1)) {
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static QemuOptsList qcow2_runtime_opts = {
|
||||||
|
.name = "qcow2",
|
||||||
|
.head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head),
|
||||||
|
@@ -468,6 +494,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ ret = validate_table_offset(bs, s->refcount_table_offset,
|
||||||
|
+ s->refcount_table_size, sizeof(uint64_t));
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ fprintf(stderr, "Invalid reference count table offset");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
s->snapshots_offset = header.snapshots_offset;
|
||||||
|
s->nb_snapshots = header.nb_snapshots;
|
||||||
|
|
148
0136-qcow2-Validate-snapshot-table-offset-size-CVE-2014-0.patch
Normal file
148
0136-qcow2-Validate-snapshot-table-offset-size-CVE-2014-0.patch
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:45 +0100
|
||||||
|
Subject: [PATCH] qcow2: Validate snapshot table offset/size (CVE-2014-0144)
|
||||||
|
|
||||||
|
This avoid unbounded memory allocation and fixes a potential buffer
|
||||||
|
overflow on 32 bit hosts.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit ce48f2f441ca98885267af6fd636a7cb804ee646)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2-snapshot.c | 29 ++++-------------------------
|
||||||
|
block/qcow2.c | 15 +++++++++++++++
|
||||||
|
block/qcow2.h | 29 ++++++++++++++++++++++++++++-
|
||||||
|
3 files changed, 47 insertions(+), 26 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
|
||||||
|
index ae33b45..eb80438 100644
|
||||||
|
--- a/block/qcow2-snapshot.c
|
||||||
|
+++ b/block/qcow2-snapshot.c
|
||||||
|
@@ -26,31 +26,6 @@
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "block/qcow2.h"
|
||||||
|
|
||||||
|
-typedef struct QEMU_PACKED QCowSnapshotHeader {
|
||||||
|
- /* header is 8 byte aligned */
|
||||||
|
- uint64_t l1_table_offset;
|
||||||
|
-
|
||||||
|
- uint32_t l1_size;
|
||||||
|
- uint16_t id_str_size;
|
||||||
|
- uint16_t name_size;
|
||||||
|
-
|
||||||
|
- uint32_t date_sec;
|
||||||
|
- uint32_t date_nsec;
|
||||||
|
-
|
||||||
|
- uint64_t vm_clock_nsec;
|
||||||
|
-
|
||||||
|
- uint32_t vm_state_size;
|
||||||
|
- uint32_t extra_data_size; /* for extension */
|
||||||
|
- /* extra data follows */
|
||||||
|
- /* id_str follows */
|
||||||
|
- /* name follows */
|
||||||
|
-} QCowSnapshotHeader;
|
||||||
|
-
|
||||||
|
-typedef struct QEMU_PACKED QCowSnapshotExtraData {
|
||||||
|
- uint64_t vm_state_size_large;
|
||||||
|
- uint64_t disk_size;
|
||||||
|
-} QCowSnapshotExtraData;
|
||||||
|
-
|
||||||
|
void qcow2_free_snapshots(BlockDriverState *bs)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
@@ -326,6 +301,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info)
|
||||||
|
uint64_t *l1_table = NULL;
|
||||||
|
int64_t l1_table_offset;
|
||||||
|
|
||||||
|
+ if (s->nb_snapshots >= QCOW_MAX_SNAPSHOTS) {
|
||||||
|
+ return -EFBIG;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
memset(sn, 0, sizeof(*sn));
|
||||||
|
|
||||||
|
/* Generate an ID if it wasn't passed */
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index db35ffe..43e2db1 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -501,6 +501,21 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Snapshot table offset/length */
|
||||||
|
+ if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) {
|
||||||
|
+ fprintf(stderr, "Too many snapshots");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = validate_table_offset(bs, header.snapshots_offset,
|
||||||
|
+ header.nb_snapshots,
|
||||||
|
+ sizeof(QCowSnapshotHeader));
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ fprintf(stderr, "Invalid snapshot table offset");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
s->snapshots_offset = header.snapshots_offset;
|
||||||
|
s->nb_snapshots = header.nb_snapshots;
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index da61d18..1e1294c 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -38,6 +38,7 @@
|
||||||
|
#define QCOW_CRYPT_AES 1
|
||||||
|
|
||||||
|
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
||||||
|
+#define QCOW_MAX_SNAPSHOTS 65536
|
||||||
|
|
||||||
|
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||||
|
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||||
|
@@ -88,6 +89,32 @@ typedef struct QCowHeader {
|
||||||
|
uint32_t header_length;
|
||||||
|
} QCowHeader;
|
||||||
|
|
||||||
|
+typedef struct QEMU_PACKED QCowSnapshotHeader {
|
||||||
|
+ /* header is 8 byte aligned */
|
||||||
|
+ uint64_t l1_table_offset;
|
||||||
|
+
|
||||||
|
+ uint32_t l1_size;
|
||||||
|
+ uint16_t id_str_size;
|
||||||
|
+ uint16_t name_size;
|
||||||
|
+
|
||||||
|
+ uint32_t date_sec;
|
||||||
|
+ uint32_t date_nsec;
|
||||||
|
+
|
||||||
|
+ uint64_t vm_clock_nsec;
|
||||||
|
+
|
||||||
|
+ uint32_t vm_state_size;
|
||||||
|
+ uint32_t extra_data_size; /* for extension */
|
||||||
|
+ /* extra data follows */
|
||||||
|
+ /* id_str follows */
|
||||||
|
+ /* name follows */
|
||||||
|
+} QCowSnapshotHeader;
|
||||||
|
+
|
||||||
|
+typedef struct QEMU_PACKED QCowSnapshotExtraData {
|
||||||
|
+ uint64_t vm_state_size_large;
|
||||||
|
+ uint64_t disk_size;
|
||||||
|
+} QCowSnapshotExtraData;
|
||||||
|
+
|
||||||
|
+
|
||||||
|
typedef struct QCowSnapshot {
|
||||||
|
uint64_t l1_table_offset;
|
||||||
|
uint32_t l1_size;
|
||||||
|
@@ -190,7 +217,7 @@ typedef struct BDRVQcowState {
|
||||||
|
AES_KEY aes_decrypt_key;
|
||||||
|
uint64_t snapshots_offset;
|
||||||
|
int snapshots_size;
|
||||||
|
- int nb_snapshots;
|
||||||
|
+ unsigned int nb_snapshots;
|
||||||
|
QCowSnapshot *snapshots;
|
||||||
|
|
||||||
|
int flags;
|
@ -0,0 +1,54 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:46 +0100
|
||||||
|
Subject: [PATCH] qcow2: Validate active L1 table offset and size
|
||||||
|
(CVE-2014-0144)
|
||||||
|
|
||||||
|
This avoids an unbounded allocation.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 2d51c32c4b511db8bb9e58208f1e2c25e4c06c85)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2.c | 16 ++++++++++++++++
|
||||||
|
1 file changed, 16 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 43e2db1..8dd285b 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -520,6 +520,13 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->nb_snapshots = header.nb_snapshots;
|
||||||
|
|
||||||
|
/* read the level 1 table */
|
||||||
|
+ if (header.l1_size > 0x2000000) {
|
||||||
|
+ /* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
||||||
|
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
+ fprintf(stderr, "Active L1 table too large");
|
||||||
|
+ ret = -EFBIG;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
s->l1_size = header.l1_size;
|
||||||
|
|
||||||
|
l1_vm_state_index = size_to_l1(s, header.size);
|
||||||
|
@@ -535,7 +542,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ ret = validate_table_offset(bs, header.l1_table_offset,
|
||||||
|
+ header.l1_size, sizeof(uint64_t));
|
||||||
|
+ if (ret < 0) {
|
||||||
|
+ fprintf(stderr, "Invalid L1 table offset");
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
s->l1_table_offset = header.l1_table_offset;
|
||||||
|
+
|
||||||
|
+
|
||||||
|
if (s->l1_size > 0) {
|
||||||
|
s->l1_table = g_malloc0(
|
||||||
|
align_offset(s->l1_size * sizeof(uint64_t), 512));
|
50
0138-qcow2-Fix-backing-file-name-length-check.patch
Normal file
50
0138-qcow2-Fix-backing-file-name-length-check.patch
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:47 +0100
|
||||||
|
Subject: [PATCH] qcow2: Fix backing file name length check
|
||||||
|
|
||||||
|
len could become negative and would pass the check then. Nothing bad
|
||||||
|
happened because bdrv_pread() happens to return an error for negative
|
||||||
|
length values, but make variables for sizes unsigned anyway.
|
||||||
|
|
||||||
|
This patch also changes the behaviour to error out on invalid lengths
|
||||||
|
instead of silently truncating it to 1023.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 6d33e8e7dc9d40ea105feed4b39caa3e641569e8)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2.c | 9 ++++++---
|
||||||
|
1 file changed, 6 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 8dd285b..10bfaaf 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -344,7 +344,8 @@ static QemuOptsList qcow2_runtime_opts = {
|
||||||
|
static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
- int len, i, ret = 0;
|
||||||
|
+ unsigned int len, i;
|
||||||
|
+ int ret = 0;
|
||||||
|
QCowHeader header;
|
||||||
|
QemuOpts *opts;
|
||||||
|
Error *local_err = NULL;
|
||||||
|
@@ -593,8 +594,10 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
/* read the backing file name */
|
||||||
|
if (header.backing_file_offset != 0) {
|
||||||
|
len = header.backing_file_size;
|
||||||
|
- if (len > 1023) {
|
||||||
|
- len = 1023;
|
||||||
|
+ if (len > MIN(1023, s->cluster_size - header.backing_file_offset)) {
|
||||||
|
+ fprintf(stderr, "Backing file name too long");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
}
|
||||||
|
ret = bdrv_pread(bs->file, header.backing_file_offset,
|
||||||
|
bs->backing_file, len);
|
@ -0,0 +1,46 @@
|
|||||||
|
From: Hu Tao <hutao@cn.fujitsu.com>
|
||||||
|
Date: Sun, 26 Jan 2014 11:12:38 +0800
|
||||||
|
Subject: [PATCH] qcow2: fix offset overflow in qcow2_alloc_clusters_at()
|
||||||
|
|
||||||
|
When cluster size is big enough it can lead to an offset overflow
|
||||||
|
in qcow2_alloc_clusters_at(). This patch fixes it.
|
||||||
|
|
||||||
|
The allocation is stopped each time at L2 table boundary
|
||||||
|
(see handle_alloc()), so the possible maximum bytes could be
|
||||||
|
|
||||||
|
2^(cluster_bits - 3 + cluster_bits)
|
||||||
|
|
||||||
|
cluster_bits - 3 is used to compute the number of entry by L2
|
||||||
|
and the additional cluster_bits is to take into account each
|
||||||
|
clusters referenced by the L2 entries.
|
||||||
|
|
||||||
|
so int is safe for cluster_bits<=17, unsafe otherwise.
|
||||||
|
|
||||||
|
Signed-off-by: Hu Tao <hutao@cn.fujitsu.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Reviewed-by: Benoit Canet <benoit@irqsave.net>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
(cherry picked from commit 33304ec9fa484e765c6249673e09e1b7d49c5b85)
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 8 +++++++-
|
||||||
|
1 file changed, 7 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index cd641c9..1308151 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -676,7 +676,13 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
uint64_t cluster_index;
|
||||||
|
uint64_t old_free_cluster_index;
|
||||||
|
- int i, refcount, ret;
|
||||||
|
+ uint64_t i;
|
||||||
|
+ int refcount, ret;
|
||||||
|
+
|
||||||
|
+ assert(nb_clusters >= 0);
|
||||||
|
+ if (nb_clusters == 0) {
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
/* Check how many clusters there are free */
|
||||||
|
cluster_index = offset >> s->cluster_bits;
|
224
0140-qcow2-Don-t-rely-on-free_cluster_index-in-alloc_refc.patch
Normal file
224
0140-qcow2-Don-t-rely-on-free_cluster_index-in-alloc_refc.patch
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Fri, 28 Mar 2014 18:06:31 +0100
|
||||||
|
Subject: [PATCH] qcow2: Don't rely on free_cluster_index in
|
||||||
|
alloc_refcount_block() (CVE-2014-0147)
|
||||||
|
|
||||||
|
free_cluster_index is only correct if update_refcount() was called from
|
||||||
|
an allocation function, and even there it's brittle because it's used to
|
||||||
|
protect unfinished allocations which still have a refcount of 0 - if it
|
||||||
|
moves in the wrong place, the unfinished allocation can be corrupted.
|
||||||
|
|
||||||
|
So not using it any more seems to be a good idea. Instead, use the
|
||||||
|
first requested cluster to do the calculations. Return -EAGAIN if
|
||||||
|
unfinished allocations could become invalid and let the caller restart
|
||||||
|
its search for some free clusters.
|
||||||
|
|
||||||
|
The context of creating a snapsnot is one situation where
|
||||||
|
update_refcount() is called outside of a cluster allocation. For this
|
||||||
|
case, the change fixes a buffer overflow if a cluster is referenced in
|
||||||
|
an L2 table that cannot be represented by an existing refcount block.
|
||||||
|
(new_table[refcount_table_index] was out of bounds)
|
||||||
|
|
||||||
|
[Bump the qemu-iotests 026 refblock_alloc.write leak count from 10 to
|
||||||
|
11.
|
||||||
|
--Stefan]
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit b106ad9185f35fc4ad669555ad0e79e276083bd7)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/qcow2.c
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 72 ++++++++++++++++++++++++++------------------------
|
||||||
|
block/qcow2.c | 11 ++++----
|
||||||
|
2 files changed, 43 insertions(+), 40 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index 1308151..d784dd6 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -191,10 +191,11 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
|
* they can describe them themselves.
|
||||||
|
*
|
||||||
|
* - We need to consider that at this point we are inside update_refcounts
|
||||||
|
- * and doing the initial refcount increase. This means that some clusters
|
||||||
|
- * have already been allocated by the caller, but their refcount isn't
|
||||||
|
- * accurate yet. free_cluster_index tells us where this allocation ends
|
||||||
|
- * as long as we don't overwrite it by freeing clusters.
|
||||||
|
+ * and potentially doing an initial refcount increase. This means that
|
||||||
|
+ * some clusters have already been allocated by the caller, but their
|
||||||
|
+ * refcount isn't accurate yet. If we allocate clusters for metadata, we
|
||||||
|
+ * need to return -EAGAIN to signal the caller that it needs to restart
|
||||||
|
+ * the search for free clusters.
|
||||||
|
*
|
||||||
|
* - alloc_clusters_noref and qcow2_free_clusters may load a different
|
||||||
|
* refcount block into the cache
|
||||||
|
@@ -279,7 +280,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
|
}
|
||||||
|
|
||||||
|
s->refcount_table[refcount_table_index] = new_block;
|
||||||
|
- return 0;
|
||||||
|
+
|
||||||
|
+ /* The new refcount block may be where the caller intended to put its
|
||||||
|
+ * data, so let it restart the search. */
|
||||||
|
+ return -EAGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block);
|
||||||
|
@@ -302,8 +306,7 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
|
|
||||||
|
/* Calculate the number of refcount blocks needed so far */
|
||||||
|
uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT);
|
||||||
|
- uint64_t blocks_used = (s->free_cluster_index +
|
||||||
|
- refcount_block_clusters - 1) / refcount_block_clusters;
|
||||||
|
+ uint64_t blocks_used = DIV_ROUND_UP(cluster_index, refcount_block_clusters);
|
||||||
|
|
||||||
|
/* And now we need at least one block more for the new metadata */
|
||||||
|
uint64_t table_size = next_refcount_table_size(s, blocks_used + 1);
|
||||||
|
@@ -336,8 +339,6 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
|
uint16_t *new_blocks = g_malloc0(blocks_clusters * s->cluster_size);
|
||||||
|
uint64_t *new_table = g_malloc0(table_size * sizeof(uint64_t));
|
||||||
|
|
||||||
|
- assert(meta_offset >= (s->free_cluster_index * s->cluster_size));
|
||||||
|
-
|
||||||
|
/* Fill the new refcount table */
|
||||||
|
memcpy(new_table, s->refcount_table,
|
||||||
|
s->refcount_table_size * sizeof(uint64_t));
|
||||||
|
@@ -400,18 +401,19 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
|
s->refcount_table_size = table_size;
|
||||||
|
s->refcount_table_offset = table_offset;
|
||||||
|
|
||||||
|
- /* Free old table. Remember, we must not change free_cluster_index */
|
||||||
|
- uint64_t old_free_cluster_index = s->free_cluster_index;
|
||||||
|
+ /* Free old table. */
|
||||||
|
qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t),
|
||||||
|
QCOW2_DISCARD_OTHER);
|
||||||
|
- s->free_cluster_index = old_free_cluster_index;
|
||||||
|
|
||||||
|
ret = load_refcount_block(bs, new_block, (void**) refcount_block);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
- return 0;
|
||||||
|
+ /* If we were trying to do the initial refcount update for some cluster
|
||||||
|
+ * allocation, we might have used the same clusters to store newly
|
||||||
|
+ * allocated metadata. Make the caller search some new space. */
|
||||||
|
+ return -EAGAIN;
|
||||||
|
|
||||||
|
fail_table:
|
||||||
|
g_free(new_table);
|
||||||
|
@@ -657,12 +659,15 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
|
||||||
|
- offset = alloc_clusters_noref(bs, size);
|
||||||
|
- if (offset < 0) {
|
||||||
|
- return offset;
|
||||||
|
- }
|
||||||
|
+ do {
|
||||||
|
+ offset = alloc_clusters_noref(bs, size);
|
||||||
|
+ if (offset < 0) {
|
||||||
|
+ return offset;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER);
|
||||||
|
+ } while (ret == -EAGAIN);
|
||||||
|
|
||||||
|
- ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@@ -675,7 +680,6 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
uint64_t cluster_index;
|
||||||
|
- uint64_t old_free_cluster_index;
|
||||||
|
uint64_t i;
|
||||||
|
int refcount, ret;
|
||||||
|
|
||||||
|
@@ -684,30 +688,28 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Check how many clusters there are free */
|
||||||
|
- cluster_index = offset >> s->cluster_bits;
|
||||||
|
- for(i = 0; i < nb_clusters; i++) {
|
||||||
|
- refcount = get_refcount(bs, cluster_index++);
|
||||||
|
+ do {
|
||||||
|
+ /* Check how many clusters there are free */
|
||||||
|
+ cluster_index = offset >> s->cluster_bits;
|
||||||
|
+ for(i = 0; i < nb_clusters; i++) {
|
||||||
|
+ refcount = get_refcount(bs, cluster_index++);
|
||||||
|
|
||||||
|
- if (refcount < 0) {
|
||||||
|
- return refcount;
|
||||||
|
- } else if (refcount != 0) {
|
||||||
|
- break;
|
||||||
|
+ if (refcount < 0) {
|
||||||
|
+ return refcount;
|
||||||
|
+ } else if (refcount != 0) {
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
- }
|
||||||
|
|
||||||
|
- /* And then allocate them */
|
||||||
|
- old_free_cluster_index = s->free_cluster_index;
|
||||||
|
- s->free_cluster_index = cluster_index + i;
|
||||||
|
+ /* And then allocate them */
|
||||||
|
+ ret = update_refcount(bs, offset, i << s->cluster_bits, 1,
|
||||||
|
+ QCOW2_DISCARD_NEVER);
|
||||||
|
+ } while (ret == -EAGAIN);
|
||||||
|
|
||||||
|
- ret = update_refcount(bs, offset, i << s->cluster_bits, 1,
|
||||||
|
- QCOW2_DISCARD_NEVER);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
- s->free_cluster_index = old_free_cluster_index;
|
||||||
|
-
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 10bfaaf..5d45036 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -1385,7 +1385,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
|
*/
|
||||||
|
BlockDriverState* bs;
|
||||||
|
QCowHeader *header;
|
||||||
|
- uint8_t* refcount_table;
|
||||||
|
+ uint64_t* refcount_table;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bdrv_create_file(filename, options);
|
||||||
|
@@ -1431,9 +1431,10 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Write an empty refcount table */
|
||||||
|
- refcount_table = g_malloc0(cluster_size);
|
||||||
|
- ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size);
|
||||||
|
+ /* Write a refcount table with one refcount block */
|
||||||
|
+ refcount_table = g_malloc0(2 * cluster_size);
|
||||||
|
+ refcount_table[0] = cpu_to_be64(2 * cluster_size);
|
||||||
|
+ ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size);
|
||||||
|
g_free(refcount_table);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
@@ -1455,7 +1456,7 @@ static int qcow2_create2(const char *filename, int64_t total_size,
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
- ret = qcow2_alloc_clusters(bs, 2 * cluster_size);
|
||||||
|
+ ret = qcow2_alloc_clusters(bs, 3 * cluster_size);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out;
|
||||||
|
|
@ -0,0 +1,28 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:49 +0100
|
||||||
|
Subject: [PATCH] qcow2: Avoid integer overflow in get_refcount (CVE-2014-0143)
|
||||||
|
|
||||||
|
This ensures that the checks catch all invalid cluster indexes
|
||||||
|
instead of returning the refcount of a wrong cluster.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit db8a31d11d6a60f48d6817530640d75aa72a9a2f)
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index d784dd6..3e473bd 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -87,7 +87,7 @@ static int load_refcount_block(BlockDriverState *bs,
|
||||||
|
static int get_refcount(BlockDriverState *bs, int64_t cluster_index)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
- int refcount_table_index, block_index;
|
||||||
|
+ uint64_t refcount_table_index, block_index;
|
||||||
|
int64_t refcount_block_offset;
|
||||||
|
int ret;
|
||||||
|
uint16_t *refcount_block;
|
77
0142-qcow2-Check-new-refcount-table-size-on-growth.patch
Normal file
77
0142-qcow2-Check-new-refcount-table-size-on-growth.patch
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:50 +0100
|
||||||
|
Subject: [PATCH] qcow2: Check new refcount table size on growth
|
||||||
|
|
||||||
|
If the size becomes larger than what qcow2_open() would accept, fail the
|
||||||
|
growing operation.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 2b5d5953eec0cc541857c3df812bdf8421596ab2)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/qcow2.c
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 4 ++++
|
||||||
|
block/qcow2.c | 4 +---
|
||||||
|
block/qcow2.h | 9 +++++++++
|
||||||
|
3 files changed, 14 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index 3e473bd..3cdcfb6 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -308,6 +308,10 @@ static int alloc_refcount_block(BlockDriverState *bs,
|
||||||
|
uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT);
|
||||||
|
uint64_t blocks_used = DIV_ROUND_UP(cluster_index, refcount_block_clusters);
|
||||||
|
|
||||||
|
+ if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) {
|
||||||
|
+ return -EFBIG;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* And now we need at least one block more for the new metadata */
|
||||||
|
uint64_t table_size = next_refcount_table_size(s, blocks_used + 1);
|
||||||
|
uint64_t last_table_size;
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index 5d45036..af0a45c 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -487,9 +487,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->refcount_table_size =
|
||||||
|
header.refcount_table_clusters << (s->cluster_bits - 3);
|
||||||
|
|
||||||
|
- if (header.refcount_table_clusters > (0x800000 >> s->cluster_bits)) {
|
||||||
|
- /* 8 MB refcount table is enough for 2 PB images at 64k cluster size
|
||||||
|
- * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
+ if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) {
|
||||||
|
fprintf(stderr, "Reference count table too large");
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index 1e1294c..e802c55 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -40,6 +40,10 @@
|
||||||
|
#define QCOW_MAX_CRYPT_CLUSTERS 32
|
||||||
|
#define QCOW_MAX_SNAPSHOTS 65536
|
||||||
|
|
||||||
|
+/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
|
||||||
|
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
+#define QCOW_MAX_REFTABLE_SIZE 0x800000
|
||||||
|
+
|
||||||
|
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||||
|
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||||
|
/* indicate that the cluster is compressed (they never have the copied flag) */
|
||||||
|
@@ -356,6 +360,11 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s)
|
||||||
|
return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s)
|
||||||
|
+{
|
||||||
|
+ return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static inline int qcow2_get_cluster_type(uint64_t l2_entry)
|
||||||
|
{
|
||||||
|
if (l2_entry & QCOW_OFLAG_COMPRESSED) {
|
@ -0,0 +1,85 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:51 +0100
|
||||||
|
Subject: [PATCH] qcow2: Fix types in qcow2_alloc_clusters and
|
||||||
|
alloc_clusters_noref
|
||||||
|
|
||||||
|
In order to avoid integer overflows.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit bb572aefbdac290363bfa5ca0e810ccce0a14ed6)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/qcow2.h
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 11 ++++++-----
|
||||||
|
block/qcow2.h | 6 +++---
|
||||||
|
2 files changed, 9 insertions(+), 8 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index 3cdcfb6..d424c22 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -26,7 +26,7 @@
|
||||||
|
#include "block/block_int.h"
|
||||||
|
#include "block/qcow2.h"
|
||||||
|
|
||||||
|
-static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size);
|
||||||
|
+static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
|
||||||
|
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t length,
|
||||||
|
int addend, enum qcow2_discard_type type);
|
||||||
|
@@ -632,15 +632,16 @@ static int update_cluster_refcount(BlockDriverState *bs,
|
||||||
|
|
||||||
|
|
||||||
|
/* return < 0 if error */
|
||||||
|
-static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size)
|
||||||
|
+static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
- int i, nb_clusters, refcount;
|
||||||
|
+ uint64_t i, nb_clusters;
|
||||||
|
+ int refcount;
|
||||||
|
|
||||||
|
nb_clusters = size_to_clusters(s, size);
|
||||||
|
retry:
|
||||||
|
for(i = 0; i < nb_clusters; i++) {
|
||||||
|
- int64_t next_cluster_index = s->free_cluster_index++;
|
||||||
|
+ uint64_t next_cluster_index = s->free_cluster_index++;
|
||||||
|
refcount = get_refcount(bs, next_cluster_index);
|
||||||
|
|
||||||
|
if (refcount < 0) {
|
||||||
|
@@ -657,7 +658,7 @@ retry:
|
||||||
|
return (s->free_cluster_index - nb_clusters) << s->cluster_bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
-int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size)
|
||||||
|
+int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
|
||||||
|
{
|
||||||
|
int64_t offset;
|
||||||
|
int ret;
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index e802c55..baf62a0 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -210,8 +210,8 @@ typedef struct BDRVQcowState {
|
||||||
|
uint64_t *refcount_table;
|
||||||
|
uint64_t refcount_table_offset;
|
||||||
|
uint32_t refcount_table_size;
|
||||||
|
- int64_t free_cluster_index;
|
||||||
|
- int64_t free_byte_offset;
|
||||||
|
+ uint64_t free_cluster_index;
|
||||||
|
+ uint64_t free_byte_offset;
|
||||||
|
|
||||||
|
CoMutex lock;
|
||||||
|
|
||||||
|
@@ -408,7 +408,7 @@ int qcow2_update_header(BlockDriverState *bs);
|
||||||
|
int qcow2_refcount_init(BlockDriverState *bs);
|
||||||
|
void qcow2_refcount_close(BlockDriverState *bs);
|
||||||
|
|
||||||
|
-int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size);
|
||||||
|
+int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size);
|
||||||
|
int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset,
|
||||||
|
int nb_clusters);
|
||||||
|
int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size);
|
@ -0,0 +1,61 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:52 +0100
|
||||||
|
Subject: [PATCH] qcow2: Protect against some integer overflows in bdrv_check
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 0abe740f1de899737242bcba1fb4a9857f7a3087)
|
||||||
|
---
|
||||||
|
block/qcow2-refcount.c | 18 +++++++++---------
|
||||||
|
1 file changed, 9 insertions(+), 9 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
|
||||||
|
index d424c22..fc2d367 100644
|
||||||
|
--- a/block/qcow2-refcount.c
|
||||||
|
+++ b/block/qcow2-refcount.c
|
||||||
|
@@ -997,8 +997,7 @@ static void inc_refcounts(BlockDriverState *bs,
|
||||||
|
int64_t offset, int64_t size)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
- int64_t start, last, cluster_offset;
|
||||||
|
- int k;
|
||||||
|
+ uint64_t start, last, cluster_offset, k;
|
||||||
|
|
||||||
|
if (size <= 0)
|
||||||
|
return;
|
||||||
|
@@ -1008,11 +1007,7 @@ static void inc_refcounts(BlockDriverState *bs,
|
||||||
|
for(cluster_offset = start; cluster_offset <= last;
|
||||||
|
cluster_offset += s->cluster_size) {
|
||||||
|
k = cluster_offset >> s->cluster_bits;
|
||||||
|
- if (k < 0) {
|
||||||
|
- fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n",
|
||||||
|
- cluster_offset);
|
||||||
|
- res->corruptions++;
|
||||||
|
- } else if (k >= refcount_table_size) {
|
||||||
|
+ if (k >= refcount_table_size) {
|
||||||
|
fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after "
|
||||||
|
"the end of the image file, can't properly check refcounts.\n",
|
||||||
|
cluster_offset);
|
||||||
|
@@ -1253,14 +1248,19 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res,
|
||||||
|
BdrvCheckMode fix)
|
||||||
|
{
|
||||||
|
BDRVQcowState *s = bs->opaque;
|
||||||
|
- int64_t size, i, highest_cluster;
|
||||||
|
- int nb_clusters, refcount1, refcount2;
|
||||||
|
+ int64_t size, i, highest_cluster, nb_clusters;
|
||||||
|
+ int refcount1, refcount2;
|
||||||
|
QCowSnapshot *sn;
|
||||||
|
uint16_t *refcount_table;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
size = bdrv_getlength(bs->file);
|
||||||
|
nb_clusters = size_to_clusters(s, size);
|
||||||
|
+ if (nb_clusters > INT_MAX) {
|
||||||
|
+ res->check_errors++;
|
||||||
|
+ return -EFBIG;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t));
|
||||||
|
|
||||||
|
res->bfi.total_clusters =
|
28
0145-qcow2-Fix-new-L1-table-size-check-CVE-2014-0143.patch
Normal file
28
0145-qcow2-Fix-new-L1-table-size-check-CVE-2014-0143.patch
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:53 +0100
|
||||||
|
Subject: [PATCH] qcow2: Fix new L1 table size check (CVE-2014-0143)
|
||||||
|
|
||||||
|
The size in bytes is assigned to an int later, so check that instead of
|
||||||
|
the number of entries.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit cab60de930684c33f67d4e32c7509b567f8c445b)
|
||||||
|
---
|
||||||
|
block/qcow2-cluster.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
|
||||||
|
index 09abbf0..06e3e14 100644
|
||||||
|
--- a/block/qcow2-cluster.c
|
||||||
|
+++ b/block/qcow2-cluster.c
|
||||||
|
@@ -54,7 +54,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (new_l1_size > INT_MAX) {
|
||||||
|
+ if (new_l1_size > INT_MAX / sizeof(uint64_t)) {
|
||||||
|
return -EFBIG;
|
||||||
|
}
|
||||||
|
|
321
0146-dmg-coding-style-and-indentation-cleanup.patch
Normal file
321
0146-dmg-coding-style-and-indentation-cleanup.patch
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:54 +0100
|
||||||
|
Subject: [PATCH] dmg: coding style and indentation cleanup
|
||||||
|
|
||||||
|
Clean up the mix of tabs and spaces, as well as the coding style
|
||||||
|
violations in block/dmg.c. There are no semantic changes since this
|
||||||
|
patch simply reformats the code.
|
||||||
|
|
||||||
|
This patch is necessary before we can make meaningful changes to this
|
||||||
|
file, due to the inconsistent formatting and confusing indentation.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 2c1885adcf0312da80c7317b09f9adad97fa0fc6)
|
||||||
|
---
|
||||||
|
block/dmg.c | 224 ++++++++++++++++++++++++++++++++----------------------------
|
||||||
|
1 file changed, 120 insertions(+), 104 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index 3141cb5..e8b88dc 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -95,9 +95,9 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
|
||||||
|
static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
{
|
||||||
|
BDRVDMGState *s = bs->opaque;
|
||||||
|
- uint64_t info_begin,info_end,last_in_offset,last_out_offset;
|
||||||
|
+ uint64_t info_begin, info_end, last_in_offset, last_out_offset;
|
||||||
|
uint32_t count, tmp;
|
||||||
|
- uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i;
|
||||||
|
+ uint32_t max_compressed_size = 1, max_sectors_per_chunk = 1, i;
|
||||||
|
int64_t offset;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
@@ -159,37 +159,39 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (type == 0x6d697368 && count >= 244) {
|
||||||
|
- int new_size, chunk_count;
|
||||||
|
+ if (type == 0x6d697368 && count >= 244) {
|
||||||
|
+ int new_size, chunk_count;
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
offset += 200;
|
||||||
|
|
||||||
|
- chunk_count = (count-204)/40;
|
||||||
|
- new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
|
||||||
|
- s->types = g_realloc(s->types, new_size/2);
|
||||||
|
- s->offsets = g_realloc(s->offsets, new_size);
|
||||||
|
- s->lengths = g_realloc(s->lengths, new_size);
|
||||||
|
- s->sectors = g_realloc(s->sectors, new_size);
|
||||||
|
- s->sectorcounts = g_realloc(s->sectorcounts, new_size);
|
||||||
|
+ chunk_count = (count - 204) / 40;
|
||||||
|
+ new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count);
|
||||||
|
+ s->types = g_realloc(s->types, new_size / 2);
|
||||||
|
+ s->offsets = g_realloc(s->offsets, new_size);
|
||||||
|
+ s->lengths = g_realloc(s->lengths, new_size);
|
||||||
|
+ s->sectors = g_realloc(s->sectors, new_size);
|
||||||
|
+ s->sectorcounts = g_realloc(s->sectorcounts, new_size);
|
||||||
|
|
||||||
|
for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) {
|
||||||
|
ret = read_uint32(bs, offset, &s->types[i]);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
- offset += 4;
|
||||||
|
- if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) {
|
||||||
|
- if(s->types[i]==0xffffffff) {
|
||||||
|
- last_in_offset = s->offsets[i-1]+s->lengths[i-1];
|
||||||
|
- last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1];
|
||||||
|
- }
|
||||||
|
- chunk_count--;
|
||||||
|
- i--;
|
||||||
|
- offset += 36;
|
||||||
|
- continue;
|
||||||
|
- }
|
||||||
|
- offset += 4;
|
||||||
|
+ offset += 4;
|
||||||
|
+ if (s->types[i] != 0x80000005 && s->types[i] != 1 &&
|
||||||
|
+ s->types[i] != 2) {
|
||||||
|
+ if (s->types[i] == 0xffffffff) {
|
||||||
|
+ last_in_offset = s->offsets[i - 1] + s->lengths[i - 1];
|
||||||
|
+ last_out_offset = s->sectors[i - 1] +
|
||||||
|
+ s->sectorcounts[i - 1];
|
||||||
|
+ }
|
||||||
|
+ chunk_count--;
|
||||||
|
+ i--;
|
||||||
|
+ offset += 36;
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+ offset += 4;
|
||||||
|
|
||||||
|
ret = read_uint64(bs, offset, &s->sectors[i]);
|
||||||
|
if (ret < 0) {
|
||||||
|
@@ -217,19 +219,21 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
offset += 8;
|
||||||
|
|
||||||
|
- if(s->lengths[i]>max_compressed_size)
|
||||||
|
- max_compressed_size = s->lengths[i];
|
||||||
|
- if(s->sectorcounts[i]>max_sectors_per_chunk)
|
||||||
|
- max_sectors_per_chunk = s->sectorcounts[i];
|
||||||
|
- }
|
||||||
|
- s->n_chunks+=chunk_count;
|
||||||
|
- }
|
||||||
|
+ if (s->lengths[i] > max_compressed_size) {
|
||||||
|
+ max_compressed_size = s->lengths[i];
|
||||||
|
+ }
|
||||||
|
+ if (s->sectorcounts[i] > max_sectors_per_chunk) {
|
||||||
|
+ max_sectors_per_chunk = s->sectorcounts[i];
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ s->n_chunks += chunk_count;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* initialize zlib engine */
|
||||||
|
- s->compressed_chunk = g_malloc(max_compressed_size+1);
|
||||||
|
- s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk);
|
||||||
|
- if(inflateInit(&s->zstream) != Z_OK) {
|
||||||
|
+ s->compressed_chunk = g_malloc(max_compressed_size + 1);
|
||||||
|
+ s->uncompressed_chunk = g_malloc(512 * max_sectors_per_chunk);
|
||||||
|
+ if (inflateInit(&s->zstream) != Z_OK) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
@@ -251,27 +255,29 @@ fail:
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||||
|
- uint32_t chunk_num,int sector_num)
|
||||||
|
+ uint32_t chunk_num, int sector_num)
|
||||||
|
{
|
||||||
|
- if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num ||
|
||||||
|
- s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num)
|
||||||
|
- return 0;
|
||||||
|
- else
|
||||||
|
- return -1;
|
||||||
|
+ if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num ||
|
||||||
|
+ s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) {
|
||||||
|
+ return 0;
|
||||||
|
+ } else {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
-static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num)
|
||||||
|
+static inline uint32_t search_chunk(BDRVDMGState *s, int sector_num)
|
||||||
|
{
|
||||||
|
/* binary search */
|
||||||
|
- uint32_t chunk1=0,chunk2=s->n_chunks,chunk3;
|
||||||
|
- while(chunk1!=chunk2) {
|
||||||
|
- chunk3 = (chunk1+chunk2)/2;
|
||||||
|
- if(s->sectors[chunk3]>sector_num)
|
||||||
|
- chunk2 = chunk3;
|
||||||
|
- else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num)
|
||||||
|
- return chunk3;
|
||||||
|
- else
|
||||||
|
- chunk1 = chunk3;
|
||||||
|
+ uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
|
||||||
|
+ while (chunk1 != chunk2) {
|
||||||
|
+ chunk3 = (chunk1 + chunk2) / 2;
|
||||||
|
+ if (s->sectors[chunk3] > sector_num) {
|
||||||
|
+ chunk2 = chunk3;
|
||||||
|
+ } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) {
|
||||||
|
+ return chunk3;
|
||||||
|
+ } else {
|
||||||
|
+ chunk1 = chunk3;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
return s->n_chunks; /* error */
|
||||||
|
}
|
||||||
|
@@ -280,54 +286,62 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
|
||||||
|
{
|
||||||
|
BDRVDMGState *s = bs->opaque;
|
||||||
|
|
||||||
|
- if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) {
|
||||||
|
- int ret;
|
||||||
|
- uint32_t chunk = search_chunk(s,sector_num);
|
||||||
|
+ if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) {
|
||||||
|
+ int ret;
|
||||||
|
+ uint32_t chunk = search_chunk(s, sector_num);
|
||||||
|
|
||||||
|
- if(chunk>=s->n_chunks)
|
||||||
|
- return -1;
|
||||||
|
+ if (chunk >= s->n_chunks) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
- s->current_chunk = s->n_chunks;
|
||||||
|
- switch(s->types[chunk]) {
|
||||||
|
- case 0x80000005: { /* zlib compressed */
|
||||||
|
- int i;
|
||||||
|
+ s->current_chunk = s->n_chunks;
|
||||||
|
+ switch (s->types[chunk]) {
|
||||||
|
+ case 0x80000005: { /* zlib compressed */
|
||||||
|
+ int i;
|
||||||
|
|
||||||
|
- /* we need to buffer, because only the chunk as whole can be
|
||||||
|
- * inflated. */
|
||||||
|
- i=0;
|
||||||
|
- do {
|
||||||
|
+ /* we need to buffer, because only the chunk as whole can be
|
||||||
|
+ * inflated. */
|
||||||
|
+ i = 0;
|
||||||
|
+ do {
|
||||||
|
ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
|
||||||
|
- s->compressed_chunk+i, s->lengths[chunk]-i);
|
||||||
|
- if(ret<0 && errno==EINTR)
|
||||||
|
- ret=0;
|
||||||
|
- i+=ret;
|
||||||
|
- } while(ret>=0 && ret+i<s->lengths[chunk]);
|
||||||
|
-
|
||||||
|
- if (ret != s->lengths[chunk])
|
||||||
|
- return -1;
|
||||||
|
-
|
||||||
|
- s->zstream.next_in = s->compressed_chunk;
|
||||||
|
- s->zstream.avail_in = s->lengths[chunk];
|
||||||
|
- s->zstream.next_out = s->uncompressed_chunk;
|
||||||
|
- s->zstream.avail_out = 512*s->sectorcounts[chunk];
|
||||||
|
- ret = inflateReset(&s->zstream);
|
||||||
|
- if(ret != Z_OK)
|
||||||
|
- return -1;
|
||||||
|
- ret = inflate(&s->zstream, Z_FINISH);
|
||||||
|
- if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk])
|
||||||
|
- return -1;
|
||||||
|
- break; }
|
||||||
|
- case 1: /* copy */
|
||||||
|
- ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||||
|
+ s->compressed_chunk + i,
|
||||||
|
+ s->lengths[chunk] - i);
|
||||||
|
+ if (ret < 0 && errno == EINTR) {
|
||||||
|
+ ret = 0;
|
||||||
|
+ }
|
||||||
|
+ i += ret;
|
||||||
|
+ } while (ret >= 0 && ret + i < s->lengths[chunk]);
|
||||||
|
+
|
||||||
|
+ if (ret != s->lengths[chunk]) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ s->zstream.next_in = s->compressed_chunk;
|
||||||
|
+ s->zstream.avail_in = s->lengths[chunk];
|
||||||
|
+ s->zstream.next_out = s->uncompressed_chunk;
|
||||||
|
+ s->zstream.avail_out = 512 * s->sectorcounts[chunk];
|
||||||
|
+ ret = inflateReset(&s->zstream);
|
||||||
|
+ if (ret != Z_OK) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ ret = inflate(&s->zstream, Z_FINISH);
|
||||||
|
+ if (ret != Z_STREAM_END ||
|
||||||
|
+ s->zstream.total_out != 512 * s->sectorcounts[chunk]) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ break; }
|
||||||
|
+ case 1: /* copy */
|
||||||
|
+ ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||||
|
s->uncompressed_chunk, s->lengths[chunk]);
|
||||||
|
- if (ret != s->lengths[chunk])
|
||||||
|
- return -1;
|
||||||
|
- break;
|
||||||
|
- case 2: /* zero */
|
||||||
|
- memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]);
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- s->current_chunk = chunk;
|
||||||
|
+ if (ret != s->lengths[chunk]) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
+ case 2: /* zero */
|
||||||
|
+ memset(s->uncompressed_chunk, 0, 512 * s->sectorcounts[chunk]);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ s->current_chunk = chunk;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -338,12 +352,14 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
BDRVDMGState *s = bs->opaque;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
- for(i=0;i<nb_sectors;i++) {
|
||||||
|
- uint32_t sector_offset_in_chunk;
|
||||||
|
- if(dmg_read_chunk(bs, sector_num+i) != 0)
|
||||||
|
- return -1;
|
||||||
|
- sector_offset_in_chunk = sector_num+i-s->sectors[s->current_chunk];
|
||||||
|
- memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512);
|
||||||
|
+ for (i = 0; i < nb_sectors; i++) {
|
||||||
|
+ uint32_t sector_offset_in_chunk;
|
||||||
|
+ if (dmg_read_chunk(bs, sector_num + i) != 0) {
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk];
|
||||||
|
+ memcpy(buf + i * 512,
|
||||||
|
+ s->uncompressed_chunk + sector_offset_in_chunk * 512, 512);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
@@ -375,12 +391,12 @@ static void dmg_close(BlockDriverState *bs)
|
||||||
|
}
|
||||||
|
|
||||||
|
static BlockDriver bdrv_dmg = {
|
||||||
|
- .format_name = "dmg",
|
||||||
|
- .instance_size = sizeof(BDRVDMGState),
|
||||||
|
- .bdrv_probe = dmg_probe,
|
||||||
|
- .bdrv_open = dmg_open,
|
||||||
|
- .bdrv_read = dmg_co_read,
|
||||||
|
- .bdrv_close = dmg_close,
|
||||||
|
+ .format_name = "dmg",
|
||||||
|
+ .instance_size = sizeof(BDRVDMGState),
|
||||||
|
+ .bdrv_probe = dmg_probe,
|
||||||
|
+ .bdrv_open = dmg_open,
|
||||||
|
+ .bdrv_read = dmg_co_read,
|
||||||
|
+ .bdrv_close = dmg_close,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void bdrv_dmg_init(void)
|
@ -0,0 +1,33 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:55 +0100
|
||||||
|
Subject: [PATCH] dmg: prevent out-of-bounds array access on terminator
|
||||||
|
|
||||||
|
When a terminator is reached the base for offsets and sectors is stored.
|
||||||
|
The following records that are processed will use this base value.
|
||||||
|
|
||||||
|
If the first record we encounter is a terminator, then calculating the
|
||||||
|
base values would result in out-of-bounds array accesses. Don't do
|
||||||
|
that.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 73ed27ec28a1dbebdd2ae792284151f029950fbe)
|
||||||
|
---
|
||||||
|
block/dmg.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index e8b88dc..cb4060c 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -181,7 +181,7 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
offset += 4;
|
||||||
|
if (s->types[i] != 0x80000005 && s->types[i] != 1 &&
|
||||||
|
s->types[i] != 2) {
|
||||||
|
- if (s->types[i] == 0xffffffff) {
|
||||||
|
+ if (s->types[i] == 0xffffffff && i > 0) {
|
||||||
|
last_in_offset = s->offsets[i - 1] + s->lengths[i - 1];
|
||||||
|
last_out_offset = s->sectors[i - 1] +
|
||||||
|
s->sectorcounts[i - 1];
|
56
0148-dmg-drop-broken-bdrv_pread-loop.patch
Normal file
56
0148-dmg-drop-broken-bdrv_pread-loop.patch
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:56 +0100
|
||||||
|
Subject: [PATCH] dmg: drop broken bdrv_pread() loop
|
||||||
|
|
||||||
|
It is not necessary to check errno for EINTR and the block layer does
|
||||||
|
not produce short reads. Therefore we can drop the loop that attempts
|
||||||
|
to read a compressed chunk.
|
||||||
|
|
||||||
|
The loop is buggy because it incorrectly adds the transferred bytes
|
||||||
|
twice:
|
||||||
|
|
||||||
|
do {
|
||||||
|
ret = bdrv_pread(...);
|
||||||
|
i += ret;
|
||||||
|
} while (ret >= 0 && ret + i < s->lengths[chunk]);
|
||||||
|
|
||||||
|
Luckily we can drop the loop completely and perform a single
|
||||||
|
bdrv_pread().
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit b404bf854217dbe8a5649449eb3ad33777f7d900)
|
||||||
|
---
|
||||||
|
block/dmg.c | 15 ++-------------
|
||||||
|
1 file changed, 2 insertions(+), 13 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index cb4060c..24f08ef 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -297,21 +297,10 @@ static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
|
||||||
|
s->current_chunk = s->n_chunks;
|
||||||
|
switch (s->types[chunk]) {
|
||||||
|
case 0x80000005: { /* zlib compressed */
|
||||||
|
- int i;
|
||||||
|
-
|
||||||
|
/* we need to buffer, because only the chunk as whole can be
|
||||||
|
* inflated. */
|
||||||
|
- i = 0;
|
||||||
|
- do {
|
||||||
|
- ret = bdrv_pread(bs->file, s->offsets[chunk] + i,
|
||||||
|
- s->compressed_chunk + i,
|
||||||
|
- s->lengths[chunk] - i);
|
||||||
|
- if (ret < 0 && errno == EINTR) {
|
||||||
|
- ret = 0;
|
||||||
|
- }
|
||||||
|
- i += ret;
|
||||||
|
- } while (ret >= 0 && ret + i < s->lengths[chunk]);
|
||||||
|
-
|
||||||
|
+ ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||||
|
+ s->compressed_chunk, s->lengths[chunk]);
|
||||||
|
if (ret != s->lengths[chunk]) {
|
||||||
|
return -1;
|
||||||
|
}
|
41
0149-dmg-use-appropriate-types-when-reading-chunks.patch
Normal file
41
0149-dmg-use-appropriate-types-when-reading-chunks.patch
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:57 +0100
|
||||||
|
Subject: [PATCH] dmg: use appropriate types when reading chunks
|
||||||
|
|
||||||
|
Use the right types instead of signed int:
|
||||||
|
|
||||||
|
size_t new_size;
|
||||||
|
|
||||||
|
This is a byte count for g_realloc() that is calculated from uint32_t
|
||||||
|
and size_t values.
|
||||||
|
|
||||||
|
uint32_t chunk_count;
|
||||||
|
|
||||||
|
Use the same type as s->n_chunks, which is used together with
|
||||||
|
chunk_count.
|
||||||
|
|
||||||
|
This patch is a cleanup and does not fix bugs.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit eb71803b041f55779ea10d860c0f66df285c68de)
|
||||||
|
---
|
||||||
|
block/dmg.c | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index 24f08ef..5650e73 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -160,7 +160,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == 0x6d697368 && count >= 244) {
|
||||||
|
- int new_size, chunk_count;
|
||||||
|
+ size_t new_size;
|
||||||
|
+ uint32_t chunk_count;
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
offset += 200;
|
@ -0,0 +1,67 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:58 +0100
|
||||||
|
Subject: [PATCH] dmg: sanitize chunk length and sectorcount (CVE-2014-0145)
|
||||||
|
|
||||||
|
Chunk length and sectorcount are used for decompression buffers as well
|
||||||
|
as the bdrv_pread() count argument. Ensure that they have reasonable
|
||||||
|
values so neither memory allocation nor conversion from uint64_t to int
|
||||||
|
will cause problems.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit c165f7758009a4f793c1fc19ebb69cf55313450b)
|
||||||
|
---
|
||||||
|
block/dmg.c | 24 ++++++++++++++++++++++++
|
||||||
|
1 file changed, 24 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index 5650e73..1a751ea 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -27,6 +27,14 @@
|
||||||
|
#include "qemu/module.h"
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
+enum {
|
||||||
|
+ /* Limit chunk sizes to prevent unreasonable amounts of memory being used
|
||||||
|
+ * or truncating when converting to 32-bit types
|
||||||
|
+ */
|
||||||
|
+ DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */
|
||||||
|
+ DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
typedef struct BDRVDMGState {
|
||||||
|
CoMutex lock;
|
||||||
|
/* each chunk contains a certain number of sectors,
|
||||||
|
@@ -207,6 +215,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
offset += 8;
|
||||||
|
|
||||||
|
+ if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) {
|
||||||
|
+ error_report("sector count %" PRIu64 " for chunk %u is "
|
||||||
|
+ "larger than max (%u)",
|
||||||
|
+ s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX);
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
ret = read_uint64(bs, offset, &s->offsets[i]);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
||||||
|
@@ -220,6 +236,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
offset += 8;
|
||||||
|
|
||||||
|
+ if (s->lengths[i] > DMG_LENGTHS_MAX) {
|
||||||
|
+ error_report("length %" PRIu64 " for chunk %u is larger "
|
||||||
|
+ "than max (%u)",
|
||||||
|
+ s->lengths[i], i, DMG_LENGTHS_MAX);
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (s->lengths[i] > max_compressed_size) {
|
||||||
|
max_compressed_size = s->lengths[i];
|
||||||
|
}
|
@ -0,0 +1,48 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:05:59 +0100
|
||||||
|
Subject: [PATCH] dmg: use uint64_t consistently for sectors and lengths
|
||||||
|
|
||||||
|
The DMG metadata is stored as uint64_t, so use the same type for
|
||||||
|
sector_num. int was a particularly poor choice since it is only 32-bit
|
||||||
|
and would truncate large values.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 686d7148ec23402a172628c800022b3a95a022c9)
|
||||||
|
---
|
||||||
|
block/dmg.c | 6 +++---
|
||||||
|
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index 1a751ea..5229876 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -280,7 +280,7 @@ fail:
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||||
|
- uint32_t chunk_num, int sector_num)
|
||||||
|
+ uint32_t chunk_num, uint64_t sector_num)
|
||||||
|
{
|
||||||
|
if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num ||
|
||||||
|
s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) {
|
||||||
|
@@ -290,7 +290,7 @@ static inline int is_sector_in_chunk(BDRVDMGState* s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-static inline uint32_t search_chunk(BDRVDMGState *s, int sector_num)
|
||||||
|
+static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num)
|
||||||
|
{
|
||||||
|
/* binary search */
|
||||||
|
uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3;
|
||||||
|
@@ -307,7 +307,7 @@ static inline uint32_t search_chunk(BDRVDMGState *s, int sector_num)
|
||||||
|
return s->n_chunks; /* error */
|
||||||
|
}
|
||||||
|
|
||||||
|
-static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num)
|
||||||
|
+static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num)
|
||||||
|
{
|
||||||
|
BDRVDMGState *s = bs->opaque;
|
||||||
|
|
94
0152-dmg-prevent-chunk-buffer-overflow-CVE-2014-0145.patch
Normal file
94
0152-dmg-prevent-chunk-buffer-overflow-CVE-2014-0145.patch
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
From: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:00 +0100
|
||||||
|
Subject: [PATCH] dmg: prevent chunk buffer overflow (CVE-2014-0145)
|
||||||
|
|
||||||
|
Both compressed and uncompressed I/O is buffered. dmg_open() calculates
|
||||||
|
the maximum buffer size needed from the metadata in the image file.
|
||||||
|
|
||||||
|
There is currently a buffer overflow since ->lengths[] is accounted
|
||||||
|
against the maximum compressed buffer size but actually uses the
|
||||||
|
uncompressed buffer:
|
||||||
|
|
||||||
|
switch (s->types[chunk]) {
|
||||||
|
case 1: /* copy */
|
||||||
|
ret = bdrv_pread(bs->file, s->offsets[chunk],
|
||||||
|
s->uncompressed_chunk, s->lengths[chunk]);
|
||||||
|
|
||||||
|
We must account against the maximum uncompressed buffer size for type=1
|
||||||
|
chunks.
|
||||||
|
|
||||||
|
This patch fixes the maximum buffer size calculation to take into
|
||||||
|
account the chunk type. It is critical that we update the correct
|
||||||
|
maximum since there are two buffers ->compressed_chunk and
|
||||||
|
->uncompressed_chunk.
|
||||||
|
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit f0dce23475b5af5da6b17b97c1765271307734b6)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/dmg.c
|
||||||
|
---
|
||||||
|
block/dmg.c | 40 ++++++++++++++++++++++++++++++++++------
|
||||||
|
1 file changed, 34 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/dmg.c b/block/dmg.c
|
||||||
|
index 5229876..8d5d42f 100644
|
||||||
|
--- a/block/dmg.c
|
||||||
|
+++ b/block/dmg.c
|
||||||
|
@@ -100,6 +100,38 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int dmg_open(BlockDriverState *bs, QDict *options, int flags);
|
||||||
|
+/* Increase max chunk sizes, if necessary. This function is used to calculate
|
||||||
|
+ * the buffer sizes needed for compressed/uncompressed chunk I/O.
|
||||||
|
+ */
|
||||||
|
+static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk,
|
||||||
|
+ uint32_t *max_compressed_size,
|
||||||
|
+ uint32_t *max_sectors_per_chunk)
|
||||||
|
+{
|
||||||
|
+ uint32_t compressed_size = 0;
|
||||||
|
+ uint32_t uncompressed_sectors = 0;
|
||||||
|
+
|
||||||
|
+ switch (s->types[chunk]) {
|
||||||
|
+ case 0x80000005: /* zlib compressed */
|
||||||
|
+ compressed_size = s->lengths[chunk];
|
||||||
|
+ uncompressed_sectors = s->sectorcounts[chunk];
|
||||||
|
+ break;
|
||||||
|
+ case 1: /* copy */
|
||||||
|
+ uncompressed_sectors = (s->lengths[chunk] + 511) / 512;
|
||||||
|
+ break;
|
||||||
|
+ case 2: /* zero */
|
||||||
|
+ uncompressed_sectors = s->sectorcounts[chunk];
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (compressed_size > *max_compressed_size) {
|
||||||
|
+ *max_compressed_size = compressed_size;
|
||||||
|
+ }
|
||||||
|
+ if (uncompressed_sectors > *max_sectors_per_chunk) {
|
||||||
|
+ *max_sectors_per_chunk = uncompressed_sectors;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
{
|
||||||
|
BDRVDMGState *s = bs->opaque;
|
||||||
|
@@ -244,12 +276,8 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (s->lengths[i] > max_compressed_size) {
|
||||||
|
- max_compressed_size = s->lengths[i];
|
||||||
|
- }
|
||||||
|
- if (s->sectorcounts[i] > max_sectors_per_chunk) {
|
||||||
|
- max_sectors_per_chunk = s->sectorcounts[i];
|
||||||
|
- }
|
||||||
|
+ update_max_chunk_size(s, i, &max_compressed_size,
|
||||||
|
+ &max_sectors_per_chunk);
|
||||||
|
}
|
||||||
|
s->n_chunks += chunk_count;
|
||||||
|
}
|
32
0153-block-Limit-request-size-CVE-2014-0143.patch
Normal file
32
0153-block-Limit-request-size-CVE-2014-0143.patch
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:02 +0100
|
||||||
|
Subject: [PATCH] block: Limit request size (CVE-2014-0143)
|
||||||
|
|
||||||
|
Limiting the size of a single request to INT_MAX not only fixes a
|
||||||
|
direct integer overflow in bdrv_check_request() (which would only
|
||||||
|
trigger bad behaviour with ridiculously huge images, as in close to
|
||||||
|
2^64 bytes), but can also prevent overflows in all block drivers.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 8f4754ede56e3f9ea3fd7207f4a7c4453e59285b)
|
||||||
|
---
|
||||||
|
block.c | 4 ++++
|
||||||
|
1 file changed, 4 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/block.c b/block.c
|
||||||
|
index 8ce8b91..6c48469 100644
|
||||||
|
--- a/block.c
|
||||||
|
+++ b/block.c
|
||||||
|
@@ -2160,6 +2160,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset,
|
||||||
|
static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num,
|
||||||
|
int nb_sectors)
|
||||||
|
{
|
||||||
|
+ if (nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) {
|
||||||
|
+ return -EIO;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE,
|
||||||
|
nb_sectors * BDRV_SECTOR_SIZE);
|
||||||
|
}
|
117
0154-qcow2-Fix-copy_sectors-with-VM-state.patch
Normal file
117
0154-qcow2-Fix-copy_sectors-with-VM-state.patch
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:03 +0100
|
||||||
|
Subject: [PATCH] qcow2: Fix copy_sectors() with VM state
|
||||||
|
|
||||||
|
bs->total_sectors is not the highest possible sector number that could
|
||||||
|
be involved in a copy on write operation: VM state is after the end of
|
||||||
|
the virtual disk. This resulted in wrong values for the number of
|
||||||
|
sectors to be copied (n).
|
||||||
|
|
||||||
|
The code that checks for the end of the image isn't required any more
|
||||||
|
because the code hasn't been calling the block layer's bdrv_read() for a
|
||||||
|
long time; instead, it directly calls qcow2_readv(), which doesn't error
|
||||||
|
out on VM state sector numbers.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 6b7d4c55586a849aa8313282d79432917eade3bf)
|
||||||
|
---
|
||||||
|
block/qcow2-cluster.c | 9 ---------
|
||||||
|
tests/qemu-iotests/029 | 22 ++++++++++++++++++++--
|
||||||
|
tests/qemu-iotests/029.out | 13 +++++++++++++
|
||||||
|
3 files changed, 33 insertions(+), 11 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
|
||||||
|
index 06e3e14..3df140c 100644
|
||||||
|
--- a/block/qcow2-cluster.c
|
||||||
|
+++ b/block/qcow2-cluster.c
|
||||||
|
@@ -335,15 +335,6 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
|
||||||
|
struct iovec iov;
|
||||||
|
int n, ret;
|
||||||
|
|
||||||
|
- /*
|
||||||
|
- * If this is the last cluster and it is only partially used, we must only
|
||||||
|
- * copy until the end of the image, or bdrv_check_request will fail for the
|
||||||
|
- * bdrv_read/write calls below.
|
||||||
|
- */
|
||||||
|
- if (start_sect + n_end > bs->total_sectors) {
|
||||||
|
- n_end = bs->total_sectors - start_sect;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
n = n_end - n_start;
|
||||||
|
if (n <= 0) {
|
||||||
|
return 0;
|
||||||
|
diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029
|
||||||
|
index 0ad5e45..478fd96 100755
|
||||||
|
--- a/tests/qemu-iotests/029
|
||||||
|
+++ b/tests/qemu-iotests/029
|
||||||
|
@@ -1,7 +1,6 @@
|
||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
-# Test loading internal snapshots where the L1 table of the snapshot
|
||||||
|
-# is smaller than the current L1 table.
|
||||||
|
+# qcow2 internal snapshots/VM state tests
|
||||||
|
#
|
||||||
|
# Copyright (C) 2011 Red Hat, Inc.
|
||||||
|
#
|
||||||
|
@@ -45,6 +44,11 @@ _supported_fmt qcow2
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
+echo
|
||||||
|
+echo Test loading internal snapshots where the L1 table of the snapshot
|
||||||
|
+echo is smaller than the current L1 table.
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
CLUSTER_SIZE=65536
|
||||||
|
_make_test_img 64M
|
||||||
|
$QEMU_IMG snapshot -c foo $TEST_IMG
|
||||||
|
@@ -59,6 +63,20 @@ $QEMU_IO -c 'write -b 0 4M' $TEST_IMG | _filter_qemu_io
|
||||||
|
$QEMU_IMG snapshot -a foo $TEST_IMG
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
+
|
||||||
|
+echo
|
||||||
|
+echo Try using a huge VM state
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+CLUSTER_SIZE=65536
|
||||||
|
+_make_test_img 64M
|
||||||
|
+{ $QEMU_IO -c "write -b -P 0x11 1T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
+{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
+{ $QEMU_IMG snapshot -a foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
+{ $QEMU_IO -c "read -b -P 0x11 1T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
+_check_test_img
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out
|
||||||
|
index 0eedb3a..9029698 100644
|
||||||
|
--- a/tests/qemu-iotests/029.out
|
||||||
|
+++ b/tests/qemu-iotests/029.out
|
||||||
|
@@ -1,4 +1,8 @@
|
||||||
|
QA output created by 029
|
||||||
|
+
|
||||||
|
+Test loading internal snapshots where the L1 table of the snapshot
|
||||||
|
+is smaller than the current L1 table.
|
||||||
|
+
|
||||||
|
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
wrote 4096/4096 bytes at offset 0
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
@@ -7,4 +11,13 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216
|
||||||
|
wrote 4194304/4194304 bytes at offset 0
|
||||||
|
4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
No errors were found on the image.
|
||||||
|
+
|
||||||
|
+Try using a huge VM state
|
||||||
|
+
|
||||||
|
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
+wrote 4096/4096 bytes at offset 1099511627776
|
||||||
|
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
+read 4096/4096 bytes at offset 1099511627776
|
||||||
|
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
+No errors were found on the image.
|
||||||
|
*** done
|
@ -0,0 +1,48 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:04 +0100
|
||||||
|
Subject: [PATCH] qcow2: Fix NULL dereference in qcow2_open() error path
|
||||||
|
(CVE-2014-0146)
|
||||||
|
|
||||||
|
The qcow2 code assumes that s->snapshots is non-NULL if s->nb_snapshots
|
||||||
|
!= 0. By having the initialisation of both fields separated in
|
||||||
|
qcow2_open(), any error occuring in between would cause the error path
|
||||||
|
to dereference NULL in qcow2_free_snapshots() if the image had any
|
||||||
|
snapshots.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 11b128f4062dd7f89b14abc8877ff20d41b28be9)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2.c | 7 ++++---
|
||||||
|
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index af0a45c..c9beb01 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -515,9 +515,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- s->snapshots_offset = header.snapshots_offset;
|
||||||
|
- s->nb_snapshots = header.nb_snapshots;
|
||||||
|
-
|
||||||
|
/* read the level 1 table */
|
||||||
|
if (header.l1_size > 0x2000000) {
|
||||||
|
/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
||||||
|
@@ -605,6 +602,10 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
bs->backing_file[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Internal snapshots */
|
||||||
|
+ s->snapshots_offset = header.snapshots_offset;
|
||||||
|
+ s->nb_snapshots = header.nb_snapshots;
|
||||||
|
+
|
||||||
|
ret = qcow2_read_snapshots(bs);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto fail;
|
@ -0,0 +1,89 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:05 +0100
|
||||||
|
Subject: [PATCH] qcow2: Fix L1 allocation size in qcow2_snapshot_load_tmp()
|
||||||
|
(CVE-2014-0145)
|
||||||
|
|
||||||
|
For the L1 table to loaded for an internal snapshot, the code allocated
|
||||||
|
only enough memory to hold the currently active L1 table. If the
|
||||||
|
snapshot's L1 table is actually larger than the current one, this leads
|
||||||
|
to a buffer overflow.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit c05e4667be91b46ab42b5a11babf8e84d476cc6b)
|
||||||
|
---
|
||||||
|
block/qcow2-snapshot.c | 2 +-
|
||||||
|
tests/qemu-iotests/029 | 18 +++++++++++++++++-
|
||||||
|
tests/qemu-iotests/029.out | 4 ++++
|
||||||
|
3 files changed, 22 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
|
||||||
|
index eb80438..dc8736a 100644
|
||||||
|
--- a/block/qcow2-snapshot.c
|
||||||
|
+++ b/block/qcow2-snapshot.c
|
||||||
|
@@ -622,7 +622,7 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||||
|
sn = &s->snapshots[snapshot_index];
|
||||||
|
|
||||||
|
/* Allocate and read in the snapshot's L1 table */
|
||||||
|
- new_l1_bytes = s->l1_size * sizeof(uint64_t);
|
||||||
|
+ new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||||
|
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes);
|
||||||
|
diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029
|
||||||
|
index 478fd96..dfb5726 100755
|
||||||
|
--- a/tests/qemu-iotests/029
|
||||||
|
+++ b/tests/qemu-iotests/029
|
||||||
|
@@ -30,7 +30,8 @@ status=1 # failure is the default!
|
||||||
|
|
||||||
|
_cleanup()
|
||||||
|
{
|
||||||
|
- _cleanup_test_img
|
||||||
|
+ rm -f $TEST_IMG.snap
|
||||||
|
+ _cleanup_test_img
|
||||||
|
}
|
||||||
|
trap "_cleanup; exit \$status" 0 1 2 3 15
|
||||||
|
|
||||||
|
@@ -44,6 +45,9 @@ _supported_fmt qcow2
|
||||||
|
_supported_proto generic
|
||||||
|
_supported_os Linux
|
||||||
|
|
||||||
|
+offset_size=24
|
||||||
|
+offset_l1_size=36
|
||||||
|
+
|
||||||
|
echo
|
||||||
|
echo Test loading internal snapshots where the L1 table of the snapshot
|
||||||
|
echo is smaller than the current L1 table.
|
||||||
|
@@ -77,6 +81,18 @@ _make_test_img 64M
|
||||||
|
_check_test_img
|
||||||
|
|
||||||
|
|
||||||
|
+echo
|
||||||
|
+echo "qcow2_snapshot_load_tmp() should take the L1 size from the snapshot"
|
||||||
|
+echo
|
||||||
|
+
|
||||||
|
+CLUSTER_SIZE=512
|
||||||
|
+_make_test_img 64M
|
||||||
|
+{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
+poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00"
|
||||||
|
+poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01"
|
||||||
|
+{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# success, all done
|
||||||
|
echo "*** done"
|
||||||
|
rm -f $seq.full
|
||||||
|
diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out
|
||||||
|
index 9029698..ce0e64d 100644
|
||||||
|
--- a/tests/qemu-iotests/029.out
|
||||||
|
+++ b/tests/qemu-iotests/029.out
|
||||||
|
@@ -20,4 +20,8 @@ wrote 4096/4096 bytes at offset 1099511627776
|
||||||
|
read 4096/4096 bytes at offset 1099511627776
|
||||||
|
4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
|
||||||
|
No errors were found on the image.
|
||||||
|
+
|
||||||
|
+qcow2_snapshot_load_tmp() should take the L1 size from the snapshot
|
||||||
|
+
|
||||||
|
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
|
||||||
|
*** done
|
@ -0,0 +1,67 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:06 +0100
|
||||||
|
Subject: [PATCH] qcow2: Check maximum L1 size in qcow2_snapshot_load_tmp()
|
||||||
|
(CVE-2014-0143)
|
||||||
|
|
||||||
|
This avoids an unbounded allocation.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 6a83f8b5bec6f59e56cc49bd49e4c3f8f805d56f)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
block/qcow2.c
|
||||||
|
tests/qemu-iotests/080
|
||||||
|
tests/qemu-iotests/080.out
|
||||||
|
---
|
||||||
|
block/qcow2-snapshot.c | 4 ++++
|
||||||
|
block/qcow2.c | 4 +---
|
||||||
|
block/qcow2.h | 4 ++++
|
||||||
|
3 files changed, 9 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
|
||||||
|
index dc8736a..d2c956c 100644
|
||||||
|
--- a/block/qcow2-snapshot.c
|
||||||
|
+++ b/block/qcow2-snapshot.c
|
||||||
|
@@ -622,6 +622,10 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name)
|
||||||
|
sn = &s->snapshots[snapshot_index];
|
||||||
|
|
||||||
|
/* Allocate and read in the snapshot's L1 table */
|
||||||
|
+ if (sn->l1_size > QCOW_MAX_L1_SIZE) {
|
||||||
|
+ fprintf(stderr, "Snapshot L1 table too large");
|
||||||
|
+ return -EFBIG;
|
||||||
|
+ }
|
||||||
|
new_l1_bytes = sn->l1_size * sizeof(uint64_t);
|
||||||
|
new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512));
|
||||||
|
|
||||||
|
diff --git a/block/qcow2.c b/block/qcow2.c
|
||||||
|
index c9beb01..5dfd5ec 100644
|
||||||
|
--- a/block/qcow2.c
|
||||||
|
+++ b/block/qcow2.c
|
||||||
|
@@ -516,9 +516,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read the level 1 table */
|
||||||
|
- if (header.l1_size > 0x2000000) {
|
||||||
|
- /* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
||||||
|
- * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
+ if (header.l1_size > QCOW_MAX_L1_SIZE) {
|
||||||
|
fprintf(stderr, "Active L1 table too large");
|
||||||
|
ret = -EFBIG;
|
||||||
|
goto fail;
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index baf62a0..0912488 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -44,6 +44,10 @@
|
||||||
|
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
#define QCOW_MAX_REFTABLE_SIZE 0x800000
|
||||||
|
|
||||||
|
+/* 32 MB L1 table is enough for 2 PB images at 64k cluster size
|
||||||
|
+ * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
+#define QCOW_MAX_L1_SIZE 0x2000000
|
||||||
|
+
|
||||||
|
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||||
|
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||||
|
/* indicate that the cluster is compressed (they never have the copied flag) */
|
81
0158-qcow2-Limit-snapshot-table-size.patch
Normal file
81
0158-qcow2-Limit-snapshot-table-size.patch
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:07 +0100
|
||||||
|
Subject: [PATCH] qcow2: Limit snapshot table size
|
||||||
|
|
||||||
|
Even with a limit of 64k snapshots, each snapshot could have a filename
|
||||||
|
and an ID with up to 64k, which would still lead to pretty large
|
||||||
|
allocations, which could potentially lead to qemu aborting. Limit the
|
||||||
|
total size of the snapshot table to an average of 1k per entry when
|
||||||
|
the limit of 64k snapshots is fully used. This should be plenty for any
|
||||||
|
reasonable user.
|
||||||
|
|
||||||
|
This also fixes potential integer overflows of s->snapshot_size.
|
||||||
|
|
||||||
|
Suggested-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 5dae6e30c531feb31eed99f9039b52bf70832ce3)
|
||||||
|
---
|
||||||
|
block/qcow2-snapshot.c | 15 ++++++++++++++-
|
||||||
|
block/qcow2.h | 4 ++++
|
||||||
|
2 files changed, 18 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
|
||||||
|
index d2c956c..5d3c0e6 100644
|
||||||
|
--- a/block/qcow2-snapshot.c
|
||||||
|
+++ b/block/qcow2-snapshot.c
|
||||||
|
@@ -116,8 +116,14 @@ int qcow2_read_snapshots(BlockDriverState *bs)
|
||||||
|
}
|
||||||
|
offset += name_size;
|
||||||
|
sn->name[name_size] = '\0';
|
||||||
|
+
|
||||||
|
+ if (offset - s->snapshots_offset > QCOW_MAX_SNAPSHOTS_SIZE) {
|
||||||
|
+ ret = -EFBIG;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
+ assert(offset - s->snapshots_offset <= INT_MAX);
|
||||||
|
s->snapshots_size = offset - s->snapshots_offset;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
@@ -138,7 +144,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||||
|
uint32_t nb_snapshots;
|
||||||
|
uint64_t snapshots_offset;
|
||||||
|
} QEMU_PACKED header_data;
|
||||||
|
- int64_t offset, snapshots_offset;
|
||||||
|
+ int64_t offset, snapshots_offset = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* compute the size of the snapshots */
|
||||||
|
@@ -150,7 +156,14 @@ static int qcow2_write_snapshots(BlockDriverState *bs)
|
||||||
|
offset += sizeof(extra);
|
||||||
|
offset += strlen(sn->id_str);
|
||||||
|
offset += strlen(sn->name);
|
||||||
|
+
|
||||||
|
+ if (offset > QCOW_MAX_SNAPSHOTS_SIZE) {
|
||||||
|
+ ret = -EFBIG;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
+
|
||||||
|
+ assert(offset <= INT_MAX);
|
||||||
|
snapshots_size = offset;
|
||||||
|
|
||||||
|
/* Allocate space for the new snapshot list */
|
||||||
|
diff --git a/block/qcow2.h b/block/qcow2.h
|
||||||
|
index 0912488..797c9d8 100644
|
||||||
|
--- a/block/qcow2.h
|
||||||
|
+++ b/block/qcow2.h
|
||||||
|
@@ -48,6 +48,10 @@
|
||||||
|
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
|
||||||
|
#define QCOW_MAX_L1_SIZE 0x2000000
|
||||||
|
|
||||||
|
+/* Allow for an average of 1k per snapshot table entry, should be plenty of
|
||||||
|
+ * space for snapshot names and IDs */
|
||||||
|
+#define QCOW_MAX_SNAPSHOTS_SIZE (1024 * QCOW_MAX_SNAPSHOTS)
|
||||||
|
+
|
||||||
|
/* indicate that the refcount of the referenced cluster is exactly one. */
|
||||||
|
#define QCOW_OFLAG_COPIED (1LL << 63)
|
||||||
|
/* indicate that the cluster is compressed (they never have the copied flag) */
|
@ -0,0 +1,52 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:08 +0100
|
||||||
|
Subject: [PATCH] parallels: Fix catalog size integer overflow (CVE-2014-0143)
|
||||||
|
|
||||||
|
The first test case would cause a huge memory allocation, leading to a
|
||||||
|
qemu abort; the second one to a too small malloc() for the catalog
|
||||||
|
(smaller than s->catalog_size), which causes a read-only out-of-bounds
|
||||||
|
array access and on big endian hosts an endianess conversion for an
|
||||||
|
undefined memory area.
|
||||||
|
|
||||||
|
The sample image used here is not an original Parallels image. It was
|
||||||
|
created using an hexeditor on the basis of the struct that qemu uses.
|
||||||
|
Good enough for trying to crash the driver, but not for ensuring
|
||||||
|
compatibility.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit afbcc40bee4ef51731102d7d4b499ee12fc182e1)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/common
|
||||||
|
tests/qemu-iotests/group
|
||||||
|
---
|
||||||
|
block/parallels.c | 7 ++++++-
|
||||||
|
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/parallels.c b/block/parallels.c
|
||||||
|
index 18b3ac0..bad47f4 100644
|
||||||
|
--- a/block/parallels.c
|
||||||
|
+++ b/block/parallels.c
|
||||||
|
@@ -49,7 +49,7 @@ typedef struct BDRVParallelsState {
|
||||||
|
CoMutex lock;
|
||||||
|
|
||||||
|
uint32_t *catalog_bitmap;
|
||||||
|
- int catalog_size;
|
||||||
|
+ unsigned int catalog_size;
|
||||||
|
|
||||||
|
int tracks;
|
||||||
|
} BDRVParallelsState;
|
||||||
|
@@ -93,6 +93,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
s->tracks = le32_to_cpu(ph.tracks);
|
||||||
|
|
||||||
|
s->catalog_size = le32_to_cpu(ph.catalog_entries);
|
||||||
|
+ if (s->catalog_size > INT_MAX / 4) {
|
||||||
|
+ fprintf(stderr, "Catalog too large");
|
||||||
|
+ ret = -EFBIG;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
s->catalog_bitmap = g_malloc(s->catalog_size * 4);
|
||||||
|
|
||||||
|
ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4);
|
47
0160-parallels-Sanity-check-for-s-tracks-CVE-2014-0142.patch
Normal file
47
0160-parallels-Sanity-check-for-s-tracks-CVE-2014-0142.patch
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
From: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Date: Wed, 26 Mar 2014 13:06:09 +0100
|
||||||
|
Subject: [PATCH] parallels: Sanity check for s->tracks (CVE-2014-0142)
|
||||||
|
|
||||||
|
This avoids a possible division by zero.
|
||||||
|
|
||||||
|
Convert s->tracks to unsigned as well because it feels better than
|
||||||
|
surviving just because the results of calculations with s->tracks are
|
||||||
|
converted to unsigned anyway.
|
||||||
|
|
||||||
|
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
Reviewed-by: Max Reitz <mreitz@redhat.com>
|
||||||
|
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
|
||||||
|
(cherry picked from commit 9302e863aa8baa5d932fc078967050c055fa1a7f)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
tests/qemu-iotests/076
|
||||||
|
tests/qemu-iotests/076.out
|
||||||
|
---
|
||||||
|
block/parallels.c | 7 ++++++-
|
||||||
|
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/block/parallels.c b/block/parallels.c
|
||||||
|
index bad47f4..4bcfe06 100644
|
||||||
|
--- a/block/parallels.c
|
||||||
|
+++ b/block/parallels.c
|
||||||
|
@@ -51,7 +51,7 @@ typedef struct BDRVParallelsState {
|
||||||
|
uint32_t *catalog_bitmap;
|
||||||
|
unsigned int catalog_size;
|
||||||
|
|
||||||
|
- int tracks;
|
||||||
|
+ unsigned int tracks;
|
||||||
|
} BDRVParallelsState;
|
||||||
|
|
||||||
|
static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename)
|
||||||
|
@@ -91,6 +91,11 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags)
|
||||||
|
bs->total_sectors = le32_to_cpu(ph.nb_sectors);
|
||||||
|
|
||||||
|
s->tracks = le32_to_cpu(ph.tracks);
|
||||||
|
+ if (s->tracks == 0) {
|
||||||
|
+ fprintf(stderr, "Invalid image: Zero sectors per track");
|
||||||
|
+ ret = -EINVAL;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
s->catalog_size = le32_to_cpu(ph.catalog_entries);
|
||||||
|
if (s->catalog_size > INT_MAX / 4) {
|
50
0201-virtio-net-fix-guest-triggerable-buffer-overrun.patch
Normal file
50
0201-virtio-net-fix-guest-triggerable-buffer-overrun.patch
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
From: "Michael S. Tsirkin" <mst@redhat.com>
|
||||||
|
Date: Fri, 11 Apr 2014 15:18:08 +0300
|
||||||
|
Subject: [PATCH] virtio-net: fix guest-triggerable buffer overrun
|
||||||
|
|
||||||
|
When VM guest programs multicast addresses for
|
||||||
|
a virtio net card, it supplies a 32 bit
|
||||||
|
entries counter for the number of addresses.
|
||||||
|
These addresses are read into tail portion of
|
||||||
|
a fixed macs array which has size MAC_TABLE_ENTRIES,
|
||||||
|
at offset equal to in_use.
|
||||||
|
|
||||||
|
To avoid overflow of this array by guest, qemu attempts
|
||||||
|
to test the size as follows:
|
||||||
|
- if (in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
|
||||||
|
|
||||||
|
however, as mac_data.entries is uint32_t, this sum
|
||||||
|
can overflow, e.g. if in_use is 1 and mac_data.entries
|
||||||
|
is 0xffffffff then in_use + mac_data.entries will be 0.
|
||||||
|
|
||||||
|
Qemu will then read guest supplied buffer into this
|
||||||
|
memory, overflowing buffer on heap.
|
||||||
|
|
||||||
|
CVE-2014-0150
|
||||||
|
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Message-id: 1397218574-25058-1-git-send-email-mst@redhat.com
|
||||||
|
Reviewed-by: Michael Tokarev <mjt@tls.msk.ru>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit edc243851279e3393000b28b6b69454cae1190ef)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
hw/net/virtio-net.c
|
||||||
|
---
|
||||||
|
hw/net/virtio-net.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
|
||||||
|
index 060b900..63f777f 100644
|
||||||
|
--- a/hw/net/virtio-net.c
|
||||||
|
+++ b/hw/net/virtio-net.c
|
||||||
|
@@ -655,7 +655,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) {
|
||||||
|
+ if (mac_data.entries <= MAC_TABLE_ENTRIES - n->mac_table.in_use) {
|
||||||
|
s = iov_to_buf(iov, iov_cnt, 0, n->mac_table.macs,
|
||||||
|
mac_data.entries * ETH_ALEN);
|
||||||
|
if (s != mac_data.entries * ETH_ALEN) {
|
@ -0,0 +1,95 @@
|
|||||||
|
From: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Date: Fri, 4 Apr 2014 12:45:19 +0300
|
||||||
|
Subject: [PATCH] vmxnet3: validate interrupt indices coming from guest
|
||||||
|
|
||||||
|
CVE-2013-4544
|
||||||
|
|
||||||
|
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Reported-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
|
Message-id: 1396604722-11902-2-git-send-email-dmitry@daynix.com
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 8c6c0478996e8f77374e69b6df68655b0b4ba689)
|
||||||
|
|
||||||
|
Conflicts:
|
||||||
|
hw/net/vmxnet3.c
|
||||||
|
---
|
||||||
|
hw/net/vmxnet3.c | 36 ++++++++++++++++++++++++++++++++++--
|
||||||
|
1 file changed, 34 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
|
||||||
|
index 49c2466..6ae39a4 100644
|
||||||
|
--- a/hw/net/vmxnet3.c
|
||||||
|
+++ b/hw/net/vmxnet3.c
|
||||||
|
@@ -52,6 +52,9 @@
|
||||||
|
#define VMXNET3_DEVICE_VERSION 0x1
|
||||||
|
#define VMXNET3_DEVICE_REVISION 0x1
|
||||||
|
|
||||||
|
+/* Number of interrupt vectors for non-MSIx modes */
|
||||||
|
+#define VMXNET3_MAX_NMSIX_INTRS (1)
|
||||||
|
+
|
||||||
|
/* Macros for rings descriptors access */
|
||||||
|
#define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \
|
||||||
|
(vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field)))
|
||||||
|
@@ -1299,6 +1302,34 @@ static void vmxnet3_update_features(VMXNET3State *s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx)
|
||||||
|
+{
|
||||||
|
+ int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS;
|
||||||
|
+ if (idx >= max_ints) {
|
||||||
|
+ hw_error("Bad interrupt index: %d\n", idx);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void vmxnet3_validate_interrupts(VMXNET3State *s)
|
||||||
|
+{
|
||||||
|
+ int i;
|
||||||
|
+
|
||||||
|
+ VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx);
|
||||||
|
+ vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx);
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < s->txq_num; i++) {
|
||||||
|
+ int idx = s->txq_descr[i].intr_idx;
|
||||||
|
+ VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx);
|
||||||
|
+ vmxnet3_validate_interrupt_idx(s->msix_used, idx);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < s->rxq_num; i++) {
|
||||||
|
+ int idx = s->rxq_descr[i].intr_idx;
|
||||||
|
+ VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx);
|
||||||
|
+ vmxnet3_validate_interrupt_idx(s->msix_used, idx);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static void vmxnet3_activate_device(VMXNET3State *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
@@ -1438,6 +1469,8 @@ static void vmxnet3_activate_device(VMXNET3State *s)
|
||||||
|
sizeof(s->rxq_descr[i].rxq_stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
+ vmxnet3_validate_interrupts(s);
|
||||||
|
+
|
||||||
|
/* Make sure everything is in place before device activation */
|
||||||
|
smp_wmb();
|
||||||
|
|
||||||
|
@@ -1998,7 +2031,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-#define VMXNET3_MSI_NUM_VECTORS (1)
|
||||||
|
#define VMXNET3_MSI_OFFSET (0x50)
|
||||||
|
#define VMXNET3_USE_64BIT (true)
|
||||||
|
#define VMXNET3_PER_VECTOR_MASK (false)
|
||||||
|
@@ -2009,7 +2041,7 @@ vmxnet3_init_msi(VMXNET3State *s)
|
||||||
|
PCIDevice *d = PCI_DEVICE(s);
|
||||||
|
int res;
|
||||||
|
|
||||||
|
- res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS,
|
||||||
|
+ res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS,
|
||||||
|
VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK);
|
||||||
|
if (0 > res) {
|
||||||
|
VMW_WRPRN("Failed to initialize MSI, error %d", res);
|
@ -0,0 +1,54 @@
|
|||||||
|
From: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Date: Fri, 4 Apr 2014 12:45:20 +0300
|
||||||
|
Subject: [PATCH] vmxnet3: validate queues configuration coming from guest
|
||||||
|
|
||||||
|
CVE-2013-4544
|
||||||
|
|
||||||
|
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Reported-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
|
Message-id: 1396604722-11902-3-git-send-email-dmitry@daynix.com
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 9878d173f574df74bde0ff50b2f81009fbee81bb)
|
||||||
|
---
|
||||||
|
hw/net/vmxnet3.c | 19 ++++++++++++++++++-
|
||||||
|
1 file changed, 18 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
|
||||||
|
index 6ae39a4..7325670 100644
|
||||||
|
--- a/hw/net/vmxnet3.c
|
||||||
|
+++ b/hw/net/vmxnet3.c
|
||||||
|
@@ -1330,6 +1330,23 @@ static void vmxnet3_validate_interrupts(VMXNET3State *s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void vmxnet3_validate_queues(VMXNET3State *s)
|
||||||
|
+{
|
||||||
|
+ /*
|
||||||
|
+ * txq_num and rxq_num are total number of queues
|
||||||
|
+ * configured by guest. These numbers must not
|
||||||
|
+ * exceed corresponding maximal values.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+ if (s->txq_num > VMXNET3_DEVICE_MAX_TX_QUEUES) {
|
||||||
|
+ hw_error("Bad TX queues number: %d\n", s->txq_num);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (s->rxq_num > VMXNET3_DEVICE_MAX_RX_QUEUES) {
|
||||||
|
+ hw_error("Bad RX queues number: %d\n", s->rxq_num);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static void vmxnet3_activate_device(VMXNET3State *s)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
@@ -1375,7 +1392,7 @@ static void vmxnet3_activate_device(VMXNET3State *s)
|
||||||
|
VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues);
|
||||||
|
|
||||||
|
VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num);
|
||||||
|
- assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES);
|
||||||
|
+ vmxnet3_validate_queues(s);
|
||||||
|
|
||||||
|
qdescr_table_pa =
|
||||||
|
VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA);
|
@ -0,0 +1,30 @@
|
|||||||
|
From: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Date: Fri, 4 Apr 2014 12:45:21 +0300
|
||||||
|
Subject: [PATCH] vmxnet3: validate interrupt indices read on migration
|
||||||
|
|
||||||
|
CVE-2013-4544
|
||||||
|
|
||||||
|
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Reported-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
|
Message-id: 1396604722-11902-4-git-send-email-dmitry@daynix.com
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 3c99afc779c2c78718a565ad8c5e98de7c2c7484)
|
||||||
|
---
|
||||||
|
hw/net/vmxnet3.c | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
|
||||||
|
index 7325670..20bd3c6 100644
|
||||||
|
--- a/hw/net/vmxnet3.c
|
||||||
|
+++ b/hw/net/vmxnet3.c
|
||||||
|
@@ -2384,6 +2384,8 @@ static int vmxnet3_post_load(void *opaque, int version_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ vmxnet3_validate_interrupts(s);
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
|||||||
|
From: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Date: Fri, 4 Apr 2014 12:45:22 +0300
|
||||||
|
Subject: [PATCH] vmxnet3: validate queues configuration read on migration
|
||||||
|
|
||||||
|
CVE-2013-4544
|
||||||
|
|
||||||
|
Signed-off-by: Dmitry Fleytman <dmitry@daynix.com>
|
||||||
|
Reported-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
|
Message-id: 1396604722-11902-5-git-send-email-dmitry@daynix.com
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit f12d048a523780dbda702027d4a91b62af1a08d7)
|
||||||
|
---
|
||||||
|
hw/net/vmxnet3.c | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
|
||||||
|
index 20bd3c6..1bbea3b 100644
|
||||||
|
--- a/hw/net/vmxnet3.c
|
||||||
|
+++ b/hw/net/vmxnet3.c
|
||||||
|
@@ -2384,6 +2384,7 @@ static int vmxnet3_post_load(void *opaque, int version_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ vmxnet3_validate_queues(s);
|
||||||
|
vmxnet3_validate_interrupts(s);
|
||||||
|
|
||||||
|
return 0;
|
@ -0,0 +1,37 @@
|
|||||||
|
From: =?UTF-8?q?Beno=C3=AEt=20Canet?= <benoit.canet@irqsave.net>
|
||||||
|
Date: Sat, 12 Apr 2014 22:59:50 +0200
|
||||||
|
Subject: [PATCH] ide: Correct improper smart self test counter reset in ide
|
||||||
|
core.
|
||||||
|
|
||||||
|
The SMART self test counter was incorrectly being reset to zero,
|
||||||
|
not 1. This had the effect that on every 21st SMART EXECUTE OFFLINE:
|
||||||
|
* We would write off the beginning of a dynamically allocated buffer
|
||||||
|
* We forgot the SMART history
|
||||||
|
Fix this.
|
||||||
|
|
||||||
|
Signed-off-by: Benoit Canet <benoit@irqsave.net>
|
||||||
|
Message-id: 1397336390-24664-1-git-send-email-benoit.canet@irqsave.net
|
||||||
|
Reviewed-by: Markus Armbruster <armbru@redhat.com>
|
||||||
|
Cc: qemu-stable@nongnu.org
|
||||||
|
Acked-by: Kevin Wolf <kwolf@redhat.com>
|
||||||
|
[PMM: tweaked commit message as per suggestions from Markus]
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
|
||||||
|
(cherry picked from commit 940973ae0b45c9b6817bab8e4cf4df99a9ef83d7)
|
||||||
|
---
|
||||||
|
hw/ide/core.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/ide/core.c b/hw/ide/core.c
|
||||||
|
index a73af72..3fe67ca 100644
|
||||||
|
--- a/hw/ide/core.c
|
||||||
|
+++ b/hw/ide/core.c
|
||||||
|
@@ -1601,7 +1601,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd)
|
||||||
|
case 2: /* extended self test */
|
||||||
|
s->smart_selftest_count++;
|
||||||
|
if (s->smart_selftest_count > 21) {
|
||||||
|
- s->smart_selftest_count = 0;
|
||||||
|
+ s->smart_selftest_count = 1;
|
||||||
|
}
|
||||||
|
n = 2 + (s->smart_selftest_count - 1) * 24;
|
||||||
|
s->smart_selftest_data[n] = s->sector;
|
29
0207-char-serial-Fix-emptyness-check.patch
Normal file
29
0207-char-serial-Fix-emptyness-check.patch
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
From: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
|
Date: Mon, 10 Feb 2014 22:49:35 -0800
|
||||||
|
Subject: [PATCH] char/serial: Fix emptyness check
|
||||||
|
|
||||||
|
This was guarding against a full fifo rather than an empty fifo when
|
||||||
|
popping. Fix.
|
||||||
|
|
||||||
|
Signed-off-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
|
Reviewed-by: Martin Kletzander <mkletzan@redhat.com>
|
||||||
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
|
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
|
||||||
|
(cherry picked from commit 88c1ee73d3231c74ff90bcfc084a7589670ec244)
|
||||||
|
---
|
||||||
|
hw/char/serial.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/char/serial.c b/hw/char/serial.c
|
||||||
|
index 6025592..2989ca2 100644
|
||||||
|
--- a/hw/char/serial.c
|
||||||
|
+++ b/hw/char/serial.c
|
||||||
|
@@ -224,7 +224,7 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
|
||||||
|
|
||||||
|
if (s->tsr_retry <= 0) {
|
||||||
|
if (s->fcr & UART_FCR_FE) {
|
||||||
|
- s->tsr = fifo8_is_full(&s->xmit_fifo) ?
|
||||||
|
+ s->tsr = fifo8_is_empty(&s->xmit_fifo) ?
|
||||||
|
0 : fifo8_pop(&s->xmit_fifo);
|
||||||
|
if (!s->xmit_fifo.num) {
|
||||||
|
s->lsr |= UART_LSR_THRE;
|
37
0208-char-serial-Fix-emptyness-handling.patch
Normal file
37
0208-char-serial-Fix-emptyness-handling.patch
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
From: Don Slutz <dslutz@verizon.com>
|
||||||
|
Date: Tue, 18 Mar 2014 12:29:34 -0400
|
||||||
|
Subject: [PATCH] char/serial: Fix emptyness handling
|
||||||
|
|
||||||
|
The commit 88c1ee73d3231c74ff90bcfc084a7589670ec244
|
||||||
|
char/serial: Fix emptyness check
|
||||||
|
|
||||||
|
Still causes extra NULL byte(s) to be sent.
|
||||||
|
|
||||||
|
So if the fifo is empty, do not send an extra NULL byte.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Crosthwaite <peter.crosthwaite@xilinx.com>
|
||||||
|
Signed-off-by: Don Slutz <dslutz@verizon.com>
|
||||||
|
Message-id: 1395160174-16006-1-git-send-email-dslutz@verizon.com
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit dffacd4654ec8bf2898aed230852154c6ed755ed)
|
||||||
|
---
|
||||||
|
hw/char/serial.c | 6 ++++--
|
||||||
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/char/serial.c b/hw/char/serial.c
|
||||||
|
index 2989ca2..6d10747 100644
|
||||||
|
--- a/hw/char/serial.c
|
||||||
|
+++ b/hw/char/serial.c
|
||||||
|
@@ -224,8 +224,10 @@ static gboolean serial_xmit(GIOChannel *chan, GIOCondition cond, void *opaque)
|
||||||
|
|
||||||
|
if (s->tsr_retry <= 0) {
|
||||||
|
if (s->fcr & UART_FCR_FE) {
|
||||||
|
- s->tsr = fifo8_is_empty(&s->xmit_fifo) ?
|
||||||
|
- 0 : fifo8_pop(&s->xmit_fifo);
|
||||||
|
+ if (fifo8_is_empty(&s->xmit_fifo)) {
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+ s->tsr = fifo8_pop(&s->xmit_fifo);
|
||||||
|
if (!s->xmit_fifo.num) {
|
||||||
|
s->lsr |= UART_LSR_THRE;
|
||||||
|
}
|
34
0209-vmstate-Add-uint32-2D-array-support.patch
Normal file
34
0209-vmstate-Add-uint32-2D-array-support.patch
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Fri, 20 Sep 2013 20:35:06 +0100
|
||||||
|
Subject: [PATCH] vmstate: Add uint32 2D-array support
|
||||||
|
|
||||||
|
Add support for saving VMState of 2D arrays of uint32 values.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit a1b1d277cdaac98f25be249e7819aac781a35530)
|
||||||
|
---
|
||||||
|
include/migration/vmstate.h | 6 ++++++
|
||||||
|
1 file changed, 6 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
|
||||||
|
index 1c31b5d..e5538c7 100644
|
||||||
|
--- a/include/migration/vmstate.h
|
||||||
|
+++ b/include/migration/vmstate.h
|
||||||
|
@@ -633,9 +633,15 @@ extern const VMStateInfo vmstate_info_bitmap;
|
||||||
|
#define VMSTATE_UINT32_ARRAY_V(_f, _s, _n, _v) \
|
||||||
|
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint32, uint32_t)
|
||||||
|
|
||||||
|
+#define VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, _v) \
|
||||||
|
+ VMSTATE_2DARRAY(_f, _s, _n1, _n2, _v, vmstate_info_uint32, uint32_t)
|
||||||
|
+
|
||||||
|
#define VMSTATE_UINT32_ARRAY(_f, _s, _n) \
|
||||||
|
VMSTATE_UINT32_ARRAY_V(_f, _s, _n, 0)
|
||||||
|
|
||||||
|
+#define VMSTATE_UINT32_2DARRAY(_f, _s, _n1, _n2) \
|
||||||
|
+ VMSTATE_UINT32_2DARRAY_V(_f, _s, _n1, _n2, 0)
|
||||||
|
+
|
||||||
|
#define VMSTATE_UINT64_ARRAY_V(_f, _s, _n, _v) \
|
||||||
|
VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_uint64, uint64_t)
|
||||||
|
|
312
0210-arm_gic-Extract-headers-hw-intc-arm_gic-_common-.h.patch
Normal file
312
0210-arm_gic-Extract-headers-hw-intc-arm_gic-_common-.h.patch
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
From: =?UTF-8?q?Andreas=20F=C3=A4rber?= <afaerber@suse.de>
|
||||||
|
Date: Tue, 23 Jul 2013 03:37:49 +0200
|
||||||
|
Subject: [PATCH] arm_gic: Extract headers hw/intc/arm_gic{,_common}.h
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/plain; charset=UTF-8
|
||||||
|
Content-Transfer-Encoding: 8bit
|
||||||
|
|
||||||
|
Rename NCPU to GIC_NCPU and move GICState away from gic_internal.h.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Andreas Färber <afaerber@suse.de>
|
||||||
|
(cherry picked from commit 83728796ad3f2ce7d6162c1cb894528b12915646)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic_common.c | 18 ++++----
|
||||||
|
hw/intc/gic_internal.h | 80 +---------------------------------
|
||||||
|
include/hw/intc/arm_gic.h | 42 ++++++++++++++++++
|
||||||
|
include/hw/intc/arm_gic_common.h | 92 ++++++++++++++++++++++++++++++++++++++++
|
||||||
|
4 files changed, 145 insertions(+), 87 deletions(-)
|
||||||
|
create mode 100644 include/hw/intc/arm_gic.h
|
||||||
|
create mode 100644 include/hw/intc/arm_gic_common.h
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
|
||||||
|
index 709b5c2..c765850 100644
|
||||||
|
--- a/hw/intc/arm_gic_common.c
|
||||||
|
+++ b/hw/intc/arm_gic_common.c
|
||||||
|
@@ -64,17 +64,17 @@ static const VMStateDescription vmstate_gic = {
|
||||||
|
.post_load = gic_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
VMSTATE_BOOL(enabled, GICState),
|
||||||
|
- VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, NCPU),
|
||||||
|
+ VMSTATE_BOOL_ARRAY(cpu_enabled, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1,
|
||||||
|
vmstate_gic_irq_state, gic_irq_state),
|
||||||
|
VMSTATE_UINT8_ARRAY(irq_target, GICState, GIC_MAXIRQ),
|
||||||
|
- VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, NCPU),
|
||||||
|
+ VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
|
||||||
|
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
|
||||||
|
- VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, NCPU),
|
||||||
|
- VMSTATE_UINT16_ARRAY(priority_mask, GICState, NCPU),
|
||||||
|
- VMSTATE_UINT16_ARRAY(running_irq, GICState, NCPU),
|
||||||
|
- VMSTATE_UINT16_ARRAY(running_priority, GICState, NCPU),
|
||||||
|
- VMSTATE_UINT16_ARRAY(current_pending, GICState, NCPU),
|
||||||
|
+ VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@@ -84,9 +84,9 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp)
|
||||||
|
GICState *s = ARM_GIC_COMMON(dev);
|
||||||
|
int num_irq = s->num_irq;
|
||||||
|
|
||||||
|
- if (s->num_cpu > NCPU) {
|
||||||
|
+ if (s->num_cpu > GIC_NCPU) {
|
||||||
|
error_setg(errp, "requested %u CPUs exceeds GIC maximum %d",
|
||||||
|
- s->num_cpu, NCPU);
|
||||||
|
+ s->num_cpu, GIC_NCPU);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s->num_irq += GIC_BASE_IRQ;
|
||||||
|
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
|
||||||
|
index 1426437..3989fd1 100644
|
||||||
|
--- a/hw/intc/gic_internal.h
|
||||||
|
+++ b/hw/intc/gic_internal.h
|
||||||
|
@@ -21,16 +21,9 @@
|
||||||
|
#ifndef QEMU_ARM_GIC_INTERNAL_H
|
||||||
|
#define QEMU_ARM_GIC_INTERNAL_H
|
||||||
|
|
||||||
|
-#include "hw/sysbus.h"
|
||||||
|
+#include "hw/intc/arm_gic.h"
|
||||||
|
|
||||||
|
-/* Maximum number of possible interrupts, determined by the GIC architecture */
|
||||||
|
-#define GIC_MAXIRQ 1020
|
||||||
|
-/* First 32 are private to each CPU (SGIs and PPIs). */
|
||||||
|
-#define GIC_INTERNAL 32
|
||||||
|
-/* Maximum number of possible CPU interfaces, determined by GIC architecture */
|
||||||
|
-#define NCPU 8
|
||||||
|
-
|
||||||
|
-#define ALL_CPU_MASK ((unsigned)(((1 << NCPU) - 1)))
|
||||||
|
+#define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1)))
|
||||||
|
|
||||||
|
/* The NVIC has 16 internal vectors. However these are not exposed
|
||||||
|
through the normal GIC interface. */
|
||||||
|
@@ -59,48 +52,6 @@
|
||||||
|
s->priority2[(irq) - GIC_INTERNAL])
|
||||||
|
#define GIC_TARGET(irq) s->irq_target[irq]
|
||||||
|
|
||||||
|
-typedef struct gic_irq_state {
|
||||||
|
- /* The enable bits are only banked for per-cpu interrupts. */
|
||||||
|
- uint8_t enabled;
|
||||||
|
- uint8_t pending;
|
||||||
|
- uint8_t active;
|
||||||
|
- uint8_t level;
|
||||||
|
- bool model; /* 0 = N:N, 1 = 1:N */
|
||||||
|
- bool trigger; /* nonzero = edge triggered. */
|
||||||
|
-} gic_irq_state;
|
||||||
|
-
|
||||||
|
-typedef struct GICState {
|
||||||
|
- /*< private >*/
|
||||||
|
- SysBusDevice parent_obj;
|
||||||
|
- /*< public >*/
|
||||||
|
-
|
||||||
|
- qemu_irq parent_irq[NCPU];
|
||||||
|
- bool enabled;
|
||||||
|
- bool cpu_enabled[NCPU];
|
||||||
|
-
|
||||||
|
- gic_irq_state irq_state[GIC_MAXIRQ];
|
||||||
|
- uint8_t irq_target[GIC_MAXIRQ];
|
||||||
|
- uint8_t priority1[GIC_INTERNAL][NCPU];
|
||||||
|
- uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
|
||||||
|
- uint16_t last_active[GIC_MAXIRQ][NCPU];
|
||||||
|
-
|
||||||
|
- uint16_t priority_mask[NCPU];
|
||||||
|
- uint16_t running_irq[NCPU];
|
||||||
|
- uint16_t running_priority[NCPU];
|
||||||
|
- uint16_t current_pending[NCPU];
|
||||||
|
-
|
||||||
|
- uint32_t num_cpu;
|
||||||
|
-
|
||||||
|
- MemoryRegion iomem; /* Distributor */
|
||||||
|
- /* This is just so we can have an opaque pointer which identifies
|
||||||
|
- * both this GIC and which CPU interface we should be accessing.
|
||||||
|
- */
|
||||||
|
- struct GICState *backref[NCPU];
|
||||||
|
- MemoryRegion cpuiomem[NCPU+1]; /* CPU interfaces */
|
||||||
|
- uint32_t num_irq;
|
||||||
|
- uint32_t revision;
|
||||||
|
-} GICState;
|
||||||
|
-
|
||||||
|
/* The special cases for the revision property: */
|
||||||
|
#define REV_11MPCORE 0
|
||||||
|
#define REV_NVIC 0xffffffff
|
||||||
|
@@ -111,31 +62,4 @@ void gic_complete_irq(GICState *s, int cpu, int irq);
|
||||||
|
void gic_update(GICState *s);
|
||||||
|
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
|
||||||
|
|
||||||
|
-#define TYPE_ARM_GIC_COMMON "arm_gic_common"
|
||||||
|
-#define ARM_GIC_COMMON(obj) \
|
||||||
|
- OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC_COMMON)
|
||||||
|
-#define ARM_GIC_COMMON_CLASS(klass) \
|
||||||
|
- OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON)
|
||||||
|
-#define ARM_GIC_COMMON_GET_CLASS(obj) \
|
||||||
|
- OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON)
|
||||||
|
-
|
||||||
|
-typedef struct ARMGICCommonClass {
|
||||||
|
- SysBusDeviceClass parent_class;
|
||||||
|
- void (*pre_save)(GICState *s);
|
||||||
|
- void (*post_load)(GICState *s);
|
||||||
|
-} ARMGICCommonClass;
|
||||||
|
-
|
||||||
|
-#define TYPE_ARM_GIC "arm_gic"
|
||||||
|
-#define ARM_GIC(obj) \
|
||||||
|
- OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC)
|
||||||
|
-#define ARM_GIC_CLASS(klass) \
|
||||||
|
- OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC)
|
||||||
|
-#define ARM_GIC_GET_CLASS(obj) \
|
||||||
|
- OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC)
|
||||||
|
-
|
||||||
|
-typedef struct ARMGICClass {
|
||||||
|
- ARMGICCommonClass parent_class;
|
||||||
|
- DeviceRealize parent_realize;
|
||||||
|
-} ARMGICClass;
|
||||||
|
-
|
||||||
|
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
|
||||||
|
diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..0971e37
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/hw/intc/arm_gic.h
|
||||||
|
@@ -0,0 +1,42 @@
|
||||||
|
+/*
|
||||||
|
+ * ARM GIC support
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2012 Linaro Limited
|
||||||
|
+ * Written by Peter Maydell
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License as published by
|
||||||
|
+ * the Free Software Foundation, either version 2 of the License, or
|
||||||
|
+ * (at your option) any later version.
|
||||||
|
+ *
|
||||||
|
+ * This program is distributed in the hope that it will be useful,
|
||||||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
+ * GNU General Public License for more details.
|
||||||
|
+ *
|
||||||
|
+ * You should have received a copy of the GNU General Public License along
|
||||||
|
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef HW_ARM_GIC_H
|
||||||
|
+#define HW_ARM_GIC_H
|
||||||
|
+
|
||||||
|
+#include "arm_gic_common.h"
|
||||||
|
+
|
||||||
|
+#define TYPE_ARM_GIC "arm_gic"
|
||||||
|
+#define ARM_GIC(obj) \
|
||||||
|
+ OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC)
|
||||||
|
+#define ARM_GIC_CLASS(klass) \
|
||||||
|
+ OBJECT_CLASS_CHECK(ARMGICClass, (klass), TYPE_ARM_GIC)
|
||||||
|
+#define ARM_GIC_GET_CLASS(obj) \
|
||||||
|
+ OBJECT_GET_CLASS(ARMGICClass, (obj), TYPE_ARM_GIC)
|
||||||
|
+
|
||||||
|
+typedef struct ARMGICClass {
|
||||||
|
+ /*< private >*/
|
||||||
|
+ ARMGICCommonClass parent_class;
|
||||||
|
+ /*< public >*/
|
||||||
|
+
|
||||||
|
+ DeviceRealize parent_realize;
|
||||||
|
+} ARMGICClass;
|
||||||
|
+
|
||||||
|
+#endif
|
||||||
|
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..4f381bd
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/hw/intc/arm_gic_common.h
|
||||||
|
@@ -0,0 +1,92 @@
|
||||||
|
+/*
|
||||||
|
+ * ARM GIC support
|
||||||
|
+ *
|
||||||
|
+ * Copyright (c) 2012 Linaro Limited
|
||||||
|
+ * Written by Peter Maydell
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify
|
||||||
|
+ * it under the terms of the GNU General Public License as published by
|
||||||
|
+ * the Free Software Foundation, either version 2 of the License, or
|
||||||
|
+ * (at your option) any later version.
|
||||||
|
+ *
|
||||||
|
+ * This program is distributed in the hope that it will be useful,
|
||||||
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
+ * GNU General Public License for more details.
|
||||||
|
+ *
|
||||||
|
+ * You should have received a copy of the GNU General Public License along
|
||||||
|
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef HW_ARM_GIC_COMMON_H
|
||||||
|
+#define HW_ARM_GIC_COMMON_H
|
||||||
|
+
|
||||||
|
+#include "hw/sysbus.h"
|
||||||
|
+
|
||||||
|
+/* Maximum number of possible interrupts, determined by the GIC architecture */
|
||||||
|
+#define GIC_MAXIRQ 1020
|
||||||
|
+/* First 32 are private to each CPU (SGIs and PPIs). */
|
||||||
|
+#define GIC_INTERNAL 32
|
||||||
|
+/* Maximum number of possible CPU interfaces, determined by GIC architecture */
|
||||||
|
+#define GIC_NCPU 8
|
||||||
|
+
|
||||||
|
+typedef struct gic_irq_state {
|
||||||
|
+ /* The enable bits are only banked for per-cpu interrupts. */
|
||||||
|
+ uint8_t enabled;
|
||||||
|
+ uint8_t pending;
|
||||||
|
+ uint8_t active;
|
||||||
|
+ uint8_t level;
|
||||||
|
+ bool model; /* 0 = N:N, 1 = 1:N */
|
||||||
|
+ bool trigger; /* nonzero = edge triggered. */
|
||||||
|
+} gic_irq_state;
|
||||||
|
+
|
||||||
|
+typedef struct GICState {
|
||||||
|
+ /*< private >*/
|
||||||
|
+ SysBusDevice parent_obj;
|
||||||
|
+ /*< public >*/
|
||||||
|
+
|
||||||
|
+ qemu_irq parent_irq[GIC_NCPU];
|
||||||
|
+ bool enabled;
|
||||||
|
+ bool cpu_enabled[GIC_NCPU];
|
||||||
|
+
|
||||||
|
+ gic_irq_state irq_state[GIC_MAXIRQ];
|
||||||
|
+ uint8_t irq_target[GIC_MAXIRQ];
|
||||||
|
+ uint8_t priority1[GIC_INTERNAL][GIC_NCPU];
|
||||||
|
+ uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
|
||||||
|
+ uint16_t last_active[GIC_MAXIRQ][GIC_NCPU];
|
||||||
|
+
|
||||||
|
+ uint16_t priority_mask[GIC_NCPU];
|
||||||
|
+ uint16_t running_irq[GIC_NCPU];
|
||||||
|
+ uint16_t running_priority[GIC_NCPU];
|
||||||
|
+ uint16_t current_pending[GIC_NCPU];
|
||||||
|
+
|
||||||
|
+ uint32_t num_cpu;
|
||||||
|
+
|
||||||
|
+ MemoryRegion iomem; /* Distributor */
|
||||||
|
+ /* This is just so we can have an opaque pointer which identifies
|
||||||
|
+ * both this GIC and which CPU interface we should be accessing.
|
||||||
|
+ */
|
||||||
|
+ struct GICState *backref[GIC_NCPU];
|
||||||
|
+ MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */
|
||||||
|
+ uint32_t num_irq;
|
||||||
|
+ uint32_t revision;
|
||||||
|
+} GICState;
|
||||||
|
+
|
||||||
|
+#define TYPE_ARM_GIC_COMMON "arm_gic_common"
|
||||||
|
+#define ARM_GIC_COMMON(obj) \
|
||||||
|
+ OBJECT_CHECK(GICState, (obj), TYPE_ARM_GIC_COMMON)
|
||||||
|
+#define ARM_GIC_COMMON_CLASS(klass) \
|
||||||
|
+ OBJECT_CLASS_CHECK(ARMGICCommonClass, (klass), TYPE_ARM_GIC_COMMON)
|
||||||
|
+#define ARM_GIC_COMMON_GET_CLASS(obj) \
|
||||||
|
+ OBJECT_GET_CLASS(ARMGICCommonClass, (obj), TYPE_ARM_GIC_COMMON)
|
||||||
|
+
|
||||||
|
+typedef struct ARMGICCommonClass {
|
||||||
|
+ /*< private >*/
|
||||||
|
+ SysBusDeviceClass parent_class;
|
||||||
|
+ /*< public >*/
|
||||||
|
+
|
||||||
|
+ void (*pre_save)(GICState *s);
|
||||||
|
+ void (*post_load)(GICState *s);
|
||||||
|
+} ARMGICCommonClass;
|
||||||
|
+
|
||||||
|
+#endif
|
124
0211-arm_gic-Rename-GIC_X_TRIGGER-to-GIC_X_EDGE_TRIGGER.patch
Normal file
124
0211-arm_gic-Rename-GIC_X_TRIGGER-to-GIC_X_EDGE_TRIGGER.patch
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Fri, 20 Dec 2013 22:09:32 -0800
|
||||||
|
Subject: [PATCH] arm_gic: Rename GIC_X_TRIGGER to GIC_X_EDGE_TRIGGER
|
||||||
|
|
||||||
|
TRIGGER can really mean mean anything (e.g. was it triggered, is it
|
||||||
|
level-triggered, is it edge-triggered, etc.). Rename to EDGE_TRIGGER to
|
||||||
|
make the code comprehensible without looking up the data structure.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Message-id: 1387606179-22709-2-git-send-email-christoffer.dall@linaro.org
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 04050c5c6aa6f9c086a63a30b182b996fb2d3d02)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 12 ++++++------
|
||||||
|
hw/intc/arm_gic_common.c | 4 ++--
|
||||||
|
hw/intc/gic_internal.h | 6 +++---
|
||||||
|
include/hw/intc/arm_gic_common.h | 2 +-
|
||||||
|
4 files changed, 12 insertions(+), 12 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index d431b7a..27c258a 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -128,7 +128,7 @@ static void gic_set_irq(void *opaque, int irq, int level)
|
||||||
|
|
||||||
|
if (level) {
|
||||||
|
GIC_SET_LEVEL(irq, cm);
|
||||||
|
- if (GIC_TEST_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
||||||
|
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
||||||
|
DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||||
|
GIC_SET_PENDING(irq, target);
|
||||||
|
}
|
||||||
|
@@ -188,7 +188,7 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
|
||||||
|
return; /* No active IRQ. */
|
||||||
|
/* Mark level triggered interrupts as pending if they are still
|
||||||
|
raised. */
|
||||||
|
- if (!GIC_TEST_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
||||||
|
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
||||||
|
&& GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
||||||
|
DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||||
|
GIC_SET_PENDING(irq, cm);
|
||||||
|
@@ -311,7 +311,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
if (GIC_TEST_MODEL(irq + i))
|
||||||
|
res |= (1 << (i * 2));
|
||||||
|
- if (GIC_TEST_TRIGGER(irq + i))
|
||||||
|
+ if (GIC_TEST_EDGE_TRIGGER(irq + i))
|
||||||
|
res |= (2 << (i * 2));
|
||||||
|
}
|
||||||
|
} else if (offset < 0xfe0) {
|
||||||
|
@@ -386,7 +386,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
/* If a raised level triggered IRQ enabled then mark
|
||||||
|
is as pending. */
|
||||||
|
if (GIC_TEST_LEVEL(irq + i, mask)
|
||||||
|
- && !GIC_TEST_TRIGGER(irq + i)) {
|
||||||
|
+ && !GIC_TEST_EDGE_TRIGGER(irq + i)) {
|
||||||
|
DPRINTF("Set %d pending mask %x\n", irq + i, mask);
|
||||||
|
GIC_SET_PENDING(irq + i, mask);
|
||||||
|
}
|
||||||
|
@@ -478,9 +478,9 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
GIC_CLEAR_MODEL(irq + i);
|
||||||
|
}
|
||||||
|
if (value & (2 << (i * 2))) {
|
||||||
|
- GIC_SET_TRIGGER(irq + i);
|
||||||
|
+ GIC_SET_EDGE_TRIGGER(irq + i);
|
||||||
|
} else {
|
||||||
|
- GIC_CLEAR_TRIGGER(irq + i);
|
||||||
|
+ GIC_CLEAR_EDGE_TRIGGER(irq + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
|
||||||
|
index c765850..710607b 100644
|
||||||
|
--- a/hw/intc/arm_gic_common.c
|
||||||
|
+++ b/hw/intc/arm_gic_common.c
|
||||||
|
@@ -51,7 +51,7 @@ static const VMStateDescription vmstate_gic_irq_state = {
|
||||||
|
VMSTATE_UINT8(active, gic_irq_state),
|
||||||
|
VMSTATE_UINT8(level, gic_irq_state),
|
||||||
|
VMSTATE_BOOL(model, gic_irq_state),
|
||||||
|
- VMSTATE_BOOL(trigger, gic_irq_state),
|
||||||
|
+ VMSTATE_BOOL(edge_trigger, gic_irq_state),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@@ -126,7 +126,7 @@ static void arm_gic_common_reset(DeviceState *dev)
|
||||||
|
}
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
GIC_SET_ENABLED(i, ALL_CPU_MASK);
|
||||||
|
- GIC_SET_TRIGGER(i);
|
||||||
|
+ GIC_SET_EDGE_TRIGGER(i);
|
||||||
|
}
|
||||||
|
if (s->num_cpu == 1) {
|
||||||
|
/* For uniprocessor GICs all interrupts always target the sole CPU */
|
||||||
|
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
|
||||||
|
index 3989fd1..efac78d 100644
|
||||||
|
--- a/hw/intc/gic_internal.h
|
||||||
|
+++ b/hw/intc/gic_internal.h
|
||||||
|
@@ -44,9 +44,9 @@
|
||||||
|
#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm)
|
||||||
|
#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm)
|
||||||
|
#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0)
|
||||||
|
-#define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = true
|
||||||
|
-#define GIC_CLEAR_TRIGGER(irq) s->irq_state[irq].trigger = false
|
||||||
|
-#define GIC_TEST_TRIGGER(irq) s->irq_state[irq].trigger
|
||||||
|
+#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true
|
||||||
|
+#define GIC_CLEAR_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = false
|
||||||
|
+#define GIC_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger)
|
||||||
|
#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \
|
||||||
|
s->priority1[irq][cpu] : \
|
||||||
|
s->priority2[(irq) - GIC_INTERNAL])
|
||||||
|
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
|
||||||
|
index 4f381bd..0d232df 100644
|
||||||
|
--- a/include/hw/intc/arm_gic_common.h
|
||||||
|
+++ b/include/hw/intc/arm_gic_common.h
|
||||||
|
@@ -37,7 +37,7 @@ typedef struct gic_irq_state {
|
||||||
|
uint8_t active;
|
||||||
|
uint8_t level;
|
||||||
|
bool model; /* 0 = N:N, 1 = 1:N */
|
||||||
|
- bool trigger; /* nonzero = edge triggered. */
|
||||||
|
+ bool edge_trigger; /* true: edge-triggered, false: level-triggered */
|
||||||
|
} gic_irq_state;
|
||||||
|
|
||||||
|
typedef struct GICState {
|
62
0212-hw-arm_gic-Introduce-gic_set_priority-function.patch
Normal file
62
0212-hw-arm_gic-Introduce-gic_set_priority-function.patch
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Fri, 20 Dec 2013 22:09:33 -0800
|
||||||
|
Subject: [PATCH] hw: arm_gic: Introduce gic_set_priority function
|
||||||
|
|
||||||
|
To make the code slightly cleaner to look at and make the save/restore
|
||||||
|
code easier to understand, introduce this function to set the priority of
|
||||||
|
interrupts.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Message-id: 1387606179-22709-3-git-send-email-christoffer.dall@linaro.org
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 9df90ad078ec782d1339bd6879b6ea117f9759f7)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 15 ++++++++++-----
|
||||||
|
hw/intc/gic_internal.h | 1 +
|
||||||
|
2 files changed, 11 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 27c258a..6c59650 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -168,6 +168,15 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu)
|
||||||
|
return new_irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
|
||||||
|
+{
|
||||||
|
+ if (irq < GIC_INTERNAL) {
|
||||||
|
+ s->priority1[irq][cpu] = val;
|
||||||
|
+ } else {
|
||||||
|
+ s->priority2[(irq) - GIC_INTERNAL] = val;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
void gic_complete_irq(GICState *s, int cpu, int irq)
|
||||||
|
{
|
||||||
|
int update = 0;
|
||||||
|
@@ -443,11 +452,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
irq = (offset - 0x400) + GIC_BASE_IRQ;
|
||||||
|
if (irq >= s->num_irq)
|
||||||
|
goto bad_reg;
|
||||||
|
- if (irq < GIC_INTERNAL) {
|
||||||
|
- s->priority1[irq][cpu] = value;
|
||||||
|
- } else {
|
||||||
|
- s->priority2[irq - GIC_INTERNAL] = value;
|
||||||
|
- }
|
||||||
|
+ gic_set_priority(s, cpu, irq, value);
|
||||||
|
} else if (offset < 0xc00) {
|
||||||
|
/* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the
|
||||||
|
* annoying exception of the 11MPCore's GIC.
|
||||||
|
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
|
||||||
|
index efac78d..8c02d58 100644
|
||||||
|
--- a/hw/intc/gic_internal.h
|
||||||
|
+++ b/hw/intc/gic_internal.h
|
||||||
|
@@ -61,5 +61,6 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu);
|
||||||
|
void gic_complete_irq(GICState *s, int cpu, int irq);
|
||||||
|
void gic_update(GICState *s);
|
||||||
|
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
|
||||||
|
+void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
|
||||||
|
|
||||||
|
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
|
70
0213-arm_gic-Introduce-define-for-GIC_NR_SGIS.patch
Normal file
70
0213-arm_gic-Introduce-define-for-GIC_NR_SGIS.patch
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Fri, 31 Jan 2014 14:47:38 +0000
|
||||||
|
Subject: [PATCH] arm_gic: Introduce define for GIC_NR_SGIS
|
||||||
|
|
||||||
|
Instead of hardcoding 16 various places in the code, use a define to
|
||||||
|
make it more clear what is going on.
|
||||||
|
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 41ab7b55108e2699e7c2e77788465cb52a0b2c08)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 17 +++++++++++------
|
||||||
|
include/hw/intc/arm_gic_common.h | 1 +
|
||||||
|
2 files changed, 12 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 6c59650..0ce11ac 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -380,8 +380,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
irq = (offset - 0x100) * 8 + GIC_BASE_IRQ;
|
||||||
|
if (irq >= s->num_irq)
|
||||||
|
goto bad_reg;
|
||||||
|
- if (irq < 16)
|
||||||
|
- value = 0xff;
|
||||||
|
+ if (irq < GIC_NR_SGIS) {
|
||||||
|
+ value = 0xff;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (value & (1 << i)) {
|
||||||
|
int mask =
|
||||||
|
@@ -406,8 +408,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
irq = (offset - 0x180) * 8 + GIC_BASE_IRQ;
|
||||||
|
if (irq >= s->num_irq)
|
||||||
|
goto bad_reg;
|
||||||
|
- if (irq < 16)
|
||||||
|
- value = 0;
|
||||||
|
+ if (irq < GIC_NR_SGIS) {
|
||||||
|
+ value = 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (value & (1 << i)) {
|
||||||
|
int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK;
|
||||||
|
@@ -423,8 +427,9 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
irq = (offset - 0x200) * 8 + GIC_BASE_IRQ;
|
||||||
|
if (irq >= s->num_irq)
|
||||||
|
goto bad_reg;
|
||||||
|
- if (irq < 16)
|
||||||
|
- irq = 0;
|
||||||
|
+ if (irq < GIC_NR_SGIS) {
|
||||||
|
+ irq = 0;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
if (value & (1 << i)) {
|
||||||
|
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
|
||||||
|
index 0d232df..8a2aa00 100644
|
||||||
|
--- a/include/hw/intc/arm_gic_common.h
|
||||||
|
+++ b/include/hw/intc/arm_gic_common.h
|
||||||
|
@@ -27,6 +27,7 @@
|
||||||
|
#define GIC_MAXIRQ 1020
|
||||||
|
/* First 32 are private to each CPU (SGIs and PPIs). */
|
||||||
|
#define GIC_INTERNAL 32
|
||||||
|
+#define GIC_NR_SGIS 16
|
||||||
|
/* Maximum number of possible CPU interfaces, determined by GIC architecture */
|
||||||
|
#define GIC_NCPU 8
|
||||||
|
|
39
0214-arm_gic-Fix-GICD_ICPENDR-and-GICD_ISPENDR-writes.patch
Normal file
39
0214-arm_gic-Fix-GICD_ICPENDR-and-GICD_ISPENDR-writes.patch
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Fri, 31 Jan 2014 14:47:38 +0000
|
||||||
|
Subject: [PATCH] arm_gic: Fix GICD_ICPENDR and GICD_ISPENDR writes
|
||||||
|
|
||||||
|
Fix two bugs that would allow changing the state of SGIs through the
|
||||||
|
ICPENDR and ISPENDRs.
|
||||||
|
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 5b0adce156216fb24dcc5f1683e8b686f3793fff)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 6 +++++-
|
||||||
|
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 0ce11ac..62153fd 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -428,7 +428,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
if (irq >= s->num_irq)
|
||||||
|
goto bad_reg;
|
||||||
|
if (irq < GIC_NR_SGIS) {
|
||||||
|
- irq = 0;
|
||||||
|
+ value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
@@ -441,6 +441,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
irq = (offset - 0x280) * 8 + GIC_BASE_IRQ;
|
||||||
|
if (irq >= s->num_irq)
|
||||||
|
goto bad_reg;
|
||||||
|
+ if (irq < GIC_NR_SGIS) {
|
||||||
|
+ value = 0;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
/* ??? This currently clears the pending bit for all CPUs, even
|
||||||
|
for per-CPU interrupts. It's unclear whether this is the
|
196
0215-arm_gic-Fix-GIC-pending-behavior.patch
Normal file
196
0215-arm_gic-Fix-GIC-pending-behavior.patch
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Mon, 18 Nov 2013 20:32:00 -0800
|
||||||
|
Subject: [PATCH] arm_gic: Fix GIC pending behavior
|
||||||
|
|
||||||
|
The existing implementation of the pending behavior in gic_set_irq,
|
||||||
|
gic_complete_irq, and the distributor pending set/clear registers does
|
||||||
|
not follow the semantics of the GICv2.0 specs, but may implement the
|
||||||
|
11MPCore support. Therefore, maintain the existing semantics for
|
||||||
|
11MPCore and v7M NVIC and change the behavior to be in accordance with
|
||||||
|
the GICv2.0 specs for "generic implementations" (s->revision == 1 ||
|
||||||
|
s->revision == 2).
|
||||||
|
|
||||||
|
Generic implementations distinguish between setting a level-triggered
|
||||||
|
interrupt pending through writes to the GICD_ISPENDR and when hardware
|
||||||
|
raises the interrupt line. Writing to the GICD_ICPENDR will not cause
|
||||||
|
the interrupt to become non-pending if the line is still active, and
|
||||||
|
conversely, if the line is deactivated but the interrupt is marked as
|
||||||
|
pending through a write to GICD_ISPENDR, the interrupt remains pending.
|
||||||
|
Handle this situation in the GIC_TEST_PENDING (which now becomes a
|
||||||
|
static inline named gic_test_pending) and let the 'pending' field
|
||||||
|
correspond only to the latched state of the D-flip flop in the GICv2.0
|
||||||
|
specs Figure 4-10.
|
||||||
|
|
||||||
|
The following changes are added:
|
||||||
|
|
||||||
|
gic_test_pending:
|
||||||
|
Make this a static inline and split out the 11MPCore from the generic
|
||||||
|
behavior. For the generic behavior, consider interrupts pending if:
|
||||||
|
((s->irq_state[irq].pending & (cm) != 0) ||
|
||||||
|
(!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm))
|
||||||
|
|
||||||
|
gic_set_irq:
|
||||||
|
Split out the 11MPCore from the generic behavior. For the generic
|
||||||
|
behavior, always GIC_SET_LEVEL() on positive level, but only
|
||||||
|
GIC_SET_PENDING for edge-triggered interrupts and always simply
|
||||||
|
GIC_CLEAR_LEVEL() on negative level.
|
||||||
|
|
||||||
|
gic_complete_irq:
|
||||||
|
Only resample the line for line-triggered interrupts on an 11MPCore.
|
||||||
|
Generic implementations will sample the line directly in
|
||||||
|
gic_test_pending().
|
||||||
|
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 8d999995e45c1002aa11f269c98f2e93e6f8c42a)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 64 ++++++++++++++++++++++++++++++++++++--------------
|
||||||
|
hw/intc/gic_internal.h | 16 ++++++++++++-
|
||||||
|
2 files changed, 62 insertions(+), 18 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 62153fd..11fe3c4 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -66,7 +66,7 @@ void gic_update(GICState *s)
|
||||||
|
best_prio = 0x100;
|
||||||
|
best_irq = 1023;
|
||||||
|
for (irq = 0; irq < s->num_irq; irq++) {
|
||||||
|
- if (GIC_TEST_ENABLED(irq, cm) && GIC_TEST_PENDING(irq, cm)) {
|
||||||
|
+ if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm)) {
|
||||||
|
if (GIC_GET_PRIORITY(irq, cpu) < best_prio) {
|
||||||
|
best_prio = GIC_GET_PRIORITY(irq, cpu);
|
||||||
|
best_irq = irq;
|
||||||
|
@@ -89,14 +89,43 @@ void gic_set_pending_private(GICState *s, int cpu, int irq)
|
||||||
|
{
|
||||||
|
int cm = 1 << cpu;
|
||||||
|
|
||||||
|
- if (GIC_TEST_PENDING(irq, cm))
|
||||||
|
+ if (gic_test_pending(s, irq, cm)) {
|
||||||
|
return;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
DPRINTF("Set %d pending cpu %d\n", irq, cpu);
|
||||||
|
GIC_SET_PENDING(irq, cm);
|
||||||
|
gic_update(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static void gic_set_irq_11mpcore(GICState *s, int irq, int level,
|
||||||
|
+ int cm, int target)
|
||||||
|
+{
|
||||||
|
+ if (level) {
|
||||||
|
+ GIC_SET_LEVEL(irq, cm);
|
||||||
|
+ if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
||||||
|
+ DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||||
|
+ GIC_SET_PENDING(irq, target);
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ GIC_CLEAR_LEVEL(irq, cm);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void gic_set_irq_generic(GICState *s, int irq, int level,
|
||||||
|
+ int cm, int target)
|
||||||
|
+{
|
||||||
|
+ if (level) {
|
||||||
|
+ GIC_SET_LEVEL(irq, cm);
|
||||||
|
+ DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||||
|
+ if (GIC_TEST_EDGE_TRIGGER(irq)) {
|
||||||
|
+ GIC_SET_PENDING(irq, target);
|
||||||
|
+ }
|
||||||
|
+ } else {
|
||||||
|
+ GIC_CLEAR_LEVEL(irq, cm);
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/* Process a change in an external IRQ input. */
|
||||||
|
static void gic_set_irq(void *opaque, int irq, int level)
|
||||||
|
{
|
||||||
|
@@ -126,15 +155,12 @@ static void gic_set_irq(void *opaque, int irq, int level)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (level) {
|
||||||
|
- GIC_SET_LEVEL(irq, cm);
|
||||||
|
- if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) {
|
||||||
|
- DPRINTF("Set %d pending mask %x\n", irq, target);
|
||||||
|
- GIC_SET_PENDING(irq, target);
|
||||||
|
- }
|
||||||
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||||
|
+ gic_set_irq_11mpcore(s, irq, level, cm, target);
|
||||||
|
} else {
|
||||||
|
- GIC_CLEAR_LEVEL(irq, cm);
|
||||||
|
+ gic_set_irq_generic(s, irq, level, cm, target);
|
||||||
|
}
|
||||||
|
+
|
||||||
|
gic_update(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -195,14 +221,18 @@ void gic_complete_irq(GICState *s, int cpu, int irq)
|
||||||
|
}
|
||||||
|
if (s->running_irq[cpu] == 1023)
|
||||||
|
return; /* No active IRQ. */
|
||||||
|
- /* Mark level triggered interrupts as pending if they are still
|
||||||
|
- raised. */
|
||||||
|
- if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
||||||
|
- && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
||||||
|
- DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||||
|
- GIC_SET_PENDING(irq, cm);
|
||||||
|
- update = 1;
|
||||||
|
+
|
||||||
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||||
|
+ /* Mark level triggered interrupts as pending if they are still
|
||||||
|
+ raised. */
|
||||||
|
+ if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm)
|
||||||
|
+ && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) {
|
||||||
|
+ DPRINTF("Set %d pending mask %x\n", irq, cm);
|
||||||
|
+ GIC_SET_PENDING(irq, cm);
|
||||||
|
+ update = 1;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
+
|
||||||
|
if (irq != s->running_irq[cpu]) {
|
||||||
|
/* Complete an IRQ that is not currently running. */
|
||||||
|
int tmp = s->running_irq[cpu];
|
||||||
|
@@ -273,7 +303,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
|
||||||
|
res = 0;
|
||||||
|
mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK;
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
- if (GIC_TEST_PENDING(irq + i, mask)) {
|
||||||
|
+ if (gic_test_pending(s, irq + i, mask)) {
|
||||||
|
res |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h
|
||||||
|
index 8c02d58..92a6f7a 100644
|
||||||
|
--- a/hw/intc/gic_internal.h
|
||||||
|
+++ b/hw/intc/gic_internal.h
|
||||||
|
@@ -34,7 +34,6 @@
|
||||||
|
#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0)
|
||||||
|
#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm)
|
||||||
|
#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm)
|
||||||
|
-#define GIC_TEST_PENDING(irq, cm) ((s->irq_state[irq].pending & (cm)) != 0)
|
||||||
|
#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm)
|
||||||
|
#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm)
|
||||||
|
#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0)
|
||||||
|
@@ -63,4 +62,19 @@ void gic_update(GICState *s);
|
||||||
|
void gic_init_irqs_and_distributor(GICState *s, int num_irq);
|
||||||
|
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val);
|
||||||
|
|
||||||
|
+static inline bool gic_test_pending(GICState *s, int irq, int cm)
|
||||||
|
+{
|
||||||
|
+ if (s->revision == REV_NVIC || s->revision == REV_11MPCORE) {
|
||||||
|
+ return s->irq_state[irq].pending & cm;
|
||||||
|
+ } else {
|
||||||
|
+ /* Edge-triggered interrupts are marked pending on a rising edge, but
|
||||||
|
+ * level-triggered interrupts are either considered pending when the
|
||||||
|
+ * level is active or if software has explicitly written to
|
||||||
|
+ * GICD_ISPENDR to set the state pending.
|
||||||
|
+ */
|
||||||
|
+ return (s->irq_state[irq].pending & cm) ||
|
||||||
|
+ (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm));
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#endif /* !QEMU_ARM_GIC_INTERNAL_H */
|
217
0216-arm_gic-Keep-track-of-SGI-sources.patch
Normal file
217
0216-arm_gic-Keep-track-of-SGI-sources.patch
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Mon, 18 Nov 2013 20:32:00 -0800
|
||||||
|
Subject: [PATCH] arm_gic: Keep track of SGI sources
|
||||||
|
|
||||||
|
Right now the arm gic emulation doesn't keep track of the source of an
|
||||||
|
SGI (which apparently Linux guests don't use, or they're fine with
|
||||||
|
assuming CPU 0 always).
|
||||||
|
|
||||||
|
Add the necessary matrix on the GICState structure and maintain the data
|
||||||
|
when setting and clearing the pending state of an IRQ and make the state
|
||||||
|
visible to the guest.
|
||||||
|
|
||||||
|
Note that we always choose to present the source as the lowest-numbered
|
||||||
|
CPU in case multiple cores have signalled the same SGI number to a core
|
||||||
|
on the system.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit 40d225009efe17cad647b4b7424b77a3ace232f1)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 98 +++++++++++++++++++++++++++++++++++-----
|
||||||
|
hw/intc/arm_gic_common.c | 5 +-
|
||||||
|
include/hw/intc/arm_gic_common.h | 7 +++
|
||||||
|
3 files changed, 96 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 11fe3c4..29f98be 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -151,6 +151,8 @@ static void gic_set_irq(void *opaque, int irq, int level)
|
||||||
|
target = cm;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ assert(irq >= GIC_NR_SGIS);
|
||||||
|
+
|
||||||
|
if (level == GIC_TEST_LEVEL(irq, cm)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
@@ -177,21 +179,48 @@ static void gic_set_running_irq(GICState *s, int cpu, int irq)
|
||||||
|
|
||||||
|
uint32_t gic_acknowledge_irq(GICState *s, int cpu)
|
||||||
|
{
|
||||||
|
- int new_irq;
|
||||||
|
+ int ret, irq, src;
|
||||||
|
int cm = 1 << cpu;
|
||||||
|
- new_irq = s->current_pending[cpu];
|
||||||
|
- if (new_irq == 1023
|
||||||
|
- || GIC_GET_PRIORITY(new_irq, cpu) >= s->running_priority[cpu]) {
|
||||||
|
+ irq = s->current_pending[cpu];
|
||||||
|
+ if (irq == 1023
|
||||||
|
+ || GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) {
|
||||||
|
DPRINTF("ACK no pending IRQ\n");
|
||||||
|
return 1023;
|
||||||
|
}
|
||||||
|
- s->last_active[new_irq][cpu] = s->running_irq[cpu];
|
||||||
|
- /* Clear pending flags for both level and edge triggered interrupts.
|
||||||
|
- Level triggered IRQs will be reasserted once they become inactive. */
|
||||||
|
- GIC_CLEAR_PENDING(new_irq, GIC_TEST_MODEL(new_irq) ? ALL_CPU_MASK : cm);
|
||||||
|
- gic_set_running_irq(s, cpu, new_irq);
|
||||||
|
- DPRINTF("ACK %d\n", new_irq);
|
||||||
|
- return new_irq;
|
||||||
|
+ s->last_active[irq][cpu] = s->running_irq[cpu];
|
||||||
|
+
|
||||||
|
+ if (s->revision == REV_11MPCORE) {
|
||||||
|
+ /* Clear pending flags for both level and edge triggered interrupts.
|
||||||
|
+ * Level triggered IRQs will be reasserted once they become inactive.
|
||||||
|
+ */
|
||||||
|
+ GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
|
||||||
|
+ ret = irq;
|
||||||
|
+ } else {
|
||||||
|
+ if (irq < GIC_NR_SGIS) {
|
||||||
|
+ /* Lookup the source CPU for the SGI and clear this in the
|
||||||
|
+ * sgi_pending map. Return the src and clear the overall pending
|
||||||
|
+ * state on this CPU if the SGI is not pending from any CPUs.
|
||||||
|
+ */
|
||||||
|
+ assert(s->sgi_pending[irq][cpu] != 0);
|
||||||
|
+ src = ctz32(s->sgi_pending[irq][cpu]);
|
||||||
|
+ s->sgi_pending[irq][cpu] &= ~(1 << src);
|
||||||
|
+ if (s->sgi_pending[irq][cpu] == 0) {
|
||||||
|
+ GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
|
||||||
|
+ }
|
||||||
|
+ ret = irq | ((src & 0x7) << 10);
|
||||||
|
+ } else {
|
||||||
|
+ /* Clear pending state for both level and edge triggered
|
||||||
|
+ * interrupts. (level triggered interrupts with an active line
|
||||||
|
+ * remain pending, see gic_test_pending)
|
||||||
|
+ */
|
||||||
|
+ GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm);
|
||||||
|
+ ret = irq;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ gic_set_running_irq(s, cpu, irq);
|
||||||
|
+ DPRINTF("ACK %d\n", irq);
|
||||||
|
+ return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val)
|
||||||
|
@@ -353,6 +382,22 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset)
|
||||||
|
if (GIC_TEST_EDGE_TRIGGER(irq + i))
|
||||||
|
res |= (2 << (i * 2));
|
||||||
|
}
|
||||||
|
+ } else if (offset < 0xf10) {
|
||||||
|
+ goto bad_reg;
|
||||||
|
+ } else if (offset < 0xf30) {
|
||||||
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||||
|
+ goto bad_reg;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (offset < 0xf20) {
|
||||||
|
+ /* GICD_CPENDSGIRn */
|
||||||
|
+ irq = (offset - 0xf10);
|
||||||
|
+ } else {
|
||||||
|
+ irq = (offset - 0xf20);
|
||||||
|
+ /* GICD_SPENDSGIRn */
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ res = s->sgi_pending[irq][cpu];
|
||||||
|
} else if (offset < 0xfe0) {
|
||||||
|
goto bad_reg;
|
||||||
|
} else /* offset >= 0xfe0 */ {
|
||||||
|
@@ -527,9 +572,31 @@ static void gic_dist_writeb(void *opaque, hwaddr offset,
|
||||||
|
GIC_CLEAR_EDGE_TRIGGER(irq + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
- } else {
|
||||||
|
+ } else if (offset < 0xf10) {
|
||||||
|
/* 0xf00 is only handled for 32-bit writes. */
|
||||||
|
goto bad_reg;
|
||||||
|
+ } else if (offset < 0xf20) {
|
||||||
|
+ /* GICD_CPENDSGIRn */
|
||||||
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||||
|
+ goto bad_reg;
|
||||||
|
+ }
|
||||||
|
+ irq = (offset - 0xf10);
|
||||||
|
+
|
||||||
|
+ s->sgi_pending[irq][cpu] &= ~value;
|
||||||
|
+ if (s->sgi_pending[irq][cpu] == 0) {
|
||||||
|
+ GIC_CLEAR_PENDING(irq, 1 << cpu);
|
||||||
|
+ }
|
||||||
|
+ } else if (offset < 0xf30) {
|
||||||
|
+ /* GICD_SPENDSGIRn */
|
||||||
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||||
|
+ goto bad_reg;
|
||||||
|
+ }
|
||||||
|
+ irq = (offset - 0xf20);
|
||||||
|
+
|
||||||
|
+ GIC_SET_PENDING(irq, 1 << cpu);
|
||||||
|
+ s->sgi_pending[irq][cpu] |= value;
|
||||||
|
+ } else {
|
||||||
|
+ goto bad_reg;
|
||||||
|
}
|
||||||
|
gic_update(s);
|
||||||
|
return;
|
||||||
|
@@ -553,6 +620,7 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
|
||||||
|
int cpu;
|
||||||
|
int irq;
|
||||||
|
int mask;
|
||||||
|
+ int target_cpu;
|
||||||
|
|
||||||
|
cpu = gic_get_current_cpu(s);
|
||||||
|
irq = value & 0x3ff;
|
||||||
|
@@ -572,6 +640,12 @@ static void gic_dist_writel(void *opaque, hwaddr offset,
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
GIC_SET_PENDING(irq, mask);
|
||||||
|
+ target_cpu = ctz32(mask);
|
||||||
|
+ while (target_cpu < GIC_NCPU) {
|
||||||
|
+ s->sgi_pending[irq][target_cpu] |= (1 << cpu);
|
||||||
|
+ mask &= ~(1 << target_cpu);
|
||||||
|
+ target_cpu = ctz32(mask);
|
||||||
|
+ }
|
||||||
|
gic_update(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
|
||||||
|
index 710607b..f4c7f14 100644
|
||||||
|
--- a/hw/intc/arm_gic_common.c
|
||||||
|
+++ b/hw/intc/arm_gic_common.c
|
||||||
|
@@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_gic = {
|
||||||
|
.name = "arm_gic",
|
||||||
|
- .version_id = 4,
|
||||||
|
- .minimum_version_id = 4,
|
||||||
|
+ .version_id = 5,
|
||||||
|
+ .minimum_version_id = 5,
|
||||||
|
.pre_save = gic_pre_save,
|
||||||
|
.post_load = gic_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
@@ -71,6 +71,7 @@ static const VMStateDescription vmstate_gic = {
|
||||||
|
VMSTATE_UINT8_2DARRAY(priority1, GICState, GIC_INTERNAL, GIC_NCPU),
|
||||||
|
VMSTATE_UINT8_ARRAY(priority2, GICState, GIC_MAXIRQ - GIC_INTERNAL),
|
||||||
|
VMSTATE_UINT16_2DARRAY(last_active, GICState, GIC_MAXIRQ, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU),
|
||||||
|
VMSTATE_UINT16_ARRAY(priority_mask, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
|
||||||
|
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
|
||||||
|
index 8a2aa00..d2e0c2f 100644
|
||||||
|
--- a/include/hw/intc/arm_gic_common.h
|
||||||
|
+++ b/include/hw/intc/arm_gic_common.h
|
||||||
|
@@ -55,6 +55,13 @@ typedef struct GICState {
|
||||||
|
uint8_t priority1[GIC_INTERNAL][GIC_NCPU];
|
||||||
|
uint8_t priority2[GIC_MAXIRQ - GIC_INTERNAL];
|
||||||
|
uint16_t last_active[GIC_MAXIRQ][GIC_NCPU];
|
||||||
|
+ /* For each SGI on the target CPU, we store 8 bits
|
||||||
|
+ * indicating which source CPUs have made this SGI
|
||||||
|
+ * pending on the target CPU. These correspond to
|
||||||
|
+ * the bytes in the GIC_SPENDSGIR* registers as
|
||||||
|
+ * read by the target CPU.
|
||||||
|
+ */
|
||||||
|
+ uint8_t sgi_pending[GIC_NR_SGIS][GIC_NCPU];
|
||||||
|
|
||||||
|
uint16_t priority_mask[GIC_NCPU];
|
||||||
|
uint16_t running_irq[GIC_NCPU];
|
99
0217-arm_gic-Support-setting-getting-binary-point-reg.patch
Normal file
99
0217-arm_gic-Support-setting-getting-binary-point-reg.patch
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Thu, 12 Sep 2013 22:18:20 -0700
|
||||||
|
Subject: [PATCH] arm_gic: Support setting/getting binary point reg
|
||||||
|
|
||||||
|
Add a binary_point field to the gic emulation structure and support
|
||||||
|
setting/getting this register now when we have it. We don't actually
|
||||||
|
support interrupt grouping yet, oh well.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit aa7d461ae9dd79d35999f4710743cdf9dec88cef)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 12 +++++++++---
|
||||||
|
hw/intc/arm_gic_common.c | 6 ++++--
|
||||||
|
include/hw/intc/arm_gic_common.h | 7 +++++++
|
||||||
|
3 files changed, 20 insertions(+), 5 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 29f98be..d31892d 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -669,14 +669,15 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
|
||||||
|
case 0x04: /* Priority mask */
|
||||||
|
return s->priority_mask[cpu];
|
||||||
|
case 0x08: /* Binary Point */
|
||||||
|
- /* ??? Not implemented. */
|
||||||
|
- return 0;
|
||||||
|
+ return s->bpr[cpu];
|
||||||
|
case 0x0c: /* Acknowledge */
|
||||||
|
return gic_acknowledge_irq(s, cpu);
|
||||||
|
case 0x14: /* Running Priority */
|
||||||
|
return s->running_priority[cpu];
|
||||||
|
case 0x18: /* Highest Pending Interrupt */
|
||||||
|
return s->current_pending[cpu];
|
||||||
|
+ case 0x1c: /* Aliased Binary Point */
|
||||||
|
+ return s->abpr[cpu];
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"gic_cpu_read: Bad offset %x\n", (int)offset);
|
||||||
|
@@ -695,10 +696,15 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
|
||||||
|
s->priority_mask[cpu] = (value & 0xff);
|
||||||
|
break;
|
||||||
|
case 0x08: /* Binary Point */
|
||||||
|
- /* ??? Not implemented. */
|
||||||
|
+ s->bpr[cpu] = (value & 0x7);
|
||||||
|
break;
|
||||||
|
case 0x10: /* End Of Interrupt */
|
||||||
|
return gic_complete_irq(s, cpu, value & 0x3ff);
|
||||||
|
+ case 0x1c: /* Aliased Binary Point */
|
||||||
|
+ if (s->revision >= 2) {
|
||||||
|
+ s->abpr[cpu] = (value & 0x7);
|
||||||
|
+ }
|
||||||
|
+ break;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"gic_cpu_write: Bad offset %x\n", (int)offset);
|
||||||
|
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
|
||||||
|
index f4c7f14..7966985 100644
|
||||||
|
--- a/hw/intc/arm_gic_common.c
|
||||||
|
+++ b/hw/intc/arm_gic_common.c
|
||||||
|
@@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_gic = {
|
||||||
|
.name = "arm_gic",
|
||||||
|
- .version_id = 5,
|
||||||
|
- .minimum_version_id = 5,
|
||||||
|
+ .version_id = 6,
|
||||||
|
+ .minimum_version_id = 6,
|
||||||
|
.pre_save = gic_pre_save,
|
||||||
|
.post_load = gic_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
@@ -76,6 +76,8 @@ static const VMStateDescription vmstate_gic = {
|
||||||
|
VMSTATE_UINT16_ARRAY(running_irq, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_UINT16_ARRAY(running_priority, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
|
||||||
|
index d2e0c2f..983c3cf 100644
|
||||||
|
--- a/include/hw/intc/arm_gic_common.h
|
||||||
|
+++ b/include/hw/intc/arm_gic_common.h
|
||||||
|
@@ -68,6 +68,13 @@ typedef struct GICState {
|
||||||
|
uint16_t running_priority[GIC_NCPU];
|
||||||
|
uint16_t current_pending[GIC_NCPU];
|
||||||
|
|
||||||
|
+ /* We present the GICv2 without security extensions to a guest and
|
||||||
|
+ * therefore the guest can configure the GICC_CTLR to configure group 1
|
||||||
|
+ * binary point in the abpr.
|
||||||
|
+ */
|
||||||
|
+ uint8_t bpr[GIC_NCPU];
|
||||||
|
+ uint8_t abpr[GIC_NCPU];
|
||||||
|
+
|
||||||
|
uint32_t num_cpu;
|
||||||
|
|
||||||
|
MemoryRegion iomem; /* Distributor */
|
104
0218-arm_gic-Add-GICC_APRn-state-to-the-GICState.patch
Normal file
104
0218-arm_gic-Add-GICC_APRn-state-to-the-GICState.patch
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
From: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Date: Mon, 18 Nov 2013 19:26:33 -0800
|
||||||
|
Subject: [PATCH] arm_gic: Add GICC_APRn state to the GICState
|
||||||
|
|
||||||
|
The GICC_APRn registers are not currently supported by the ARM GIC v2.0
|
||||||
|
emulation. This patch adds the missing state.
|
||||||
|
|
||||||
|
Note that we also change the number of APRs to use a define GIC_NR_APRS
|
||||||
|
based on the maximum number of preemption levels. This patch also adds
|
||||||
|
RAZ/WI accessors for the four registers on the emulated CPU interface.
|
||||||
|
|
||||||
|
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
(cherry picked from commit a9d477c4e3d614409a48d12f34624c2dd9f1ec2d)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 5 +++++
|
||||||
|
hw/intc/arm_gic_common.c | 5 +++--
|
||||||
|
include/hw/intc/arm_gic_common.h | 19 +++++++++++++++++++
|
||||||
|
3 files changed, 27 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index d31892d..9ca3f03 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -678,6 +678,8 @@ static uint32_t gic_cpu_read(GICState *s, int cpu, int offset)
|
||||||
|
return s->current_pending[cpu];
|
||||||
|
case 0x1c: /* Aliased Binary Point */
|
||||||
|
return s->abpr[cpu];
|
||||||
|
+ case 0xd0: case 0xd4: case 0xd8: case 0xdc:
|
||||||
|
+ return s->apr[(offset - 0xd0) / 4][cpu];
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"gic_cpu_read: Bad offset %x\n", (int)offset);
|
||||||
|
@@ -705,6 +707,9 @@ static void gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value)
|
||||||
|
s->abpr[cpu] = (value & 0x7);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
+ case 0xd0: case 0xd4: case 0xd8: case 0xdc:
|
||||||
|
+ qemu_log_mask(LOG_UNIMP, "Writing APR not implemented\n");
|
||||||
|
+ break;
|
||||||
|
default:
|
||||||
|
qemu_log_mask(LOG_GUEST_ERROR,
|
||||||
|
"gic_cpu_write: Bad offset %x\n", (int)offset);
|
||||||
|
diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c
|
||||||
|
index 7966985..ec6286b 100644
|
||||||
|
--- a/hw/intc/arm_gic_common.c
|
||||||
|
+++ b/hw/intc/arm_gic_common.c
|
||||||
|
@@ -58,8 +58,8 @@ static const VMStateDescription vmstate_gic_irq_state = {
|
||||||
|
|
||||||
|
static const VMStateDescription vmstate_gic = {
|
||||||
|
.name = "arm_gic",
|
||||||
|
- .version_id = 6,
|
||||||
|
- .minimum_version_id = 6,
|
||||||
|
+ .version_id = 7,
|
||||||
|
+ .minimum_version_id = 7,
|
||||||
|
.pre_save = gic_pre_save,
|
||||||
|
.post_load = gic_post_load,
|
||||||
|
.fields = (VMStateField[]) {
|
||||||
|
@@ -78,6 +78,7 @@ static const VMStateDescription vmstate_gic = {
|
||||||
|
VMSTATE_UINT16_ARRAY(current_pending, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_UINT8_ARRAY(bpr, GICState, GIC_NCPU),
|
||||||
|
VMSTATE_UINT8_ARRAY(abpr, GICState, GIC_NCPU),
|
||||||
|
+ VMSTATE_UINT32_2DARRAY(apr, GICState, GIC_NR_APRS, GIC_NCPU),
|
||||||
|
VMSTATE_END_OF_LIST()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h
|
||||||
|
index 983c3cf..89384c2 100644
|
||||||
|
--- a/include/hw/intc/arm_gic_common.h
|
||||||
|
+++ b/include/hw/intc/arm_gic_common.h
|
||||||
|
@@ -31,6 +31,9 @@
|
||||||
|
/* Maximum number of possible CPU interfaces, determined by GIC architecture */
|
||||||
|
#define GIC_NCPU 8
|
||||||
|
|
||||||
|
+#define MAX_NR_GROUP_PRIO 128
|
||||||
|
+#define GIC_NR_APRS (MAX_NR_GROUP_PRIO / 32)
|
||||||
|
+
|
||||||
|
typedef struct gic_irq_state {
|
||||||
|
/* The enable bits are only banked for per-cpu interrupts. */
|
||||||
|
uint8_t enabled;
|
||||||
|
@@ -75,6 +78,22 @@ typedef struct GICState {
|
||||||
|
uint8_t bpr[GIC_NCPU];
|
||||||
|
uint8_t abpr[GIC_NCPU];
|
||||||
|
|
||||||
|
+ /* The APR is implementation defined, so we choose a layout identical to
|
||||||
|
+ * the KVM ABI layout for QEMU's implementation of the gic:
|
||||||
|
+ * If an interrupt for preemption level X is active, then
|
||||||
|
+ * APRn[X mod 32] == 0b1, where n = X / 32
|
||||||
|
+ * otherwise the bit is clear.
|
||||||
|
+ *
|
||||||
|
+ * TODO: rewrite the interrupt acknowlege/complete routines to use
|
||||||
|
+ * the APR registers to track the necessary information to update
|
||||||
|
+ * s->running_priority[] on interrupt completion (ie completely remove
|
||||||
|
+ * last_active[][] and running_irq[]). This will be necessary if we ever
|
||||||
|
+ * want to support TCG<->KVM migration, or TCG guests which can
|
||||||
|
+ * do power management involving powering down and restarting
|
||||||
|
+ * the GIC.
|
||||||
|
+ */
|
||||||
|
+ uint32_t apr[GIC_NR_APRS][GIC_NCPU];
|
||||||
|
+
|
||||||
|
uint32_t num_cpu;
|
||||||
|
|
||||||
|
MemoryRegion iomem; /* Distributor */
|
31
0219-hw-intc-arm_gic-Fix-NVIC-assertion-failure.patch
Normal file
31
0219-hw-intc-arm_gic-Fix-NVIC-assertion-failure.patch
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
From: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Date: Thu, 20 Feb 2014 10:35:48 +0000
|
||||||
|
Subject: [PATCH] hw/intc/arm_gic: Fix NVIC assertion failure
|
||||||
|
|
||||||
|
Commit 40d225009ef accidentally changed the behaviour of
|
||||||
|
gic_acknowledge_irq() for the NVIC. The NVIC doesn't have SGIs,
|
||||||
|
so this meant we hit an assertion:
|
||||||
|
gic_acknowledge_irq: Assertion `s->sgi_pending[irq][cpu] != 0' failed.
|
||||||
|
|
||||||
|
Return NVIC acknowledge-irq to its previous behaviour, like 11MPCore.
|
||||||
|
|
||||||
|
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
|
||||||
|
Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
|
||||||
|
(cherry picked from commit 873169022aa58daabd10979002f8009c7e5f3f05)
|
||||||
|
---
|
||||||
|
hw/intc/arm_gic.c | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c
|
||||||
|
index 9ca3f03..4e0628c 100644
|
||||||
|
--- a/hw/intc/arm_gic.c
|
||||||
|
+++ b/hw/intc/arm_gic.c
|
||||||
|
@@ -189,7 +189,7 @@ uint32_t gic_acknowledge_irq(GICState *s, int cpu)
|
||||||
|
}
|
||||||
|
s->last_active[irq][cpu] = s->running_irq[cpu];
|
||||||
|
|
||||||
|
- if (s->revision == REV_11MPCORE) {
|
||||||
|
+ if (s->revision == REV_11MPCORE || s->revision == REV_NVIC) {
|
||||||
|
/* Clear pending flags for both level and edge triggered interrupts.
|
||||||
|
* Level triggered IRQs will be reasserted once they become inactive.
|
||||||
|
*/
|
56
0301-vmstate-add-VMS_MUST_EXIST.patch
Normal file
56
0301-vmstate-add-VMS_MUST_EXIST.patch
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
From: "Michael S. Tsirkin" <mst@redhat.com>
|
||||||
|
Date: Thu, 3 Apr 2014 19:50:31 +0300
|
||||||
|
Subject: [PATCH] vmstate: add VMS_MUST_EXIST
|
||||||
|
|
||||||
|
Can be used to verify a required field exists or validate
|
||||||
|
state in some other way.
|
||||||
|
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
|
||||||
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
||||||
|
(cherry picked from commit 5bf81c8d63db0216a4d29dc87f9ce530bb791dd1)
|
||||||
|
---
|
||||||
|
include/migration/vmstate.h | 1 +
|
||||||
|
savevm.c | 10 ++++++++++
|
||||||
|
2 files changed, 11 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
|
||||||
|
index e5538c7..c1717c2 100644
|
||||||
|
--- a/include/migration/vmstate.h
|
||||||
|
+++ b/include/migration/vmstate.h
|
||||||
|
@@ -100,6 +100,7 @@ enum VMStateFlags {
|
||||||
|
VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */
|
||||||
|
VMS_VARRAY_UINT8 = 0x400, /* Array with size in uint8_t field*/
|
||||||
|
VMS_VARRAY_UINT32 = 0x800, /* Array with size in uint32_t field*/
|
||||||
|
+ VMS_MUST_EXIST = 0x1000, /* Field must exist in input */
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
diff --git a/savevm.c b/savevm.c
|
||||||
|
index 03fc4d9..9f7f668 100644
|
||||||
|
--- a/savevm.c
|
||||||
|
+++ b/savevm.c
|
||||||
|
@@ -1731,6 +1731,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ } else if (field->flags & VMS_MUST_EXIST) {
|
||||||
|
+ fprintf(stderr, "Input validation failed: %s/%s\n",
|
||||||
|
+ vmsd->name, field->name);
|
||||||
|
+ return -1;
|
||||||
|
}
|
||||||
|
field++;
|
||||||
|
}
|
||||||
|
@@ -1791,6 +1795,12 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd,
|
||||||
|
field->info->put(f, addr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
+ } else {
|
||||||
|
+ if (field->flags & VMS_MUST_EXIST) {
|
||||||
|
+ fprintf(stderr, "Output state validation failed: %s/%s\n",
|
||||||
|
+ vmsd->name, field->name);
|
||||||
|
+ assert(!(field->flags & VMS_MUST_EXIST));
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
field++;
|
||||||
|
}
|
32
0302-vmstate-add-VMSTATE_VALIDATE.patch
Normal file
32
0302-vmstate-add-VMSTATE_VALIDATE.patch
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
From: "Michael S. Tsirkin" <mst@redhat.com>
|
||||||
|
Date: Thu, 3 Apr 2014 19:50:35 +0300
|
||||||
|
Subject: [PATCH] vmstate: add VMSTATE_VALIDATE
|
||||||
|
|
||||||
|
Validate state using VMS_ARRAY with num = 0 and VMS_MUST_EXIST
|
||||||
|
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
||||||
|
(cherry picked from commit 4082f0889ba04678fc14816c53e1b9251ea9207e)
|
||||||
|
---
|
||||||
|
include/migration/vmstate.h | 8 ++++++++
|
||||||
|
1 file changed, 8 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h
|
||||||
|
index c1717c2..d5a63ce 100644
|
||||||
|
--- a/include/migration/vmstate.h
|
||||||
|
+++ b/include/migration/vmstate.h
|
||||||
|
@@ -204,6 +204,14 @@ extern const VMStateInfo vmstate_info_bitmap;
|
||||||
|
.offset = vmstate_offset_value(_state, _field, _type), \
|
||||||
|
}
|
||||||
|
|
||||||
|
+/* Validate state using a boolean predicate. */
|
||||||
|
+#define VMSTATE_VALIDATE(_name, _test) { \
|
||||||
|
+ .name = (_name), \
|
||||||
|
+ .field_exists = (_test), \
|
||||||
|
+ .flags = VMS_ARRAY | VMS_MUST_EXIST, \
|
||||||
|
+ .num = 0, /* 0 elements: no data, only run _test */ \
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#define VMSTATE_POINTER(_field, _state, _version, _info, _type) { \
|
||||||
|
.name = (stringify(_field)), \
|
||||||
|
.version_id = (_version), \
|
@ -0,0 +1,58 @@
|
|||||||
|
From: "Michael S. Tsirkin" <mst@redhat.com>
|
||||||
|
Date: Thu, 3 Apr 2014 19:50:39 +0300
|
||||||
|
Subject: [PATCH] virtio-net: fix buffer overflow on invalid state load
|
||||||
|
|
||||||
|
CVE-2013-4148 QEMU 1.0 integer conversion in
|
||||||
|
virtio_net_load()@hw/net/virtio-net.c
|
||||||
|
|
||||||
|
Deals with loading a corrupted savevm image.
|
||||||
|
|
||||||
|
> n->mac_table.in_use = qemu_get_be32(f);
|
||||||
|
|
||||||
|
in_use is int so it can get negative when assigned 32bit unsigned value.
|
||||||
|
|
||||||
|
> /* MAC_TABLE_ENTRIES may be different from the saved image */
|
||||||
|
> if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) {
|
||||||
|
|
||||||
|
passing this check ^^^
|
||||||
|
|
||||||
|
> qemu_get_buffer(f, n->mac_table.macs,
|
||||||
|
> n->mac_table.in_use * ETH_ALEN);
|
||||||
|
|
||||||
|
with good in_use value, "n->mac_table.in_use * ETH_ALEN" can get
|
||||||
|
positive and bigger than mac_table.macs. For example 0x81000000
|
||||||
|
satisfies this condition when ETH_ALEN is 6.
|
||||||
|
|
||||||
|
Fix it by making the value unsigned.
|
||||||
|
For consistency, change first_multi as well.
|
||||||
|
|
||||||
|
Note: all call sites were audited to confirm that
|
||||||
|
making them unsigned didn't cause any issues:
|
||||||
|
it turns out we actually never do math on them,
|
||||||
|
so it's easy to validate because both values are
|
||||||
|
always <= MAC_TABLE_ENTRIES.
|
||||||
|
|
||||||
|
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
|
||||||
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
||||||
|
(cherry picked from commit 71f7fe48e10a8437c9d42d859389f37157f59980)
|
||||||
|
---
|
||||||
|
include/hw/virtio/virtio-net.h | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h
|
||||||
|
index df60f16..4b32440 100644
|
||||||
|
--- a/include/hw/virtio/virtio-net.h
|
||||||
|
+++ b/include/hw/virtio/virtio-net.h
|
||||||
|
@@ -176,8 +176,8 @@ typedef struct VirtIONet {
|
||||||
|
uint8_t nobcast;
|
||||||
|
uint8_t vhost_started;
|
||||||
|
struct {
|
||||||
|
- int in_use;
|
||||||
|
- int first_multi;
|
||||||
|
+ uint32_t in_use;
|
||||||
|
+ uint32_t first_multi;
|
||||||
|
uint8_t multi_overflow;
|
||||||
|
uint8_t uni_overflow;
|
||||||
|
uint8_t *macs;
|
@ -0,0 +1,54 @@
|
|||||||
|
From: "Michael S. Tsirkin" <mst@redhat.com>
|
||||||
|
Date: Thu, 3 Apr 2014 19:50:56 +0300
|
||||||
|
Subject: [PATCH] virtio-net: out-of-bounds buffer write on invalid state load
|
||||||
|
|
||||||
|
CVE-2013-4150 QEMU 1.5.0 out-of-bounds buffer write in
|
||||||
|
virtio_net_load()@hw/net/virtio-net.c
|
||||||
|
|
||||||
|
This code is in hw/net/virtio-net.c:
|
||||||
|
|
||||||
|
if (n->max_queues > 1) {
|
||||||
|
if (n->max_queues != qemu_get_be16(f)) {
|
||||||
|
error_report("virtio-net: different max_queues ");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
n->curr_queues = qemu_get_be16(f);
|
||||||
|
for (i = 1; i < n->curr_queues; i++) {
|
||||||
|
n->vqs[i].tx_waiting = qemu_get_be32(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Number of vqs is max_queues, so if we get invalid input here,
|
||||||
|
for example if max_queues = 2, curr_queues = 3, we get
|
||||||
|
write beyond end of the buffer, with data that comes from
|
||||||
|
wire.
|
||||||
|
|
||||||
|
This might be used to corrupt qemu memory in hard to predict ways.
|
||||||
|
Since we have lots of function pointers around, RCE might be possible.
|
||||||
|
|
||||||
|
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
|
||||||
|
Acked-by: Jason Wang <jasowang@redhat.com>
|
||||||
|
Reviewed-by: Michael Roth <mdroth@linux.vnet.ibm.com>
|
||||||
|
Signed-off-by: Juan Quintela <quintela@redhat.com>
|
||||||
|
(cherry picked from commit eea750a5623ddac7a61982eec8f1c93481857578)
|
||||||
|
---
|
||||||
|
hw/net/virtio-net.c | 5 +++++
|
||||||
|
1 file changed, 5 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
|
||||||
|
index 63f777f..5907e96 100644
|
||||||
|
--- a/hw/net/virtio-net.c
|
||||||
|
+++ b/hw/net/virtio-net.c
|
||||||
|
@@ -1379,6 +1379,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
n->curr_queues = qemu_get_be16(f);
|
||||||
|
+ if (n->curr_queues > n->max_queues) {
|
||||||
|
+ error_report("virtio-net: curr_queues %x > max_queues %x",
|
||||||
|
+ n->curr_queues, n->max_queues);
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
for (i = 1; i < n->curr_queues; i++) {
|
||||||
|
n->vqs[i].tx_waiting = qemu_get_be32(f);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user