Compare commits

...

No commits in common. 'c9' and 'i9-beta-el9_4' have entirely different histories.

@ -1,128 +0,0 @@
From ef212eb3026a2460f6502a76afa3baedeb74d975 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
Date: Tue, 6 Aug 2024 14:14:27 +0400
Subject: [PATCH 1/5] Fix scanout version with pc-q35-rhel9.4.0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-MergeRequest: 383: Fix scanout version with pc-q35-rhel9.4.0
RH-Jira: RHEL-53565
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] ed2860cfb933654b0046c1b59f78d2e50146bd6c
Resolves: https://issues.redhat.com/browse/RHEL-52940
Introduce hw_compat_rhel_9_4_extra so that pc-q35-rhel9.4.0 has
x-scanout-vmstate-version=1
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/arm/virt.c | 1 +
hw/core/machine.c | 13 +++++++++++--
hw/i386/pc_piix.c | 2 ++
hw/i386/pc_q35.c | 3 +++
hw/s390x/s390-virtio-ccw.c | 1 +
include/hw/boards.h | 3 +++
6 files changed, 21 insertions(+), 2 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index e4a66affcb..851eff6b28 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -3670,6 +3670,7 @@ type_init(rhel_machine_init);
static void rhel940_virt_options(MachineClass *mc)
{
+ compat_props_add(mc->compat_props, hw_compat_rhel_9_4_extra, hw_compat_rhel_9_4_extra_len);
}
DEFINE_RHEL_MACHINE_AS_LATEST(9, 4, 0)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index c8c460c916..fe0a28ef3b 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -64,6 +64,17 @@ const size_t hw_compat_7_2_len = G_N_ELEMENTS(hw_compat_7_2);
const char *rhel_old_machine_deprecation =
"machine types for previous major releases are deprecated";
+/*
+ * rhel_9_4_extra is used for <= 9.4 machines.
+ *
+ * (for compatibility and historical reasons, rhel_9_4 is used for <= 9.2 too)
+ */
+GlobalProperty hw_compat_rhel_9_4_extra[] = {
+ /* hw_compat_rhel_9_4_extra from hw_compat_8_2 */
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
+};
+const size_t hw_compat_rhel_9_4_extra_len = G_N_ELEMENTS(hw_compat_rhel_9_4_extra);
+
GlobalProperty hw_compat_rhel_9_4[] = {
/* hw_compat_rhel_9_4 from hw_compat_8_0 */
{ TYPE_VIRTIO_NET, "host_uso", "off"},
@@ -81,8 +92,6 @@ GlobalProperty hw_compat_rhel_9_4[] = {
{ "igb", "x-pcie-flr-init", "off" },
/* hw_compat_rhel_9_4 jira RHEL-24045 */
{ "virtio-mem", "dynamic-memslots", "off" },
- /* hw_compat_rhel_9_4 from hw_compat_8_1 */
- { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
};
const size_t hw_compat_rhel_9_4_len = G_N_ELEMENTS(hw_compat_rhel_9_4);
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 54d1c58bce..c846673d87 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -1031,6 +1031,8 @@ static void pc_machine_rhel760_options(MachineClass *m)
"Use a different south bridge than PIIX3");
+ compat_props_add(m->compat_props, hw_compat_rhel_9_4_extra,
+ hw_compat_rhel_9_4_extra_len);
compat_props_add(m->compat_props, hw_compat_rhel_9_4,
hw_compat_rhel_9_4_len);
compat_props_add(m->compat_props, hw_compat_rhel_9_3,
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index cd5fb7380e..02bc3d515f 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -731,6 +731,9 @@ static void pc_q35_machine_rhel940_options(MachineClass *m)
m->desc = "RHEL-9.4.0 PC (Q35 + ICH9, 2009)";
pcmc->smbios_stream_product = "RHEL";
pcmc->smbios_stream_version = "9.4.0";
+
+ compat_props_add(m->compat_props, hw_compat_rhel_9_4_extra,
+ hw_compat_rhel_9_4_extra_len);
}
DEFINE_PC_MACHINE(q35_rhel940, "pc-q35-rhel9.4.0", pc_q35_init_rhel940,
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 24f4773179..cc6087c37a 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -1277,6 +1277,7 @@ static void ccw_machine_rhel940_instance_options(MachineState *machine)
static void ccw_machine_rhel940_class_options(MachineClass *mc)
{
+ compat_props_add(mc->compat_props, hw_compat_rhel_9_4_extra, hw_compat_rhel_9_4_extra_len);
}
DEFINE_CCW_MACHINE(rhel940, "rhel9.4.0", true);
diff --git a/include/hw/boards.h b/include/hw/boards.h
index 4edfdb0ddb..e6ae0e61cf 100644
--- a/include/hw/boards.h
+++ b/include/hw/boards.h
@@ -505,6 +505,9 @@ extern const size_t hw_compat_2_2_len;
extern GlobalProperty hw_compat_2_1[];
extern const size_t hw_compat_2_1_len;
+extern GlobalProperty hw_compat_rhel_9_4_extra[];
+extern const size_t hw_compat_rhel_9_4_extra_len;
+
extern GlobalProperty hw_compat_rhel_9_4[];
extern const size_t hw_compat_rhel_9_4_len;
--
2.39.3

@ -1,155 +0,0 @@
From 5c639f8ce65183ce8e44ee8e0230e9d627a440d7 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Wed, 21 Feb 2024 17:00:27 +0000
Subject: [PATCH 05/20] Implement SMBIOS type 9 v2.6
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [3/18] ead230527d93938907a561cf5b985ee4f54d82b1
JIRA: https://issues.redhat.com/browse/RHEL-21705
Author: Felix Wu <flwu@google.com>
Signed-off-by: Felix Wu <flwu@google.com>
Signed-off-by: Nabih Estefan <nabihestefan@google.com>
Message-Id: <20240221170027.1027325-3-nabihestefan@google.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
(cherry picked from commit 04f143d828845d0fd52dd4a52664d81a4f5431f7)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/smbios/smbios.c | 49 +++++++++++++++++++++++++++++++++---
include/hw/firmware/smbios.h | 4 +++
qemu-options.hx | 2 +-
3 files changed, 51 insertions(+), 4 deletions(-)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 4f5637d445..074705fa4c 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -124,7 +124,7 @@ static QTAILQ_HEAD(, type8_instance) type8 = QTAILQ_HEAD_INITIALIZER(type8);
/* type 9 instance for parsing */
struct type9_instance {
- const char *slot_designation;
+ const char *slot_designation, *pcidev;
uint8_t slot_type, slot_data_bus_width, current_usage, slot_length,
slot_characteristics1, slot_characteristics2;
uint16_t slot_id;
@@ -427,6 +427,11 @@ static const QemuOptDesc qemu_smbios_type9_opts[] = {
.type = QEMU_OPT_NUMBER,
.help = "slot characteristics2, see the spec",
},
+ {
+ .name = "pci_device",
+ .type = QEMU_OPT_STRING,
+ .help = "PCI device, if provided."
+ }
};
static const QemuOptDesc qemu_smbios_type11_opts[] = {
@@ -851,7 +856,7 @@ static void smbios_build_type_8_table(void)
}
}
-static void smbios_build_type_9_table(void)
+static void smbios_build_type_9_table(Error **errp)
{
unsigned instance = 0;
struct type9_instance *t9;
@@ -868,6 +873,43 @@ static void smbios_build_type_9_table(void)
t->slot_characteristics1 = t9->slot_characteristics1;
t->slot_characteristics2 = t9->slot_characteristics2;
+ if (t9->pcidev) {
+ PCIDevice *pdev = NULL;
+ int rc = pci_qdev_find_device(t9->pcidev, &pdev);
+ if (rc != 0) {
+ error_setg(errp,
+ "No PCI device %s for SMBIOS type 9 entry %s",
+ t9->pcidev, t9->slot_designation);
+ return;
+ }
+ /*
+ * We only handle the case were the device is attached to
+ * the PCI root bus. The general case is more complex as
+ * bridges are enumerated later and the table would need
+ * to be updated at this moment.
+ */
+ if (!pci_bus_is_root(pci_get_bus(pdev))) {
+ error_setg(errp,
+ "Cannot create type 9 entry for PCI device %s: "
+ "not attached to the root bus",
+ t9->pcidev);
+ return;
+ }
+ t->segment_group_number = cpu_to_le16(0);
+ t->bus_number = pci_dev_bus_num(pdev);
+ t->device_number = pdev->devfn;
+ } else {
+ /*
+ * Per SMBIOS spec, For slots that are not of the PCI, AGP, PCI-X,
+ * or PCI-Express type that do not have bus/device/function
+ * information, 0FFh should be populated in the fields of Segment
+ * Group Number, Bus Number, Device/Function Number.
+ */
+ t->segment_group_number = 0xff;
+ t->bus_number = 0xff;
+ t->device_number = 0xff;
+ }
+
SMBIOS_BUILD_TABLE_POST;
instance++;
}
@@ -1222,7 +1264,7 @@ void smbios_get_tables(MachineState *ms,
}
smbios_build_type_8_table();
- smbios_build_type_9_table();
+ smbios_build_type_9_table(errp);
smbios_build_type_11_table();
#define MAX_DIMM_SZ (16 * GiB)
@@ -1568,6 +1610,7 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
t->slot_id = qemu_opt_get_number(opts, "slot_id", 0);
t->slot_characteristics1 = qemu_opt_get_number(opts, "slot_characteristics1", 0);
t->slot_characteristics2 = qemu_opt_get_number(opts, "slot_characteristics2", 0);
+ save_opt(&t->pcidev, opts, "pcidev");
QTAILQ_INSERT_TAIL(&type9, t, next);
return;
}
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index 6bbd5a4c20..f8dd07fe4c 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -222,6 +222,10 @@ struct smbios_type_9 {
uint16_t slot_id;
uint8_t slot_characteristics1;
uint8_t slot_characteristics2;
+ /* SMBIOS spec v2.6+ */
+ uint16_t segment_group_number;
+ uint8_t bus_number;
+ uint8_t device_number;
} QEMU_PACKED;
/* SMBIOS type 11 - OEM strings */
diff --git a/qemu-options.hx b/qemu-options.hx
index 94cacc2c63..93364e1765 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2710,7 +2710,7 @@ SRST
``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,processor-id=%d]``
Specify SMBIOS type 4 fields
-``-smbios type=9[,slot_designation=str][,slot_type=%d][,slot_data_bus_width=%d][,current_usage=%d][,slot_length=%d][,slot_id=%d][,slot_characteristics1=%d][,slot_characteristics12=%d]``
+``-smbios type=9[,slot_designation=str][,slot_type=%d][,slot_data_bus_width=%d][,current_usage=%d][,slot_length=%d][,slot_id=%d][,slot_characteristics1=%d][,slot_characteristics12=%d][,pci_device=str]``
Specify SMBIOS type 9 fields
``-smbios type=11[,value=str][,path=filename]``
--
2.39.3

@ -1,218 +0,0 @@
From 84fc607d678bd72397a41d706e91fa241fd97266 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Wed, 21 Feb 2024 17:00:26 +0000
Subject: [PATCH 04/20] Implement base of SMBIOS type 9 descriptor.
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [2/18] 2678cc080bfbf3357fa2f94ceaf42fc61b690d32
JIRA: https://issues.redhat.com/browse/RHEL-21705
commit: 735eee07d1f963635d3c3bf9f5e4bf1bc000870e
Author: Felix Wu <flwu@google.com>
Version 2.1+.
Signed-off-by: Felix Wu <flwu@google.com>
Signed-off-by: Nabih Estefan <nabihestefan@google.com>
Message-Id: <20240221170027.1027325-2-nabihestefan@google.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/smbios/smbios.c | 99 ++++++++++++++++++++++++++++++++++++
include/hw/firmware/smbios.h | 13 +++++
qemu-options.hx | 3 ++
3 files changed, 115 insertions(+)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 7bde23e59d..4f5637d445 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -122,6 +122,16 @@ struct type8_instance {
};
static QTAILQ_HEAD(, type8_instance) type8 = QTAILQ_HEAD_INITIALIZER(type8);
+/* type 9 instance for parsing */
+struct type9_instance {
+ const char *slot_designation;
+ uint8_t slot_type, slot_data_bus_width, current_usage, slot_length,
+ slot_characteristics1, slot_characteristics2;
+ uint16_t slot_id;
+ QTAILQ_ENTRY(type9_instance) next;
+};
+static QTAILQ_HEAD(, type9_instance) type9 = QTAILQ_HEAD_INITIALIZER(type9);
+
static struct {
size_t nvalues;
char **values;
@@ -371,6 +381,54 @@ static const QemuOptDesc qemu_smbios_type8_opts[] = {
},
};
+static const QemuOptDesc qemu_smbios_type9_opts[] = {
+ {
+ .name = "type",
+ .type = QEMU_OPT_NUMBER,
+ .help = "SMBIOS element type",
+ },
+ {
+ .name = "slot_designation",
+ .type = QEMU_OPT_STRING,
+ .help = "string number for reference designation",
+ },
+ {
+ .name = "slot_type",
+ .type = QEMU_OPT_NUMBER,
+ .help = "connector type",
+ },
+ {
+ .name = "slot_data_bus_width",
+ .type = QEMU_OPT_NUMBER,
+ .help = "port type",
+ },
+ {
+ .name = "current_usage",
+ .type = QEMU_OPT_NUMBER,
+ .help = "current usage",
+ },
+ {
+ .name = "slot_length",
+ .type = QEMU_OPT_NUMBER,
+ .help = "system slot length",
+ },
+ {
+ .name = "slot_id",
+ .type = QEMU_OPT_NUMBER,
+ .help = "system slot id",
+ },
+ {
+ .name = "slot_characteristics1",
+ .type = QEMU_OPT_NUMBER,
+ .help = "slot characteristics1, see the spec",
+ },
+ {
+ .name = "slot_characteristics2",
+ .type = QEMU_OPT_NUMBER,
+ .help = "slot characteristics2, see the spec",
+ },
+};
+
static const QemuOptDesc qemu_smbios_type11_opts[] = {
{
.name = "value",
@@ -594,6 +652,7 @@ bool smbios_skip_table(uint8_t type, bool required_table)
#define T2_BASE 0x200
#define T3_BASE 0x300
#define T4_BASE 0x400
+#define T9_BASE 0x900
#define T11_BASE 0xe00
#define T16_BASE 0x1000
@@ -792,6 +851,28 @@ static void smbios_build_type_8_table(void)
}
}
+static void smbios_build_type_9_table(void)
+{
+ unsigned instance = 0;
+ struct type9_instance *t9;
+
+ QTAILQ_FOREACH(t9, &type9, next) {
+ SMBIOS_BUILD_TABLE_PRE(9, T9_BASE + instance, true);
+
+ SMBIOS_TABLE_SET_STR(9, slot_designation, t9->slot_designation);
+ t->slot_type = t9->slot_type;
+ t->slot_data_bus_width = t9->slot_data_bus_width;
+ t->current_usage = t9->current_usage;
+ t->slot_length = t9->slot_length;
+ t->slot_id = t9->slot_id;
+ t->slot_characteristics1 = t9->slot_characteristics1;
+ t->slot_characteristics2 = t9->slot_characteristics2;
+
+ SMBIOS_BUILD_TABLE_POST;
+ instance++;
+ }
+}
+
static void smbios_build_type_11_table(void)
{
char count_str[128];
@@ -1141,6 +1222,7 @@ void smbios_get_tables(MachineState *ms,
}
smbios_build_type_8_table();
+ smbios_build_type_9_table();
smbios_build_type_11_table();
#define MAX_DIMM_SZ (16 * GiB)
@@ -1472,6 +1554,23 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
t8_i->port_type = qemu_opt_get_number(opts, "port_type", 0);
QTAILQ_INSERT_TAIL(&type8, t8_i, next);
return;
+ case 9: {
+ if (!qemu_opts_validate(opts, qemu_smbios_type9_opts, errp)) {
+ return;
+ }
+ struct type9_instance *t;
+ t = g_new0(struct type9_instance, 1);
+ save_opt(&t->slot_designation, opts, "slot_designation");
+ t->slot_type = qemu_opt_get_number(opts, "slot_type", 0);
+ t->slot_data_bus_width = qemu_opt_get_number(opts, "slot_data_bus_width", 0);
+ t->current_usage = qemu_opt_get_number(opts, "current_usage", 0);
+ t->slot_length = qemu_opt_get_number(opts, "slot_length", 0);
+ t->slot_id = qemu_opt_get_number(opts, "slot_id", 0);
+ t->slot_characteristics1 = qemu_opt_get_number(opts, "slot_characteristics1", 0);
+ t->slot_characteristics2 = qemu_opt_get_number(opts, "slot_characteristics2", 0);
+ QTAILQ_INSERT_TAIL(&type9, t, next);
+ return;
+ }
case 11:
if (!qemu_opts_validate(opts, qemu_smbios_type11_opts, errp)) {
return;
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index d24b3ccd32..6bbd5a4c20 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -211,6 +211,19 @@ struct smbios_type_8 {
uint8_t port_type;
} QEMU_PACKED;
+/* SMBIOS type 9 - System Slots (v2.1+) */
+struct smbios_type_9 {
+ struct smbios_structure_header header;
+ uint8_t slot_designation;
+ uint8_t slot_type;
+ uint8_t slot_data_bus_width;
+ uint8_t current_usage;
+ uint8_t slot_length;
+ uint16_t slot_id;
+ uint8_t slot_characteristics1;
+ uint8_t slot_characteristics2;
+} QEMU_PACKED;
+
/* SMBIOS type 11 - OEM strings */
struct smbios_type_11 {
struct smbios_structure_header header;
diff --git a/qemu-options.hx b/qemu-options.hx
index 0814f43066..94cacc2c63 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -2710,6 +2710,9 @@ SRST
``-smbios type=4[,sock_pfx=str][,manufacturer=str][,version=str][,serial=str][,asset=str][,part=str][,processor-id=%d]``
Specify SMBIOS type 4 fields
+``-smbios type=9[,slot_designation=str][,slot_type=%d][,slot_data_bus_width=%d][,current_usage=%d][,slot_length=%d][,slot_id=%d][,slot_characteristics1=%d][,slot_characteristics12=%d]``
+ Specify SMBIOS type 9 fields
+
``-smbios type=11[,value=str][,path=filename]``
Specify SMBIOS type 11 fields
--
2.39.3

@ -1,60 +0,0 @@
From 2e0e4355b2d4edb66b7d8c198339e17940abd682 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
Date: Mon, 18 Mar 2024 13:03:19 +0000
Subject: [PATCH 2/3] Revert "chardev/char-socket: Fix TLS io channels sending
too much data to the backend"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Daniel P. Berrangé <berrange@redhat.com>
RH-MergeRequest: 233: Fix handling of TLS sessions in chardevs
RH-Jira: RHEL-24614
RH-Acked-by: Thomas Huth <thuth@redhat.com>
RH-Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-Commit: [2/3] 1cb3d72b86ced0f70a09dfa0d325ae8a85db1b2b (berrange/centos-src-qemu)
This commit results in unexpected termination of the TLS connection.
When 'fd_can_read' returns 0, the code goes on to pass a zero length
buffer to qio_channel_read. The TLS impl calls into gnutls_recv()
with this zero length buffer, at which point GNUTLS returns an error
GNUTLS_E_INVALID_REQUEST. This is treated as fatal by QEMU's TLS code
resulting in the connection being torn down by the chardev.
Simply skipping the qio_channel_read when the buffer length is zero
is also not satisfactory, as it results in a high CPU burn busy loop
massively slowing QEMU's functionality.
The proper solution is to avoid tcp_chr_read being called at all
unless the frontend is able to accept more data. This will be done
in a followup commit.
This reverts commit 462945cd22d2bcd233401ed3aa167d83a8e35b05
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit e8ee827ffdb86ebbd5f5213a1f78123c25a90864)
---
chardev/char-socket.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index f48d341ebc..51d0943fce 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -492,9 +492,9 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
s->max_size <= 0) {
return TRUE;
}
- len = tcp_chr_read_poll(opaque);
- if (len > sizeof(buf)) {
- len = sizeof(buf);
+ len = sizeof(buf);
+ if (len > s->max_size) {
+ len = s->max_size;
}
size = tcp_chr_recv(chr, (void *)buf, len);
if (size == 0 || (size == -1 && errno != EAGAIN)) {
--
2.39.3

@ -1,216 +0,0 @@
From ab5a33d57b48e35388928e388bb6e6479bc77651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
Date: Mon, 18 Mar 2024 17:08:30 +0000
Subject: [PATCH 3/3] Revert "chardev: use a child source for qio input source"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Daniel P. Berrangé <berrange@redhat.com>
RH-MergeRequest: 233: Fix handling of TLS sessions in chardevs
RH-Jira: RHEL-24614
RH-Acked-by: Thomas Huth <thuth@redhat.com>
RH-Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-Commit: [3/3] b58e6c19c2b11d5d28db31cf1386226fb01d195e (berrange/centos-src-qemu)
This reverts commit a7077b8e354d90fec26c2921aa2dea85b90dff90,
and add comments to explain why child sources cannot be used.
When a GSource is added as a child of another GSource, if its
'prepare' function indicates readiness, then the parent's
'prepare' function will never be run. The io_watch_poll_prepare
absolutely *must* be run on every iteration of the main loop,
to ensure that the chardev backend doesn't feed data to the
frontend that it is unable to consume.
At the time a7077b8e354d90fec26c2921aa2dea85b90dff90 was made,
all the child GSource impls were relying on poll'ing an FD,
so their 'prepare' functions would never indicate readiness
ahead of poll() being invoked. So the buggy behaviour was
not noticed and lay dormant.
Relatively recently the QIOChannelTLS impl introduced a
level 2 child GSource, which checks with GNUTLS whether it
has cached any data that was decoded but not yet consumed:
commit ffda5db65aef42266a5053a4be34515106c4c7ee
Author: Antoine Damhet <antoine.damhet@shadow.tech>
Date: Tue Nov 15 15:23:29 2022 +0100
io/channel-tls: fix handling of bigger read buffers
Since the TLS backend can read more data from the underlying QIOChannel
we introduce a minimal child GSource to notify if we still have more
data available to be read.
Signed-off-by: Antoine Damhet <antoine.damhet@shadow.tech>
Signed-off-by: Charles Frey <charles.frey@shadow.tech>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
With this, it is now quite common for the 'prepare' function
on a QIOChannelTLS GSource to indicate immediate readiness,
bypassing the parent GSource 'prepare' function. IOW, the
critical 'io_watch_poll_prepare' is being skipped on some
iterations of the main loop. As a result chardev frontend
asserts are now being triggered as they are fed data they
are not ready to consume.
A reproducer is as follows:
* In terminal 1 run a GNUTLS *echo* server
$ gnutls-serv --echo \
--x509cafile ca-cert.pem \
--x509keyfile server-key.pem \
--x509certfile server-cert.pem \
-p 9000
* In terminal 2 run a QEMU guest
$ qemu-system-s390x \
-nodefaults \
-display none \
-object tls-creds-x509,id=tls0,dir=$PWD,endpoint=client \
-chardev socket,id=con0,host=localhost,port=9000,tls-creds=tls0 \
-device sclpconsole,chardev=con0 \
-hda Fedora-Cloud-Base-39-1.5.s390x.qcow2
After the previous patch revert, but before this patch revert,
this scenario will crash:
qemu-system-s390x: ../hw/char/sclpconsole.c:73: chr_read: Assertion
`size <= SIZE_BUFFER_VT220 - scon->iov_data_len' failed.
This assert indicates that 'tcp_chr_read' was called without
'tcp_chr_read_poll' having first been checked for ability to
receive more data
QEMU's use of a 'prepare' function to create/delete another
GSource is rather a hack and not normally the kind of thing that
is expected to be done by a GSource. There is no mechanism to
force GLib to always run the 'prepare' function of a parent
GSource. The best option is to simply not use the child source
concept, and go back to the functional approach previously
relied on.
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Tested-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit 038b4217884c6f297278bb1ec6f0463c6c8221de)
---
chardev/char-io.c | 56 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 51 insertions(+), 5 deletions(-)
diff --git a/chardev/char-io.c b/chardev/char-io.c
index 4451128cba..dab77b112e 100644
--- a/chardev/char-io.c
+++ b/chardev/char-io.c
@@ -33,6 +33,7 @@ typedef struct IOWatchPoll {
IOCanReadHandler *fd_can_read;
GSourceFunc fd_read;
void *opaque;
+ GMainContext *context;
} IOWatchPoll;
static IOWatchPoll *io_watch_poll_from_source(GSource *source)
@@ -50,28 +51,59 @@ static gboolean io_watch_poll_prepare(GSource *source,
return FALSE;
}
+ /*
+ * We do not register the QIOChannel watch as a child GSource.
+ * The 'prepare' function on the parent GSource will be
+ * skipped if a child GSource's 'prepare' function indicates
+ * readiness. We need this prepare function be guaranteed
+ * to run on *every* iteration of the main loop, because
+ * it is critical to ensure we remove the QIOChannel watch
+ * if 'fd_can_read' indicates the frontend cannot receive
+ * more data.
+ */
if (now_active) {
iwp->src = qio_channel_create_watch(
iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL);
g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL);
- g_source_add_child_source(source, iwp->src);
- g_source_unref(iwp->src);
+ g_source_attach(iwp->src, iwp->context);
} else {
- g_source_remove_child_source(source, iwp->src);
+ g_source_destroy(iwp->src);
+ g_source_unref(iwp->src);
iwp->src = NULL;
}
return FALSE;
}
+static gboolean io_watch_poll_check(GSource *source)
+{
+ return FALSE;
+}
+
static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback,
gpointer user_data)
{
- return G_SOURCE_CONTINUE;
+ abort();
+}
+
+static void io_watch_poll_finalize(GSource *source)
+{
+ /*
+ * Due to a glib bug, removing the last reference to a source
+ * inside a finalize callback causes recursive locking (and a
+ * deadlock). This is not a problem inside other callbacks,
+ * including dispatch callbacks, so we call io_remove_watch_poll
+ * to remove this source. At this point, iwp->src must
+ * be NULL, or we would leak it.
+ */
+ IOWatchPoll *iwp = io_watch_poll_from_source(source);
+ assert(iwp->src == NULL);
}
static GSourceFuncs io_watch_poll_funcs = {
.prepare = io_watch_poll_prepare,
+ .check = io_watch_poll_check,
.dispatch = io_watch_poll_dispatch,
+ .finalize = io_watch_poll_finalize,
};
GSource *io_add_watch_poll(Chardev *chr,
@@ -91,6 +123,7 @@ GSource *io_add_watch_poll(Chardev *chr,
iwp->ioc = ioc;
iwp->fd_read = (GSourceFunc) fd_read;
iwp->src = NULL;
+ iwp->context = context;
name = g_strdup_printf("chardev-iowatch-%s", chr->label);
g_source_set_name((GSource *)iwp, name);
@@ -101,10 +134,23 @@ GSource *io_add_watch_poll(Chardev *chr,
return (GSource *)iwp;
}
+static void io_remove_watch_poll(GSource *source)
+{
+ IOWatchPoll *iwp;
+
+ iwp = io_watch_poll_from_source(source);
+ if (iwp->src) {
+ g_source_destroy(iwp->src);
+ g_source_unref(iwp->src);
+ iwp->src = NULL;
+ }
+ g_source_destroy(&iwp->parent);
+}
+
void remove_fd_in_watch(Chardev *chr)
{
if (chr->gsource) {
- g_source_destroy(chr->gsource);
+ io_remove_watch_poll(chr->gsource);
chr->gsource = NULL;
}
}
--
2.39.3

@ -1,260 +0,0 @@
From 5c35b7d631e9cdf75512b9e1a0b5d48e8fd768d9 Mon Sep 17 00:00:00 2001
From: Jon Maloy <jmaloy@redhat.com>
Date: Wed, 5 Jun 2024 19:56:51 -0400
Subject: [PATCH 4/4] block: Parse filenames only when explicitly requested
RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 2: EMBARGOED CVE-2024-4467 for rhel-9.4.z (PRDSC)
RH-Jira: https://issues.redhat.com/browse/RHEL-35610
RH-CVE: CVE-2024-4467
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Hanna Czenczek <hczenczek@redhat.com>
RH-Commit: [4/4] 6f71e6a07bd5a9f8352db920f498f5fa5a2cdbfb
commit f44c2941d4419e60f16dea3e9adca164e75aa78d (origin/cve-2024-4467-hreitz-rhel-9.5.0)
Author: Kevin Wolf <kwolf@redhat.com>
Date: Thu Apr 25 14:56:02 2024 +0200
block: Parse filenames only when explicitly requested
When handling image filenames from legacy options such as -drive or from
tools, these filenames are parsed for protocol prefixes, including for
the json:{} pseudo-protocol.
This behaviour is intended for filenames that come directly from the
command line and for backing files, which may come from the image file
itself. Higher level management tools generally take care to verify that
untrusted images don't contain a bad (or any) backing file reference;
'qemu-img info' is a suitable tool for this.
However, for other files that can be referenced in images, such as
qcow2 data files or VMDK extents, the string from the image file is
usually not verified by management tools - and 'qemu-img info' wouldn't
be suitable because in contrast to backing files, it already opens these
other referenced files. So here the string should be interpreted as a
literal local filename. More complex configurations need to be specified
explicitly on the command line or in QMP.
This patch changes bdrv_open_inherit() so that it only parses filenames
if a new parameter parse_filename is true. It is set for the top level
in bdrv_open(), for the file child and for the backing file child. All
other callers pass false and disable filename parsing this way.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Upstream: N/A, embargoed
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
block.c | 90 ++++++++++++++++++++++++++++++++++++---------------------
1 file changed, 57 insertions(+), 33 deletions(-)
diff --git a/block.c b/block.c
index a097772238..8b6aa4a65c 100644
--- a/block.c
+++ b/block.c
@@ -86,6 +86,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
BlockDriverState *parent,
const BdrvChildClass *child_class,
BdrvChildRole child_role,
+ bool parse_filename,
Error **errp);
static bool bdrv_recurse_has_child(BlockDriverState *bs,
@@ -2035,7 +2036,8 @@ static void parse_json_protocol(QDict *options, const char **pfilename,
* block driver has been specified explicitly.
*/
static int bdrv_fill_options(QDict **options, const char *filename,
- int *flags, Error **errp)
+ int *flags, bool allow_parse_filename,
+ Error **errp)
{
const char *drvname;
bool protocol = *flags & BDRV_O_PROTOCOL;
@@ -2077,7 +2079,7 @@ static int bdrv_fill_options(QDict **options, const char *filename,
if (protocol && filename) {
if (!qdict_haskey(*options, "filename")) {
qdict_put_str(*options, "filename", filename);
- parse_filename = true;
+ parse_filename = allow_parse_filename;
} else {
error_setg(errp, "Can't specify 'file' and 'filename' options at "
"the same time");
@@ -3639,7 +3641,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options,
}
backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs,
- &child_of_bds, bdrv_backing_role(bs), errp);
+ &child_of_bds, bdrv_backing_role(bs), true,
+ errp);
if (!backing_hd) {
bs->open_flags |= BDRV_O_NO_BACKING;
error_prepend(errp, "Could not open backing file: ");
@@ -3673,7 +3676,8 @@ free_exit:
static BlockDriverState *
bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
BlockDriverState *parent, const BdrvChildClass *child_class,
- BdrvChildRole child_role, bool allow_none, Error **errp)
+ BdrvChildRole child_role, bool allow_none,
+ bool parse_filename, Error **errp)
{
BlockDriverState *bs = NULL;
QDict *image_options;
@@ -3704,7 +3708,8 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key,
}
bs = bdrv_open_inherit(filename, reference, image_options, 0,
- parent, child_class, child_role, errp);
+ parent, child_class, child_role, parse_filename,
+ errp);
if (!bs) {
goto done;
}
@@ -3714,6 +3719,33 @@ done:
return bs;
}
+static BdrvChild *bdrv_open_child_common(const char *filename,
+ QDict *options, const char *bdref_key,
+ BlockDriverState *parent,
+ const BdrvChildClass *child_class,
+ BdrvChildRole child_role,
+ bool allow_none, bool parse_filename,
+ Error **errp)
+{
+ BlockDriverState *bs;
+ BdrvChild *child;
+
+ GLOBAL_STATE_CODE();
+
+ bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class,
+ child_role, allow_none, parse_filename, errp);
+ if (bs == NULL) {
+ return NULL;
+ }
+
+ bdrv_graph_wrlock();
+ child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role,
+ errp);
+ bdrv_graph_wrunlock();
+
+ return child;
+}
+
/*
* Opens a disk image whose options are given as BlockdevRef in another block
* device's options.
@@ -3737,27 +3769,15 @@ BdrvChild *bdrv_open_child(const char *filename,
BdrvChildRole child_role,
bool allow_none, Error **errp)
{
- BlockDriverState *bs;
- BdrvChild *child;
-
- GLOBAL_STATE_CODE();
-
- bs = bdrv_open_child_bs(filename, options, bdref_key, parent, child_class,
- child_role, allow_none, errp);
- if (bs == NULL) {
- return NULL;
- }
-
- bdrv_graph_wrlock();
- child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role,
- errp);
- bdrv_graph_wrunlock();
-
- return child;
+ return bdrv_open_child_common(filename, options, bdref_key, parent,
+ child_class, child_role, allow_none, false,
+ errp);
}
/*
- * Wrapper on bdrv_open_child() for most popular case: open primary child of bs.
+ * This does mostly the same as bdrv_open_child(), but for opening the primary
+ * child of a node. A notable difference from bdrv_open_child() is that it
+ * enables filename parsing for protocol names (including json:).
*
* @parent can move to a different AioContext in this function.
*/
@@ -3772,8 +3792,8 @@ int bdrv_open_file_child(const char *filename,
role = parent->drv->is_filter ?
(BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY) : BDRV_CHILD_IMAGE;
- if (!bdrv_open_child(filename, options, bdref_key, parent,
- &child_of_bds, role, false, errp))
+ if (!bdrv_open_child_common(filename, options, bdref_key, parent,
+ &child_of_bds, role, false, true, errp))
{
return -EINVAL;
}
@@ -3818,7 +3838,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
}
- bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, errp);
+ bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, 0, false,
+ errp);
obj = NULL;
qobject_unref(obj);
visit_free(v);
@@ -3907,7 +3928,7 @@ static BlockDriverState * no_coroutine_fn
bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
int flags, BlockDriverState *parent,
const BdrvChildClass *child_class, BdrvChildRole child_role,
- Error **errp)
+ bool parse_filename, Error **errp)
{
int ret;
BlockBackend *file = NULL;
@@ -3955,9 +3976,11 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
}
/* json: syntax counts as explicit options, as if in the QDict */
- parse_json_protocol(options, &filename, &local_err);
- if (local_err) {
- goto fail;
+ if (parse_filename) {
+ parse_json_protocol(options, &filename, &local_err);
+ if (local_err) {
+ goto fail;
+ }
}
bs->explicit_options = qdict_clone_shallow(options);
@@ -3982,7 +4005,8 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
parent->open_flags, parent->options);
}
- ret = bdrv_fill_options(&options, filename, &flags, &local_err);
+ ret = bdrv_fill_options(&options, filename, &flags, parse_filename,
+ &local_err);
if (ret < 0) {
goto fail;
}
@@ -4051,7 +4075,7 @@ bdrv_open_inherit(const char *filename, const char *reference, QDict *options,
file_bs = bdrv_open_child_bs(filename, options, "file", bs,
&child_of_bds, BDRV_CHILD_IMAGE,
- true, &local_err);
+ true, true, &local_err);
if (local_err) {
goto fail;
}
@@ -4200,7 +4224,7 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
GLOBAL_STATE_CODE();
return bdrv_open_inherit(filename, reference, options, flags, NULL,
- NULL, 0, errp);
+ NULL, 0, true, errp);
}
/* Return true if the NULL-terminated @list contains @str */
--
2.39.3

@ -1,105 +0,0 @@
From 95b2ffc5f01dc4309c2e747ed883d22cd1d26347 Mon Sep 17 00:00:00 2001
From: Thomas Huth <thuth@redhat.com>
Date: Sat, 2 Mar 2024 17:00:23 +0100
Subject: [PATCH 2/2] chardev/char-socket: Fix TLS io channels sending too much
data to the backend
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Thomas Huth <thuth@redhat.com>
RH-MergeRequest: 227: Fix TLS io channels sending too much data to the backend
RH-Jira: RHEL-24614
RH-Acked-by: Cédric Le Goater <clg@redhat.com>
RH-Acked-by: Daniel P. Berrangé <berrange@redhat.com>
RH-Commit: [1/1] fce871914e0ce52e16a6edae0e007513f9fec1ae (thuth/qemu-kvm-cs9)
JIRA: https://issues.redhat.com/browse/RHEL-24614
commit 462945cd22d2bcd233401ed3aa167d83a8e35b05
Author: Thomas Huth <thuth@redhat.com>
Date: Thu Feb 29 11:43:37 2024 +0100
chardev/char-socket: Fix TLS io channels sending too much data to the backend
Commit ffda5db65a ("io/channel-tls: fix handling of bigger read buffers")
changed the behavior of the TLS io channels to schedule a second reading
attempt if there is still incoming data pending. This caused a regression
with backends like the sclpconsole that check in their read function that
the sender does not try to write more bytes to it than the device can
currently handle.
The problem can be reproduced like this:
1) In one terminal, do this:
mkdir qemu-pki
cd qemu-pki
openssl genrsa 2048 > ca-key.pem
openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem
# enter some dummy value for the cert
openssl genrsa 2048 > server-key.pem
openssl req -new -x509 -nodes -days 365000 -key server-key.pem \
-out server-cert.pem
# enter some other dummy values for the cert
gnutls-serv --echo --x509cafile ca-cert.pem --x509keyfile server-key.pem \
--x509certfile server-cert.pem -p 8338
2) In another terminal, do this:
wget https://download.fedoraproject.org/pub/fedora-secondary/releases/39/Cloud/s390x/images/Fedora-Cloud-Base-39-1.5.s390x.qcow2
qemu-system-s390x -nographic -nodefaults \
-hda Fedora-Cloud-Base-39-1.5.s390x.qcow2 \
-object tls-creds-x509,id=tls0,endpoint=client,verify-peer=false,dir=$PWD/qemu-pki \
-chardev socket,id=tls_chardev,host=localhost,port=8338,tls-creds=tls0 \
-device sclpconsole,chardev=tls_chardev,id=tls_serial
QEMU then aborts after a second or two with:
qemu-system-s390x: ../hw/char/sclpconsole.c:73: chr_read: Assertion
`size <= SIZE_BUFFER_VT220 - scon->iov_data_len' failed.
Aborted (core dumped)
It looks like the second read does not trigger the chr_can_read() function
to be called before the second read, which should normally always be done
before sending bytes to a character device to see how much it can handle,
so the s->max_size in tcp_chr_read() still contains the old value from the
previous read. Let's make sure that we use the up-to-date value by calling
tcp_chr_read_poll() again here.
Fixes: ffda5db65a ("io/channel-tls: fix handling of bigger read buffers")
Buglink: https://issues.redhat.com/browse/RHEL-24614
Reviewed-by: "Daniel P. Berrangé" <berrange@redhat.com>
Message-ID: <20240229104339.42574-1-thuth@redhat.com>
Reviewed-by: Antoine Damhet <antoine.damhet@blade-group.com>
Tested-by: Antoine Damhet <antoine.damhet@blade-group.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
---
chardev/char-socket.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 73947da188..034840593d 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -492,9 +492,9 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
s->max_size <= 0) {
return TRUE;
}
- len = sizeof(buf);
- if (len > s->max_size) {
- len = s->max_size;
+ len = tcp_chr_read_poll(opaque);
+ if (len > sizeof(buf)) {
+ len = sizeof(buf);
}
size = tcp_chr_recv(chr, (void *)buf, len);
if (size == 0 || (size == -1 && errno != EAGAIN)) {
--
2.39.3

@ -1,78 +0,0 @@
From 4d4102f6e2f9afd6182888787ae8b570347df87d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= <berrange@redhat.com>
Date: Mon, 18 Mar 2024 18:06:59 +0000
Subject: [PATCH 1/3] chardev: lower priority of the HUP GSource in socket
chardev
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Daniel P. Berrangé <berrange@redhat.com>
RH-MergeRequest: 233: Fix handling of TLS sessions in chardevs
RH-Jira: RHEL-24614
RH-Acked-by: Thomas Huth <thuth@redhat.com>
RH-Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-Commit: [1/3] 842f54349191b0206e68f35a7a80155f5a584942 (berrange/centos-src-qemu)
The socket chardev often has 2 GSource object registered against the
same FD. One is registered all the time and is just intended to handle
POLLHUP events, while the other gets registered & unregistered on the
fly as the frontend is ready to receive more data or not.
It is very common for poll() to signal a POLLHUP event at the same time
as there is pending incoming data from the disconnected client. It is
therefore essential to process incoming data prior to processing HUP.
The problem with having 2 GSource on the same FD is that there is no
guaranteed ordering of execution between them, so the chardev code may
process HUP first and thus discard data.
This failure scenario is non-deterministic but can be seen fairly
reliably by reverting a7077b8e354d90fec26c2921aa2dea85b90dff90, and
then running 'tests/unit/test-char', which will sometimes fail with
missing data.
Ideally QEMU would only have 1 GSource, but that's a complex code
refactoring job. The next best solution is to try to ensure ordering
between the 2 GSource objects. This can be achieved by lowering the
priority of the HUP GSource, so that it is never dispatched if the
main GSource is also ready to dispatch. Counter-intuitively, lowering
the priority of a GSource is done by raising its priority number.
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit 8bd8b04adc9f18904f323dff085f8b4ec77915c6)
---
chardev/char-socket.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 034840593d..f48d341ebc 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -597,6 +597,22 @@ static void update_ioc_handlers(SocketChardev *s)
remove_hup_source(s);
s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP);
+ /*
+ * poll() is liable to return POLLHUP even when there is
+ * still incoming data available to read on the FD. If
+ * we have the hup_source at the same priority as the
+ * main io_add_watch_poll GSource, then we might end up
+ * processing the POLLHUP event first, closing the FD,
+ * and as a result silently discard data we should have
+ * read.
+ *
+ * By setting the hup_source to G_PRIORITY_DEFAULT + 1,
+ * we ensure that io_add_watch_poll GSource will always
+ * be dispatched first, thus guaranteeing we will be
+ * able to process all incoming data before closing the
+ * FD
+ */
+ g_source_set_priority(s->hup_source, G_PRIORITY_DEFAULT + 1);
g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup,
chr, NULL);
g_source_attach(s->hup_source, chr->gcontext);
--
2.39.3

@ -1,412 +0,0 @@
From e99c56752a1c4021a93c92b7be78856ebefaa1b3 Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Mon, 18 Mar 2024 14:34:29 -0400
Subject: [PATCH 1/2] coroutine: cap per-thread local pool size
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 234: coroutine: cap per-thread local pool size
RH-Jira: RHEL-28947
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Commit: [1/2] 5971de1c1e238457925bfb9c4bfc932de857b28d (stefanha/centos-stream-qemu-kvm)
The coroutine pool implementation can hit the Linux vm.max_map_count
limit, causing QEMU to abort with "failed to allocate memory for stack"
or "failed to set up stack guard page" during coroutine creation.
This happens because per-thread pools can grow to tens of thousands of
coroutines. Each coroutine causes 2 virtual memory areas to be created.
Eventually vm.max_map_count is reached and memory-related syscalls fail.
The per-thread pool sizes are non-uniform and depend on past coroutine
usage in each thread, so it's possible for one thread to have a large
pool while another thread's pool is empty.
Switch to a new coroutine pool implementation with a global pool that
grows to a maximum number of coroutines and per-thread local pools that
are capped at hardcoded small number of coroutines.
This approach does not leave large numbers of coroutines pooled in a
thread that may not use them again. In order to perform well it
amortizes the cost of global pool accesses by working in batches of
coroutines instead of individual coroutines.
The global pool is a list. Threads donate batches of coroutines to when
they have too many and take batches from when they have too few:
.-----------------------------------.
| Batch 1 | Batch 2 | Batch 3 | ... | global_pool
`-----------------------------------'
Each thread has up to 2 batches of coroutines:
.-------------------.
| Batch 1 | Batch 2 | per-thread local_pool (maximum 2 batches)
`-------------------'
The goal of this change is to reduce the excessive number of pooled
coroutines that cause QEMU to abort when vm.max_map_count is reached
without losing the performance of an adequately sized coroutine pool.
Here are virtio-blk disk I/O benchmark results:
RW BLKSIZE IODEPTH OLD NEW CHANGE
randread 4k 1 113725 117451 +3.3%
randread 4k 8 192968 198510 +2.9%
randread 4k 16 207138 209429 +1.1%
randread 4k 32 212399 215145 +1.3%
randread 4k 64 218319 221277 +1.4%
randread 128k 1 17587 17535 -0.3%
randread 128k 8 17614 17616 +0.0%
randread 128k 16 17608 17609 +0.0%
randread 128k 32 17552 17553 +0.0%
randread 128k 64 17484 17484 +0.0%
See files/{fio.sh,test.xml.j2} for the benchmark configuration:
https://gitlab.com/stefanha/virt-playbooks/-/tree/coroutine-pool-fix-sizing
Buglink: https://issues.redhat.com/browse/RHEL-28947
Reported-by: Sanjay Rao <srao@redhat.com>
Reported-by: Boaz Ben Shabat <bbenshab@redhat.com>
Reported-by: Joe Mario <jmario@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Message-ID: <20240318183429.1039340-1-stefanha@redhat.com>
(cherry picked from commit 86a637e48104ae74d8be53bed6441ce32be33433)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
util/qemu-coroutine.c | 282 +++++++++++++++++++++++++++++++++---------
1 file changed, 223 insertions(+), 59 deletions(-)
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index 5fd2dbaf8b..2790959eaf 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -18,39 +18,200 @@
#include "qemu/atomic.h"
#include "qemu/coroutine_int.h"
#include "qemu/coroutine-tls.h"
+#include "qemu/cutils.h"
#include "block/aio.h"
-/**
- * The minimal batch size is always 64, coroutines from the release_pool are
- * reused as soon as there are 64 coroutines in it. The maximum pool size starts
- * with 64 and is increased on demand so that coroutines are not deleted even if
- * they are not immediately reused.
- */
enum {
- POOL_MIN_BATCH_SIZE = 64,
- POOL_INITIAL_MAX_SIZE = 64,
+ COROUTINE_POOL_BATCH_MAX_SIZE = 128,
};
-/** Free list to speed up creation */
-static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool);
-static unsigned int pool_max_size = POOL_INITIAL_MAX_SIZE;
-static unsigned int release_pool_size;
+/*
+ * Coroutine creation and deletion is expensive so a pool of unused coroutines
+ * is kept as a cache. When the pool has coroutines available, they are
+ * recycled instead of creating new ones from scratch. Coroutines are added to
+ * the pool upon termination.
+ *
+ * The pool is global but each thread maintains a small local pool to avoid
+ * global pool contention. Threads fetch and return batches of coroutines from
+ * the global pool to maintain their local pool. The local pool holds up to two
+ * batches whereas the maximum size of the global pool is controlled by the
+ * qemu_coroutine_inc_pool_size() API.
+ *
+ * .-----------------------------------.
+ * | Batch 1 | Batch 2 | Batch 3 | ... | global_pool
+ * `-----------------------------------'
+ *
+ * .-------------------.
+ * | Batch 1 | Batch 2 | per-thread local_pool (maximum 2 batches)
+ * `-------------------'
+ */
+typedef struct CoroutinePoolBatch {
+ /* Batches are kept in a list */
+ QSLIST_ENTRY(CoroutinePoolBatch) next;
+
+ /* This batch holds up to @COROUTINE_POOL_BATCH_MAX_SIZE coroutines */
+ QSLIST_HEAD(, Coroutine) list;
+ unsigned int size;
+} CoroutinePoolBatch;
+
+typedef QSLIST_HEAD(, CoroutinePoolBatch) CoroutinePool;
+
+/* Host operating system limit on number of pooled coroutines */
+static unsigned int global_pool_hard_max_size;
+
+static QemuMutex global_pool_lock; /* protects the following variables */
+static CoroutinePool global_pool = QSLIST_HEAD_INITIALIZER(global_pool);
+static unsigned int global_pool_size;
+static unsigned int global_pool_max_size = COROUTINE_POOL_BATCH_MAX_SIZE;
+
+QEMU_DEFINE_STATIC_CO_TLS(CoroutinePool, local_pool);
+QEMU_DEFINE_STATIC_CO_TLS(Notifier, local_pool_cleanup_notifier);
-typedef QSLIST_HEAD(, Coroutine) CoroutineQSList;
-QEMU_DEFINE_STATIC_CO_TLS(CoroutineQSList, alloc_pool);
-QEMU_DEFINE_STATIC_CO_TLS(unsigned int, alloc_pool_size);
-QEMU_DEFINE_STATIC_CO_TLS(Notifier, coroutine_pool_cleanup_notifier);
+static CoroutinePoolBatch *coroutine_pool_batch_new(void)
+{
+ CoroutinePoolBatch *batch = g_new(CoroutinePoolBatch, 1);
+
+ QSLIST_INIT(&batch->list);
+ batch->size = 0;
+ return batch;
+}
-static void coroutine_pool_cleanup(Notifier *n, void *value)
+static void coroutine_pool_batch_delete(CoroutinePoolBatch *batch)
{
Coroutine *co;
Coroutine *tmp;
- CoroutineQSList *alloc_pool = get_ptr_alloc_pool();
- QSLIST_FOREACH_SAFE(co, alloc_pool, pool_next, tmp) {
- QSLIST_REMOVE_HEAD(alloc_pool, pool_next);
+ QSLIST_FOREACH_SAFE(co, &batch->list, pool_next, tmp) {
+ QSLIST_REMOVE_HEAD(&batch->list, pool_next);
qemu_coroutine_delete(co);
}
+ g_free(batch);
+}
+
+static void local_pool_cleanup(Notifier *n, void *value)
+{
+ CoroutinePool *local_pool = get_ptr_local_pool();
+ CoroutinePoolBatch *batch;
+ CoroutinePoolBatch *tmp;
+
+ QSLIST_FOREACH_SAFE(batch, local_pool, next, tmp) {
+ QSLIST_REMOVE_HEAD(local_pool, next);
+ coroutine_pool_batch_delete(batch);
+ }
+}
+
+/* Ensure the atexit notifier is registered */
+static void local_pool_cleanup_init_once(void)
+{
+ Notifier *notifier = get_ptr_local_pool_cleanup_notifier();
+ if (!notifier->notify) {
+ notifier->notify = local_pool_cleanup;
+ qemu_thread_atexit_add(notifier);
+ }
+}
+
+/* Helper to get the next unused coroutine from the local pool */
+static Coroutine *coroutine_pool_get_local(void)
+{
+ CoroutinePool *local_pool = get_ptr_local_pool();
+ CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool);
+ Coroutine *co;
+
+ if (unlikely(!batch)) {
+ return NULL;
+ }
+
+ co = QSLIST_FIRST(&batch->list);
+ QSLIST_REMOVE_HEAD(&batch->list, pool_next);
+ batch->size--;
+
+ if (batch->size == 0) {
+ QSLIST_REMOVE_HEAD(local_pool, next);
+ coroutine_pool_batch_delete(batch);
+ }
+ return co;
+}
+
+/* Get the next batch from the global pool */
+static void coroutine_pool_refill_local(void)
+{
+ CoroutinePool *local_pool = get_ptr_local_pool();
+ CoroutinePoolBatch *batch;
+
+ WITH_QEMU_LOCK_GUARD(&global_pool_lock) {
+ batch = QSLIST_FIRST(&global_pool);
+
+ if (batch) {
+ QSLIST_REMOVE_HEAD(&global_pool, next);
+ global_pool_size -= batch->size;
+ }
+ }
+
+ if (batch) {
+ QSLIST_INSERT_HEAD(local_pool, batch, next);
+ local_pool_cleanup_init_once();
+ }
+}
+
+/* Add a batch of coroutines to the global pool */
+static void coroutine_pool_put_global(CoroutinePoolBatch *batch)
+{
+ WITH_QEMU_LOCK_GUARD(&global_pool_lock) {
+ unsigned int max = MIN(global_pool_max_size,
+ global_pool_hard_max_size);
+
+ if (global_pool_size < max) {
+ QSLIST_INSERT_HEAD(&global_pool, batch, next);
+
+ /* Overshooting the max pool size is allowed */
+ global_pool_size += batch->size;
+ return;
+ }
+ }
+
+ /* The global pool was full, so throw away this batch */
+ coroutine_pool_batch_delete(batch);
+}
+
+/* Get the next unused coroutine from the pool or return NULL */
+static Coroutine *coroutine_pool_get(void)
+{
+ Coroutine *co;
+
+ co = coroutine_pool_get_local();
+ if (!co) {
+ coroutine_pool_refill_local();
+ co = coroutine_pool_get_local();
+ }
+ return co;
+}
+
+static void coroutine_pool_put(Coroutine *co)
+{
+ CoroutinePool *local_pool = get_ptr_local_pool();
+ CoroutinePoolBatch *batch = QSLIST_FIRST(local_pool);
+
+ if (unlikely(!batch)) {
+ batch = coroutine_pool_batch_new();
+ QSLIST_INSERT_HEAD(local_pool, batch, next);
+ local_pool_cleanup_init_once();
+ }
+
+ if (unlikely(batch->size >= COROUTINE_POOL_BATCH_MAX_SIZE)) {
+ CoroutinePoolBatch *next = QSLIST_NEXT(batch, next);
+
+ /* Is the local pool full? */
+ if (next) {
+ QSLIST_REMOVE_HEAD(local_pool, next);
+ coroutine_pool_put_global(batch);
+ }
+
+ batch = coroutine_pool_batch_new();
+ QSLIST_INSERT_HEAD(local_pool, batch, next);
+ }
+
+ QSLIST_INSERT_HEAD(&batch->list, co, pool_next);
+ batch->size++;
}
Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
@@ -58,31 +219,7 @@ Coroutine *qemu_coroutine_create(CoroutineEntry *entry, void *opaque)
Coroutine *co = NULL;
if (IS_ENABLED(CONFIG_COROUTINE_POOL)) {
- CoroutineQSList *alloc_pool = get_ptr_alloc_pool();
-
- co = QSLIST_FIRST(alloc_pool);
- if (!co) {
- if (release_pool_size > POOL_MIN_BATCH_SIZE) {
- /* Slow path; a good place to register the destructor, too. */
- Notifier *notifier = get_ptr_coroutine_pool_cleanup_notifier();
- if (!notifier->notify) {
- notifier->notify = coroutine_pool_cleanup;
- qemu_thread_atexit_add(notifier);
- }
-
- /* This is not exact; there could be a little skew between
- * release_pool_size and the actual size of release_pool. But
- * it is just a heuristic, it does not need to be perfect.
- */
- set_alloc_pool_size(qatomic_xchg(&release_pool_size, 0));
- QSLIST_MOVE_ATOMIC(alloc_pool, &release_pool);
- co = QSLIST_FIRST(alloc_pool);
- }
- }
- if (co) {
- QSLIST_REMOVE_HEAD(alloc_pool, pool_next);
- set_alloc_pool_size(get_alloc_pool_size() - 1);
- }
+ co = coroutine_pool_get();
}
if (!co) {
@@ -100,19 +237,10 @@ static void coroutine_delete(Coroutine *co)
co->caller = NULL;
if (IS_ENABLED(CONFIG_COROUTINE_POOL)) {
- if (release_pool_size < qatomic_read(&pool_max_size) * 2) {
- QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next);
- qatomic_inc(&release_pool_size);
- return;
- }
- if (get_alloc_pool_size() < qatomic_read(&pool_max_size)) {
- QSLIST_INSERT_HEAD(get_ptr_alloc_pool(), co, pool_next);
- set_alloc_pool_size(get_alloc_pool_size() + 1);
- return;
- }
+ coroutine_pool_put(co);
+ } else {
+ qemu_coroutine_delete(co);
}
-
- qemu_coroutine_delete(co);
}
void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co)
@@ -223,10 +351,46 @@ AioContext *qemu_coroutine_get_aio_context(Coroutine *co)
void qemu_coroutine_inc_pool_size(unsigned int additional_pool_size)
{
- qatomic_add(&pool_max_size, additional_pool_size);
+ QEMU_LOCK_GUARD(&global_pool_lock);
+ global_pool_max_size += additional_pool_size;
}
void qemu_coroutine_dec_pool_size(unsigned int removing_pool_size)
{
- qatomic_sub(&pool_max_size, removing_pool_size);
+ QEMU_LOCK_GUARD(&global_pool_lock);
+ global_pool_max_size -= removing_pool_size;
+}
+
+static unsigned int get_global_pool_hard_max_size(void)
+{
+#ifdef __linux__
+ g_autofree char *contents = NULL;
+ int max_map_count;
+
+ /*
+ * Linux processes can have up to max_map_count virtual memory areas
+ * (VMAs). mmap(2), mprotect(2), etc fail with ENOMEM beyond this limit. We
+ * must limit the coroutine pool to a safe size to avoid running out of
+ * VMAs.
+ */
+ if (g_file_get_contents("/proc/sys/vm/max_map_count", &contents, NULL,
+ NULL) &&
+ qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) {
+ /*
+ * This is a conservative upper bound that avoids exceeding
+ * max_map_count. Leave half for non-coroutine users like library
+ * dependencies, vhost-user, etc. Each coroutine takes up 2 VMAs so
+ * halve the amount again.
+ */
+ return max_map_count / 4;
+ }
+#endif
+
+ return UINT_MAX;
+}
+
+static void __attribute__((constructor)) qemu_coroutine_init(void)
+{
+ qemu_mutex_init(&global_pool_lock);
+ global_pool_hard_max_size = get_global_pool_hard_max_size();
}
--
2.39.3

@ -1,61 +0,0 @@
From 0aa65dc3acba481f7064df936ab49e3bceb1d5bd Mon Sep 17 00:00:00 2001
From: Stefan Hajnoczi <stefanha@redhat.com>
Date: Wed, 20 Mar 2024 14:12:32 -0400
Subject: [PATCH 2/2] coroutine: reserve 5,000 mappings
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Stefan Hajnoczi <stefanha@redhat.com>
RH-MergeRequest: 234: coroutine: cap per-thread local pool size
RH-Jira: RHEL-28947
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Commit: [2/2] 78560c2b947471111cc16c313d6f38db42860a1c (stefanha/centos-stream-qemu-kvm)
Daniel P. Berrangé <berrange@redhat.com> pointed out that the coroutine
pool size heuristic is very conservative. Instead of halving
max_map_count, he suggested reserving 5,000 mappings for non-coroutine
users based on observations of guests he has access to.
Fixes: 86a637e48104 ("coroutine: cap per-thread local pool size")
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: 20240320181232.1464819-1-stefanha@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
(cherry picked from commit 9352f80cd926fe2dde7c89b93ee33bb0356ff40e)
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
util/qemu-coroutine.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index 2790959eaf..eb4eebefdf 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -377,12 +377,17 @@ static unsigned int get_global_pool_hard_max_size(void)
NULL) &&
qemu_strtoi(contents, NULL, 10, &max_map_count) == 0) {
/*
- * This is a conservative upper bound that avoids exceeding
- * max_map_count. Leave half for non-coroutine users like library
- * dependencies, vhost-user, etc. Each coroutine takes up 2 VMAs so
- * halve the amount again.
+ * This is an upper bound that avoids exceeding max_map_count. Leave a
+ * fixed amount for non-coroutine users like library dependencies,
+ * vhost-user, etc. Each coroutine takes up 2 VMAs so halve the
+ * remaining amount.
*/
- return max_map_count / 4;
+ if (max_map_count > 5000) {
+ return (max_map_count - 5000) / 2;
+ } else {
+ /* Disable the global pool but threads still have local pools */
+ return 0;
+ }
}
#endif
--
2.39.3

@ -1,186 +0,0 @@
From ea2e2368dcf4140be47288472f2c2a094358e0c7 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Thu, 8 Feb 2024 23:03:45 +0100
Subject: [PATCH 03/20] hw/i386/pc: Defer smbios_set_defaults() to machine_done
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [1/18] 9d4c1d1a910fec7d310429d6fc0b10c798932db7
JIRA: https://issues.redhat.com/browse/RHEL-21705
commit: a0204a5ed091dfe79aced7ec8f3ce1931fd25816
Author: Bernhard Beschow <shentey@gmail.com>
Handling most of smbios data generation in the machine_done notifier is similar
to how the ARM virt machine handles it which also calls smbios_set_defaults()
there. The result is that all pc machines are freed from explicitly worrying
about smbios setup.
Signed-off-by: Bernhard Beschow <shentey@gmail.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Message-ID: <20240208220349.4948-6-shentey@gmail.com>
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
Conflicts: hw/i386/pc_q35.c, hw/i386/pc_piix.c
due to missing 4d3457fef9 (w/i386/pc: Merge pc_guest_info_init() into pc_machine_initfn())
and different signature of smbios_set_defaults() downstream
Fixup: hw/i386/fw_cfg.c to account for downstream changes smbios_set_defaults()
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/fw_cfg.c | 14 +++++++++++++-
hw/i386/fw_cfg.h | 3 ++-
hw/i386/pc.c | 2 +-
hw/i386/pc_piix.c | 12 ------------
hw/i386/pc_q35.c | 11 -----------
include/hw/i386/pc.h | 1 -
6 files changed, 16 insertions(+), 27 deletions(-)
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index 7362daa45a..6a5466faf0 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -48,15 +48,27 @@ const char *fw_cfg_arch_key_name(uint16_t key)
return NULL;
}
-void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg)
+void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
{
#ifdef CONFIG_SMBIOS
uint8_t *smbios_tables, *smbios_anchor;
size_t smbios_tables_len, smbios_anchor_len;
struct smbios_phys_mem_area *mem_array;
unsigned i, array_count;
+ MachineState *ms = MACHINE(pcms);
+ PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms);
+ MachineClass *mc = MACHINE_GET_CLASS(pcms);
X86CPU *cpu = X86_CPU(ms->possible_cpus->cpus[0].cpu);
+ if (pcmc->smbios_defaults) {
+ /* These values are guest ABI, do not change */
+ smbios_set_defaults("QEMU", mc->desc, mc->name,
+ pcmc->smbios_legacy_mode, pcmc->smbios_uuid_encoded,
+ pcmc->smbios_stream_product,
+ pcmc->smbios_stream_version,
+ pcms->smbios_entry_point_type);
+ }
+
/* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h
index 86ca7c1c0c..1e1de6b4a3 100644
--- a/hw/i386/fw_cfg.h
+++ b/hw/i386/fw_cfg.h
@@ -10,6 +10,7 @@
#define HW_I386_FW_CFG_H
#include "hw/boards.h"
+#include "hw/i386/pc.h"
#include "hw/nvram/fw_cfg.h"
#define FW_CFG_IO_BASE 0x510
@@ -22,7 +23,7 @@
FWCfgState *fw_cfg_arch_create(MachineState *ms,
uint16_t boot_cpus,
uint16_t apic_id_limit);
-void fw_cfg_build_smbios(MachineState *ms, FWCfgState *fw_cfg);
+void fw_cfg_build_smbios(PCMachineState *ms, FWCfgState *fw_cfg);
void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg);
void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index a1faa9e92c..16de2a59e8 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -847,7 +847,7 @@ void pc_machine_done(Notifier *notifier, void *data)
acpi_setup();
if (x86ms->fw_cfg) {
- fw_cfg_build_smbios(MACHINE(pcms), x86ms->fw_cfg);
+ fw_cfg_build_smbios(pcms, x86ms->fw_cfg);
fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg);
/* update FW_CFG_NB_CPUS to account for -device added CPUs */
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 09d02cc91f..7344b35cf1 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -36,7 +36,6 @@
#include "hw/rtc/mc146818rtc.h"
#include "hw/southbridge/piix.h"
#include "hw/display/ramfb.h"
-#include "hw/firmware/smbios.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_ids.h"
#include "hw/usb.h"
@@ -233,17 +232,6 @@ static void pc_init1(MachineState *machine,
pc_guest_info_init(pcms);
- if (pcmc->smbios_defaults) {
- MachineClass *mc = MACHINE_GET_CLASS(machine);
- /* These values are guest ABI, do not change */
- smbios_set_defaults("Red Hat", "KVM",
- mc->desc, pcmc->smbios_legacy_mode,
- pcmc->smbios_uuid_encoded,
- pcmc->smbios_stream_product,
- pcmc->smbios_stream_version,
- pcms->smbios_entry_point_type);
- }
-
/* allocate ram and load rom/bios */
if (!xen_enabled()) {
pc_memory_init(pcms, system_memory, rom_memory, hole64_size);
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index c6967e1846..9a22ff5dd6 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -45,7 +45,6 @@
#include "hw/i386/amd_iommu.h"
#include "hw/i386/intel_iommu.h"
#include "hw/display/ramfb.h"
-#include "hw/firmware/smbios.h"
#include "hw/ide/pci.h"
#include "hw/ide/ahci.h"
#include "hw/intc/ioapic.h"
@@ -201,16 +200,6 @@ static void pc_q35_init(MachineState *machine)
pc_guest_info_init(pcms);
- if (pcmc->smbios_defaults) {
- /* These values are guest ABI, do not change */
- smbios_set_defaults("Red Hat", "KVM",
- mc->desc, pcmc->smbios_legacy_mode,
- pcmc->smbios_uuid_encoded,
- pcmc->smbios_stream_product,
- pcmc->smbios_stream_version,
- pcms->smbios_entry_point_type);
- }
-
/* create pci host bus */
phb = OBJECT(qdev_new(TYPE_Q35_HOST_DEVICE));
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index 37644ede7e..c286c10bc3 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -12,7 +12,6 @@
#include "hw/hotplug.h"
#include "qom/object.h"
#include "hw/i386/sgx-epc.h"
-#include "hw/firmware/smbios.h"
#include "hw/cxl/cxl.h"
#define HPET_INTCAP "hpet-intcap"
--
2.39.3

@ -1,69 +0,0 @@
From c2eafeb32a256cbafb0e65c0380acb478181326e Mon Sep 17 00:00:00 2001
From: Jon Maloy <jmaloy@redhat.com>
Date: Wed, 5 Jun 2024 19:56:51 -0400
Subject: [PATCH 2/4] iotests/244: Don't store data-file with protocol in image
RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 2: EMBARGOED CVE-2024-4467 for rhel-9.4.z (PRDSC)
RH-Jira: https://issues.redhat.com/browse/RHEL-35610
RH-CVE: CVE-2024-4467
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Hanna Czenczek <hczenczek@redhat.com>
RH-Commit: [2/4] ddef095945aa55bb0aacc2a2cb58f9e12ad20d5e
commit 92e00dab8be1570b13172353d77d2af44cb4e22b
Author: Kevin Wolf <kwolf@redhat.com>
Date: Thu Apr 25 14:49:40 2024 +0200
iotests/244: Don't store data-file with protocol in image
We want to disable filename parsing for data files because it's too easy
to abuse in malicious image files. Make the test ready for the change by
passing the data file explicitly in command line options.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Upstream: N/A, embargoed
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
tests/qemu-iotests/244 | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244
index 3e61fa25bb..bb9cc6512f 100755
--- a/tests/qemu-iotests/244
+++ b/tests/qemu-iotests/244
@@ -215,9 +215,22 @@ $QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG"
$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG"
# blkdebug doesn't support copy offloading, so this tests the error path
-$QEMU_IMG amend -f $IMGFMT -o "data_file=blkdebug::$TEST_IMG.data" "$TEST_IMG"
-$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$TEST_IMG"
-$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$TEST_IMG"
+test_img_with_blkdebug="json:{
+ 'driver': 'qcow2',
+ 'file': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG'
+ },
+ 'data-file': {
+ 'driver': 'blkdebug',
+ 'image': {
+ 'driver': 'file',
+ 'filename': '$TEST_IMG.data'
+ }
+ }
+}"
+$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -n -C "$TEST_IMG.src" "$test_img_with_blkdebug"
+$QEMU_IMG compare -f $IMGFMT -F $IMGFMT "$TEST_IMG.src" "$test_img_with_blkdebug"
echo
echo "=== Flushing should flush the data file ==="
--
2.39.3

@ -1,72 +0,0 @@
From 931ab59f39b5e3551b328fe5b0f872df7a19ba05 Mon Sep 17 00:00:00 2001
From: Jon Maloy <jmaloy@redhat.com>
Date: Wed, 5 Jun 2024 19:56:51 -0400
Subject: [PATCH 3/4] iotests/270: Don't store data-file with json: prefix in
image
RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 2: EMBARGOED CVE-2024-4467 for rhel-9.4.z (PRDSC)
RH-Jira: https://issues.redhat.com/browse/RHEL-35610
RH-CVE: CVE-2024-4467
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Hanna Czenczek <hczenczek@redhat.com>
RH-Commit: [3/4] 7a9844fd48e3f3c4d1711ea4fb671c795ca4a1c1
commit 705bcc2819ce8e0f8b9d660a93bc48de26413aec
Author: Kevin Wolf <kwolf@redhat.com>
Date: Thu Apr 25 14:49:40 2024 +0200
iotests/270: Don't store data-file with json: prefix in image
We want to disable filename parsing for data files because it's too easy
to abuse in malicious image files. Make the test ready for the change by
passing the data file explicitly in command line options.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Upstream: N/A, embargoed
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
tests/qemu-iotests/270 | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/tests/qemu-iotests/270 b/tests/qemu-iotests/270
index 74352342db..c37b674aa2 100755
--- a/tests/qemu-iotests/270
+++ b/tests/qemu-iotests/270
@@ -60,8 +60,16 @@ _make_test_img -o cluster_size=2M,data_file="$TEST_IMG.orig" \
# "write" 2G of data without using any space.
# (qemu-img create does not like it, though, because null-co does not
# support image creation.)
-$QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \
- "$TEST_IMG"
+test_img_with_null_data="json:{
+ 'driver': '$IMGFMT',
+ 'file': {
+ 'filename': '$TEST_IMG'
+ },
+ 'data-file': {
+ 'driver': 'null-co',
+ 'size':'4294967296'
+ }
+}"
# This gives us a range of:
# 2^31 - 512 + 768 - 1 = 2^31 + 255 > 2^31
@@ -74,7 +82,7 @@ $QEMU_IMG amend -o data_file="json:{'driver':'null-co',,'size':'4294967296'}" \
# on L2 boundaries, we need large L2 tables; hence the cluster size of
# 2 MB. (Anything from 256 kB should work, though, because then one L2
# table covers 8 GB.)
-$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$TEST_IMG" | _filter_qemu_io
+$QEMU_IO -c "write 768 $((2 ** 31 - 512))" "$test_img_with_null_data" | _filter_qemu_io
_check_test_img
--
2.39.3

@ -1,133 +0,0 @@
From 65d58819ff7b012e43b5f1da1356b559d3f5a962 Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Thu, 14 Mar 2024 17:58:25 +0100
Subject: [PATCH 3/4] iotests: Add test for reset/AioContext switches with NBD
exports
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 231: Fix deadlock and crash during storage migration
RH-Jira: RHEL-28125
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Acked-by: Eric Blake <eblake@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Commit: [3/3] 7266acdf85271d157497bfe452aa6eeb58fbe7c6 (kmwolf/centos-qemu-kvm)
This replicates the scenario in which the bug was reported.
Unfortunately this relies on actually executing a guest (so that the
firmware initialises the virtio-blk device and moves it to its
configured iothread), so this can't make use of the qtest accelerator
like most other test cases. I tried to find a different easy way to
trigger the bug, but couldn't find one.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20240314165825.40261-3-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit e8fce34eccf68a32f4ecf2c6f121ff2ac383d6bf)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
tests/qemu-iotests/tests/iothreads-nbd-export | 66 +++++++++++++++++++
.../tests/iothreads-nbd-export.out | 19 ++++++
2 files changed, 85 insertions(+)
create mode 100755 tests/qemu-iotests/tests/iothreads-nbd-export
create mode 100644 tests/qemu-iotests/tests/iothreads-nbd-export.out
diff --git a/tests/qemu-iotests/tests/iothreads-nbd-export b/tests/qemu-iotests/tests/iothreads-nbd-export
new file mode 100755
index 0000000000..037260729c
--- /dev/null
+++ b/tests/qemu-iotests/tests/iothreads-nbd-export
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+# group: rw quick
+#
+# Copyright (C) 2024 Red Hat, Inc.
+#
+# 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/>.
+#
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
+
+import time
+import qemu
+import iotests
+
+iotests.script_initialize(supported_fmts=['qcow2'],
+ supported_platforms=['linux'])
+
+with iotests.FilePath('disk1.img') as path, \
+ iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \
+ qemu.machine.QEMUMachine(iotests.qemu_prog) as vm:
+
+ img_size = '10M'
+
+ iotests.log('Preparing disk...')
+ iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size)
+ vm.add_args('-blockdev', f'file,node-name=disk-file,filename={path}')
+ vm.add_args('-blockdev', 'qcow2,node-name=disk,file=disk-file')
+ vm.add_args('-object', 'iothread,id=iothread0')
+ vm.add_args('-device',
+ 'virtio-blk,drive=disk,iothread=iothread0,share-rw=on')
+
+ iotests.log('Launching VM...')
+ vm.add_args('-accel', 'kvm', '-accel', 'tcg')
+ #vm.add_args('-accel', 'qtest')
+ vm.launch()
+
+ iotests.log('Exporting to NBD...')
+ iotests.log(vm.qmp('nbd-server-start',
+ addr={'type': 'unix', 'data': {'path': nbd_sock}}))
+ iotests.log(vm.qmp('block-export-add', type='nbd', id='exp0',
+ node_name='disk', writable=True))
+
+ iotests.log('Connecting qemu-img...')
+ qemu_io = iotests.QemuIoInteractive('-f', 'raw',
+ f'nbd+unix:///disk?socket={nbd_sock}')
+
+ iotests.log('Moving the NBD export to a different iothread...')
+ for i in range(0, 10):
+ iotests.log(vm.qmp('system_reset'))
+ time.sleep(0.1)
+
+ iotests.log('Checking that it is still alive...')
+ iotests.log(vm.qmp('query-status'))
+
+ qemu_io.close()
+ vm.shutdown()
diff --git a/tests/qemu-iotests/tests/iothreads-nbd-export.out b/tests/qemu-iotests/tests/iothreads-nbd-export.out
new file mode 100644
index 0000000000..bc514e35e5
--- /dev/null
+++ b/tests/qemu-iotests/tests/iothreads-nbd-export.out
@@ -0,0 +1,19 @@
+Preparing disk...
+Launching VM...
+Exporting to NBD...
+{"return": {}}
+{"return": {}}
+Connecting qemu-img...
+Moving the NBD export to a different iothread...
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+{"return": {}}
+Checking that it is still alive...
+{"return": {"running": true, "status": "running"}}
--
2.39.3

@ -1,275 +0,0 @@
From 4296c41178438f9dffa16c538b0c1a2e28944f4c Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Fri, 17 May 2024 21:50:15 -0500
Subject: [PATCH 4/4] iotests: test NBD+TLS+iothread
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 375: Fix regression on nbd+tls
RH-Jira: RHEL-33754
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [4/4] 5905c09466f4e65f3ff9973b41f42cbe1d75363c (ebblake/qemu-kvm)
Prevent regressions when using NBD with TLS in the presence of
iothreads, adding coverage the fix to qio channels made in the
previous patch.
The shell function pick_unused_port() was copied from
nbdkit.git/tests/functions.sh.in, where it had all authors from Red
Hat, agreeing to the resulting relicensing from 2-clause BSD to GPLv2.
CC: qemu-stable@nongnu.org
CC: "Richard W.M. Jones" <rjones@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240531180639.1392905-6-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit a73c99378022ebb785481e84cfe1e81097546268)
Jira: https://issues.redhat.com/browse/RHEL-33754
Signed-off-by: Eric Blake <eblake@redhat.com>
---
tests/qemu-iotests/tests/nbd-tls-iothread | 168 ++++++++++++++++++
tests/qemu-iotests/tests/nbd-tls-iothread.out | 54 ++++++
2 files changed, 222 insertions(+)
create mode 100755 tests/qemu-iotests/tests/nbd-tls-iothread
create mode 100644 tests/qemu-iotests/tests/nbd-tls-iothread.out
diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread b/tests/qemu-iotests/tests/nbd-tls-iothread
new file mode 100755
index 0000000000..a2fb07206e
--- /dev/null
+++ b/tests/qemu-iotests/tests/nbd-tls-iothread
@@ -0,0 +1,168 @@
+#!/usr/bin/env bash
+# group: rw quick
+#
+# Test of NBD+TLS+iothread
+#
+# Copyright (C) 2024 Red Hat, Inc.
+#
+# 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/>.
+#
+
+# creator
+owner=eblake@redhat.com
+
+seq=`basename $0`
+echo "QA output created by $seq"
+
+status=1 # failure is the default!
+
+_cleanup()
+{
+ _cleanup_qemu
+ _cleanup_test_img
+ rm -f "$dst_image"
+ tls_x509_cleanup
+}
+trap "_cleanup; exit \$status" 0 1 2 3 15
+
+# get standard environment, filters and checks
+cd ..
+. ./common.rc
+. ./common.filter
+. ./common.qemu
+. ./common.tls
+. ./common.nbd
+
+_supported_fmt qcow2 # Hardcoded to qcow2 command line and QMP below
+_supported_proto file
+
+# pick_unused_port
+#
+# Picks and returns an "unused" port, setting the global variable
+# $port.
+#
+# This is inherently racy, but we need it because qemu does not currently
+# permit NBD+TLS over a Unix domain socket
+pick_unused_port ()
+{
+ if ! (ss --version) >/dev/null 2>&1; then
+ _notrun "ss utility required, skipped this test"
+ fi
+
+ # Start at a random port to make it less likely that two parallel
+ # tests will conflict.
+ port=$(( 50000 + (RANDOM%15000) ))
+ while ss -ltn | grep -sqE ":$port\b"; do
+ ((port++))
+ if [ $port -eq 65000 ]; then port=50000; fi
+ done
+ echo picked unused port
+}
+
+tls_x509_init
+
+size=1G
+DST_IMG="$TEST_DIR/dst.qcow2"
+
+echo
+echo "== preparing TLS creds and spare port =="
+
+pick_unused_port
+tls_x509_create_root_ca "ca1"
+tls_x509_create_server "ca1" "server1"
+tls_x509_create_client "ca1" "client1"
+tls_obj_base=tls-creds-x509,id=tls0,verify-peer=true,dir="${tls_dir}"
+
+echo
+echo "== preparing image =="
+
+_make_test_img $size
+$QEMU_IMG create -f qcow2 "$DST_IMG" $size | _filter_img_create
+
+echo
+echo === Starting Src QEMU ===
+echo
+
+_launch_qemu -machine q35 \
+ -object iothread,id=iothread0 \
+ -object "${tls_obj_base}"/client1,endpoint=client \
+ -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true,
+ "bus":"pcie.0"}' \
+ -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0",
+ "bus":"root0", "iothread":"iothread0"}' \
+ -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1",
+ "bus":"virtio_scsi_pci0.0"}' \
+ -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false},
+ "filename":"'"$TEST_IMG"'", "node-name":"drive_sys1"}' \
+ -blockdev '{"driver":"qcow2", "node-name":"drive_image1",
+ "file":"drive_sys1"}'
+h1=$QEMU_HANDLE
+_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return'
+
+echo
+echo === Starting Dst VM2 ===
+echo
+
+_launch_qemu -machine q35 \
+ -object iothread,id=iothread0 \
+ -object "${tls_obj_base}"/server1,endpoint=server \
+ -device '{"driver":"pcie-root-port", "id":"root0", "multifunction":true,
+ "bus":"pcie.0"}' \
+ -device '{"driver":"virtio-scsi-pci", "id":"virtio_scsi_pci0",
+ "bus":"root0", "iothread":"iothread0"}' \
+ -device '{"driver":"scsi-hd", "id":"image1", "drive":"drive_image1",
+ "bus":"virtio_scsi_pci0.0"}' \
+ -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false},
+ "filename":"'"$DST_IMG"'", "node-name":"drive_sys1"}' \
+ -blockdev '{"driver":"qcow2", "node-name":"drive_image1",
+ "file":"drive_sys1"}' \
+ -incoming defer
+h2=$QEMU_HANDLE
+_send_qemu_cmd $h2 '{"execute": "qmp_capabilities"}' 'return'
+
+echo
+echo === Dst VM: Enable NBD server for incoming storage migration ===
+echo
+
+_send_qemu_cmd $h2 '{"execute": "nbd-server-start", "arguments":
+ {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": "'$port'"}},
+ "tls-creds": "tls0"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g"
+_send_qemu_cmd $h2 '{"execute": "block-export-add", "arguments":
+ {"node-name": "drive_image1", "type": "nbd", "writable": true,
+ "id": "drive_image1"}}' '{"return": {}}'
+
+echo
+echo === Src VM: Mirror to dst NBD for outgoing storage migration ===
+echo
+
+_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments":
+ {"node-name": "mirror", "driver": "nbd",
+ "server": {"type": "inet", "host": "127.0.0.1", "port": "'$port'"},
+ "export": "drive_image1", "tls-creds": "tls0",
+ "tls-hostname": "127.0.0.1"}}' '{"return": {}}' | sed "s/\"$port\"/PORT/g"
+_send_qemu_cmd $h1 '{"execute": "blockdev-mirror", "arguments":
+ {"sync": "full", "device": "drive_image1", "target": "mirror",
+ "job-id": "drive_image1_53"}}' '{"return": {}}'
+_timed_wait_for $h1 '"ready"'
+
+echo
+echo === Cleaning up ===
+echo
+
+_send_qemu_cmd $h1 '{"execute":"quit"}' ''
+_send_qemu_cmd $h2 '{"execute":"quit"}' ''
+
+echo "*** done"
+rm -f $seq.full
+status=0
diff --git a/tests/qemu-iotests/tests/nbd-tls-iothread.out b/tests/qemu-iotests/tests/nbd-tls-iothread.out
new file mode 100644
index 0000000000..1d83d4f903
--- /dev/null
+++ b/tests/qemu-iotests/tests/nbd-tls-iothread.out
@@ -0,0 +1,54 @@
+QA output created by nbd-tls-iothread
+
+== preparing TLS creds and spare port ==
+picked unused port
+Generating a self signed certificate...
+Generating a signed certificate...
+Generating a signed certificate...
+
+== preparing image ==
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824
+Formatting 'TEST_DIR/dst.IMGFMT', fmt=IMGFMT size=1073741824
+
+=== Starting Src QEMU ===
+
+{"execute": "qmp_capabilities"}
+{"return": {}}
+
+=== Starting Dst VM2 ===
+
+{"execute": "qmp_capabilities"}
+{"return": {}}
+
+=== Dst VM: Enable NBD server for incoming storage migration ===
+
+{"execute": "nbd-server-start", "arguments":
+ {"addr": {"type": "inet", "data": {"host": "127.0.0.1", "port": PORT}},
+ "tls-creds": "tls0"}}
+{"return": {}}
+{"execute": "block-export-add", "arguments":
+ {"node-name": "drive_image1", "type": "nbd", "writable": true,
+ "id": "drive_image1"}}
+{"return": {}}
+
+=== Src VM: Mirror to dst NBD for outgoing storage migration ===
+
+{"execute": "blockdev-add", "arguments":
+ {"node-name": "mirror", "driver": "nbd",
+ "server": {"type": "inet", "host": "127.0.0.1", "port": PORT},
+ "export": "drive_image1", "tls-creds": "tls0",
+ "tls-hostname": "127.0.0.1"}}
+{"return": {}}
+{"execute": "blockdev-mirror", "arguments":
+ {"sync": "full", "device": "drive_image1", "target": "mirror",
+ "job-id": "drive_image1_53"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "drive_image1_53"}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "drive_image1_53"}}
+{"return": {}}
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "drive_image1_53"}}
+
+=== Cleaning up ===
+
+{"execute":"quit"}
+{"execute":"quit"}
+*** done
--
2.39.3

@ -1,76 +0,0 @@
From 8a8fa4ab4dc05502550ca207926cd0c93a3341ea Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Mon, 8 Apr 2024 12:43:49 +0200
Subject: [PATCH] kvm: error out of kvm_irqchip_add_msi_route() in case of full
route table
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 374: kvm: error out of kvm_irqchip_add_msi_route() in case of full route table
RH-Jira: RHEL-32990
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] df31f2d0cafe10a1ac22a2bebb85dc17c1e891e0
RH-Jira: RHEL-32990
subj is calling kvm_add_routing_entry() which simply extends
KVMState::irq_routes::entries[]
but doesn't check if number of routes goes beyond limit the kernel
is willing to accept. Which later leads toi the assert
qemu-kvm: ../accel/kvm/kvm-all.c:1833: kvm_irqchip_commit_routes: Assertion `ret == 0' failed
typically it happens during guest boot for large enough guest
Reproduced with:
./qemu --enable-kvm -m 8G -smp 64 -machine pc \
`for b in {1..2}; do echo -n "-device pci-bridge,id=pci$b,chassis_nr=$b ";
for i in {0..31}; do touch /tmp/vblk$b$i;
echo -n "-drive file=/tmp/vblk$b$i,if=none,id=drive$b$i,format=raw
-device virtio-blk-pci,drive=drive$b$i,bus=pci$b ";
done; done`
While crash at boot time is bad, the same might happen at hotplug time
which is unacceptable.
So instead calling kvm_add_routing_entry() unconditionally, check first
that number of routes won't exceed KVM_CAP_IRQ_ROUTING. This way virtio
device insteads killin qemu, will gracefully fail to initialize device
as expected with following warnings on console:
virtio-blk failed to set guest notifier (-28), ensure -accel kvm is set.
virtio_bus_start_ioeventfd: failed. Fallback to userspace (slower).
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
accel/kvm/kvm-all.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c
index e39a810a4e..f1a4564cbd 100644
--- a/accel/kvm/kvm-all.c
+++ b/accel/kvm/kvm-all.c
@@ -2000,12 +2000,17 @@ int kvm_irqchip_add_msi_route(KVMRouteChange *c, int vector, PCIDevice *dev)
return -EINVAL;
}
- trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A",
- vector, virq);
+ if (s->irq_routes->nr < s->gsi_count) {
+ trace_kvm_irqchip_add_msi_route(dev ? dev->name : (char *)"N/A",
+ vector, virq);
- kvm_add_routing_entry(s, &kroute);
- kvm_arch_add_msi_route_post(&kroute, vector, dev);
- c->changes++;
+ kvm_add_routing_entry(s, &kroute);
+ kvm_arch_add_msi_route_post(&kroute, vector, dev);
+ c->changes++;
+ } else {
+ kvm_irqchip_release_virq(s, virq);
+ return -ENOSPC;
+ }
return virq;
}
--
2.39.3

@ -1,126 +0,0 @@
From 9d00965910d5a7818ffe55b18d3e9dd4c7210e1b Mon Sep 17 00:00:00 2001
From: Prasad Pandit <pjp@fedoraproject.org>
Date: Thu, 25 Apr 2024 12:34:12 +0530
Subject: [PATCH 4/4] linux-aio: add IO_CMD_FDSYNC command support
RH-Author: Prasad Pandit <None>
RH-MergeRequest: 378: linux-aio: add IO_CMD_FDSYNC command support
RH-Jira: RHEL-43261
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/1] 3b80d4a162aad1a87322078a7d7b060a9496035b
Libaio defines IO_CMD_FDSYNC command to sync all outstanding
asynchronous I/O operations, by flushing out file data to the
disk storage. Enable linux-aio to submit such aio request.
When using aio=native without fdsync() support, QEMU creates
pthreads, and destroying these pthreads results in TLB flushes.
In a real-time guest environment, TLB flushes cause a latency
spike. This patch helps to avoid such spikes.
Jira: https://issues.redhat.com/browse/RHEL-43261
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Signed-off-by: Prasad Pandit <pjp@fedoraproject.org>
Message-ID: <20240425070412.37248-1-ppandit@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 24687abf237e3c15816d689a8e4b08d7c3190dcb)
Signed-off-by: Prasad Pandit <pjp@fedoraproject.org>
---
block/file-posix.c | 9 +++++++++
block/linux-aio.c | 21 ++++++++++++++++++++-
include/block/raw-aio.h | 1 +
3 files changed, 30 insertions(+), 1 deletion(-)
diff --git a/block/file-posix.c b/block/file-posix.c
index 35684f7e21..9831b08fb6 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -159,6 +159,7 @@ typedef struct BDRVRawState {
bool has_discard:1;
bool has_write_zeroes:1;
bool use_linux_aio:1;
+ bool has_laio_fdsync:1;
bool use_linux_io_uring:1;
int page_cache_inconsistent; /* errno from fdatasync failure */
bool has_fallocate;
@@ -718,6 +719,9 @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
ret = -EINVAL;
goto fail;
}
+ if (s->use_linux_aio) {
+ s->has_laio_fdsync = laio_has_fdsync(s->fd);
+ }
#else
if (s->use_linux_aio) {
error_setg(errp, "aio=native was specified, but is not supported "
@@ -2599,6 +2603,11 @@ static int coroutine_fn raw_co_flush_to_disk(BlockDriverState *bs)
if (raw_check_linux_io_uring(s)) {
return luring_co_submit(bs, s->fd, 0, NULL, QEMU_AIO_FLUSH);
}
+#endif
+#ifdef CONFIG_LINUX_AIO
+ if (s->has_laio_fdsync && raw_check_linux_aio(s)) {
+ return laio_co_submit(s->fd, 0, NULL, QEMU_AIO_FLUSH, 0);
+ }
#endif
return raw_thread_pool_submit(handle_aiocb_flush, &acb);
}
diff --git a/block/linux-aio.c b/block/linux-aio.c
index ec05d946f3..e3b5ec9aba 100644
--- a/block/linux-aio.c
+++ b/block/linux-aio.c
@@ -384,6 +384,9 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
case QEMU_AIO_READ:
io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset);
break;
+ case QEMU_AIO_FLUSH:
+ io_prep_fdsync(iocbs, fd);
+ break;
/* Currently Linux kernel does not support other operations */
default:
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
@@ -412,7 +415,7 @@ int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
AioContext *ctx = qemu_get_current_aio_context();
struct qemu_laiocb laiocb = {
.co = qemu_coroutine_self(),
- .nbytes = qiov->size,
+ .nbytes = qiov ? qiov->size : 0,
.ctx = aio_get_linux_aio(ctx),
.ret = -EINPROGRESS,
.is_read = (type == QEMU_AIO_READ),
@@ -486,3 +489,19 @@ void laio_cleanup(LinuxAioState *s)
}
g_free(s);
}
+
+bool laio_has_fdsync(int fd)
+{
+ struct iocb cb;
+ struct iocb *cbs[] = {&cb, NULL};
+
+ io_context_t ctx = 0;
+ io_setup(1, &ctx);
+
+ /* check if host kernel supports IO_CMD_FDSYNC */
+ io_prep_fdsync(&cb, fd);
+ int ret = io_submit(ctx, 1, cbs);
+
+ io_destroy(ctx);
+ return (ret == -EINVAL) ? false : true;
+}
diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h
index 0f63c2800c..3166903a56 100644
--- a/include/block/raw-aio.h
+++ b/include/block/raw-aio.h
@@ -60,6 +60,7 @@ void laio_cleanup(LinuxAioState *s);
int coroutine_fn laio_co_submit(int fd, uint64_t offset, QEMUIOVector *qiov,
int type, uint64_t dev_max_batch);
+bool laio_has_fdsync(int);
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context);
void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context);
#endif
--
2.39.3

@ -1,90 +0,0 @@
From 21cadc8ed200ad9af13b217b441b9f12cd2163ee Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Wed, 13 Mar 2024 16:30:00 +0100
Subject: [PATCH 1/4] mirror: Don't call job_pause_point() under graph lock
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 231: Fix deadlock and crash during storage migration
RH-Jira: RHEL-28125
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Acked-by: Eric Blake <eblake@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Commit: [1/3] 093910fcd5ea1516d0ed48a9cd73dad6170590f3 (kmwolf/centos-qemu-kvm)
Calling job_pause_point() while holding the graph reader lock
potentially results in a deadlock: bdrv_graph_wrlock() first drains
everything, including the mirror job, which pauses it. The job is only
unpaused at the end of the drain section, which is when the graph writer
lock has been successfully taken. However, if the job happens to be
paused at a pause point where it still holds the reader lock, the writer
lock can't be taken as long as the job is still paused.
Mark job_pause_point() as GRAPH_UNLOCKED and fix mirror accordingly.
Cc: qemu-stable@nongnu.org
Buglink: https://issues.redhat.com/browse/RHEL-28125
Fixes: 004915a96a7a ("block: Protect bs->backing with graph_lock")
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20240313153000.33121-1-kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit ae5a40e8581185654a667fbbf7e4adbc2a2a3e45)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
block/mirror.c | 10 ++++++----
include/qemu/job.h | 2 +-
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/block/mirror.c b/block/mirror.c
index 5145eb53e1..1bdce3b657 100644
--- a/block/mirror.c
+++ b/block/mirror.c
@@ -479,9 +479,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
return bytes_handled;
}
-static void coroutine_fn GRAPH_RDLOCK mirror_iteration(MirrorBlockJob *s)
+static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s)
{
- BlockDriverState *source = s->mirror_top_bs->backing->bs;
+ BlockDriverState *source;
MirrorOp *pseudo_op;
int64_t offset;
/* At least the first dirty chunk is mirrored in one iteration. */
@@ -489,6 +489,10 @@ static void coroutine_fn GRAPH_RDLOCK mirror_iteration(MirrorBlockJob *s)
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES);
+ bdrv_graph_co_rdlock();
+ source = s->mirror_top_bs->backing->bs;
+ bdrv_graph_co_rdunlock();
+
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
offset = bdrv_dirty_iter_next(s->dbi);
if (offset < 0) {
@@ -1066,9 +1070,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
mirror_wait_for_free_in_flight_slot(s);
continue;
} else if (cnt != 0) {
- bdrv_graph_co_rdlock();
mirror_iteration(s);
- bdrv_graph_co_rdunlock();
}
}
diff --git a/include/qemu/job.h b/include/qemu/job.h
index 9ea98b5927..2b873f2576 100644
--- a/include/qemu/job.h
+++ b/include/qemu/job.h
@@ -483,7 +483,7 @@ void job_enter(Job *job);
*
* Called with job_mutex *not* held.
*/
-void coroutine_fn job_pause_point(Job *job);
+void coroutine_fn GRAPH_UNLOCKED job_pause_point(Job *job);
/**
* @job: The job that calls the function.
--
2.39.3

@ -1,184 +0,0 @@
From 3311ddaffa78ad8d2c40c86cd69461ebeabe1052 Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Tue, 6 Aug 2024 13:53:00 -0500
Subject: [PATCH 3/5] nbd/server: CVE-2024-7409: Cap default max-connections to
100
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
RH-Jira: RHEL-52616
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [2/4] 17c9c81282e180485e2f2d584a3e0c73a6fbd66a (ebblake/qemu-kvm)
Allowing an unlimited number of clients to any web service is a recipe
for a rudimentary denial of service attack: the client merely needs to
open lots of sockets without closing them, until qemu no longer has
any more fds available to allocate.
For qemu-nbd, we default to allowing only 1 connection unless more are
explicitly asked for (-e or --shared); this was historically picked as
a nice default (without an explicit -t, a non-persistent qemu-nbd goes
away after a client disconnects, without needing any additional
follow-up commands), and we are not going to change that interface now
(besides, someday we want to point people towards qemu-storage-daemon
instead of qemu-nbd).
But for qemu proper, and the newer qemu-storage-daemon, the QMP
nbd-server-start command has historically had a default of unlimited
number of connections, in part because unlike qemu-nbd it is
inherently persistent until nbd-server-stop. Allowing multiple client
sockets is particularly useful for clients that can take advantage of
MULTI_CONN (creating parallel sockets to increase throughput),
although known clients that do so (such as libnbd's nbdcopy) typically
use only 8 or 16 connections (the benefits of scaling diminish once
more sockets are competing for kernel attention). Picking a number
large enough for typical use cases, but not unlimited, makes it
slightly harder for a malicious client to perform a denial of service
merely by opening lots of connections withot progressing through the
handshake.
This change does not eliminate CVE-2024-7409 on its own, but reduces
the chance for fd exhaustion or unlimited memory usage as an attack
surface. On the other hand, by itself, it makes it more obvious that
with a finite limit, we have the problem of an unauthenticated client
holding 100 fds opened as a way to block out a legitimate client from
being able to connect; thus, later patches will further add timeouts
to reject clients that are not making progress.
This is an INTENTIONAL change in behavior, and will break any client
of nbd-server-start that was not passing an explicit max-connections
parameter, yet expects more than 100 simultaneous connections. We are
not aware of any such client (as stated above, most clients aware of
MULTI_CONN get by just fine on 8 or 16 connections, and probably cope
with later connections failing by relying on the earlier connections;
libvirt has not yet been passing max-connections, but generally
creates NBD servers with the intent for a single client for the sake
of live storage migration; meanwhile, the KubeSAN project anticipates
a large cluster sharing multiple clients [up to 8 per node, and up to
100 nodes in a cluster], but it currently uses qemu-nbd with an
explicit --shared=0 rather than qemu-storage-daemon with
nbd-server-start).
We considered using a deprecation period (declare that omitting
max-parameters is deprecated, and make it mandatory in 3 releases -
then we don't need to pick an arbitrary default); that has zero risk
of breaking any apps that accidentally depended on more than 100
connections, and where such breakage might not be noticed under unit
testing but only under the larger loads of production usage. But it
does not close the denial-of-service hole until far into the future,
and requires all apps to change to add the parameter even if 100 was
good enough. It also has a drawback that any app (like libvirt) that
is accidentally relying on an unlimited default should seriously
consider their own CVE now, at which point they are going to change to
pass explicit max-connections sooner than waiting for 3 qemu releases.
Finally, if our changed default breaks an app, that app can always
pass in an explicit max-parameters with a larger value.
It is also intentional that the HMP interface to nbd-server-start is
not changed to expose max-connections (any client needing to fine-tune
things should be using QMP).
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-12-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[ericb: Expand commit message to summarize Dan's argument for why we
break corner-case back-compat behavior without a deprecation period]
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit c8a76dbd90c2f48df89b75bef74917f90a59b623)
Jira: https://issues.redhat.com/browse/RHEL-52616
Signed-off-by: Eric Blake <eblake@redhat.com>
---
block/monitor/block-hmp-cmds.c | 3 ++-
blockdev-nbd.c | 8 ++++++++
include/block/nbd.h | 7 +++++++
qapi/block-export.json | 4 ++--
4 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
index bdbb5cb141..2d7b4d80c8 100644
--- a/block/monitor/block-hmp-cmds.c
+++ b/block/monitor/block-hmp-cmds.c
@@ -402,7 +402,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict)
goto exit;
}
- nbd_server_start(addr, NULL, NULL, 0, &local_err);
+ nbd_server_start(addr, NULL, NULL, NBD_DEFAULT_MAX_CONNECTIONS,
+ &local_err);
qapi_free_SocketAddress(addr);
if (local_err != NULL) {
goto exit;
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 267a1de903..24ba5382db 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -170,6 +170,10 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds,
void nbd_server_start_options(NbdServerOptions *arg, Error **errp)
{
+ if (!arg->has_max_connections) {
+ arg->max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
+ }
+
nbd_server_start(arg->addr, arg->tls_creds, arg->tls_authz,
arg->max_connections, errp);
}
@@ -182,6 +186,10 @@ void qmp_nbd_server_start(SocketAddressLegacy *addr,
{
SocketAddress *addr_flat = socket_address_flatten(addr);
+ if (!has_max_connections) {
+ max_connections = NBD_DEFAULT_MAX_CONNECTIONS;
+ }
+
nbd_server_start(addr_flat, tls_creds, tls_authz, max_connections, errp);
qapi_free_SocketAddress(addr_flat);
}
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 1d4d65922d..d4f8b21aec 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -39,6 +39,13 @@ extern const BlockExportDriver blk_exp_nbd;
*/
#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10
+/*
+ * NBD_DEFAULT_MAX_CONNECTIONS: Number of client sockets to allow at
+ * once; must be large enough to allow a MULTI_CONN-aware client like
+ * nbdcopy to create its typical number of 8-16 sockets.
+ */
+#define NBD_DEFAULT_MAX_CONNECTIONS 100
+
/* Handshake phase structs - this struct is passed on the wire */
typedef struct NBDOption {
diff --git a/qapi/block-export.json b/qapi/block-export.json
index 7874a49ba7..1d255d77e3 100644
--- a/qapi/block-export.json
+++ b/qapi/block-export.json
@@ -28,7 +28,7 @@
# @max-connections: The maximum number of connections to allow at the
# same time, 0 for unlimited. Setting this to 1 also stops the
# server from advertising multiple client support (since 5.2;
-# default: 0)
+# default: 100)
#
# Since: 4.2
##
@@ -63,7 +63,7 @@
# @max-connections: The maximum number of connections to allow at the
# same time, 0 for unlimited. Setting this to 1 also stops the
# server from advertising multiple client support (since 5.2;
-# default: 0).
+# default: 100).
#
# Returns: error if the server is already running.
#
--
2.39.3

@ -1,173 +0,0 @@
From 1c2fe81349a99cf0c28549426c7ad0c06d942ae3 Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Wed, 7 Aug 2024 12:23:13 -0500
Subject: [PATCH 5/5] nbd/server: CVE-2024-7409: Close stray clients at
server-stop
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
RH-Jira: RHEL-52616
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [4/4] 54d56c0d92d92b2b6a0f81a01853881286beae0e (ebblake/qemu-kvm)
A malicious client can attempt to connect to an NBD server, and then
intentionally delay progress in the handshake, including if it does
not know the TLS secrets. Although the previous two patches reduce
this behavior by capping the default max-connections parameter and
killing slow clients, they did not eliminate the possibility of a
client waiting to close the socket until after the QMP nbd-server-stop
command is executed, at which point qemu would SEGV when trying to
dereference the NULL nbd_server global which is no longer present.
This amounts to a denial of service attack. Worse, if another NBD
server is started before the malicious client disconnects, I cannot
rule out additional adverse effects when the old client interferes
with the connection count of the new server (although the most likely
is a crash due to an assertion failure when checking
nbd_server->connections > 0).
For environments without this patch, the CVE can be mitigated by
ensuring (such as via a firewall) that only trusted clients can
connect to an NBD server. Note that using frameworks like libvirt
that ensure that TLS is used and that nbd-server-stop is not executed
while any trusted clients are still connected will only help if there
is also no possibility for an untrusted client to open a connection
but then stall on the NBD handshake.
Given the previous patches, it would be possible to guarantee that no
clients remain connected by having nbd-server-stop sleep for longer
than the default handshake deadline before finally freeing the global
nbd_server object, but that could make QMP non-responsive for a long
time. So intead, this patch fixes the problem by tracking all client
sockets opened while the server is running, and forcefully closing any
such sockets remaining without a completed handshake at the time of
nbd-server-stop, then waiting until the coroutines servicing those
sockets notice the state change. nbd-server-stop now has a second
AIO_WAIT_WHILE_UNLOCKED (the first is indirectly through the
blk_exp_close_all_type() that disconnects all clients that completed
handshakes), but forced socket shutdown is enough to progress the
coroutines and quickly tear down all clients before the server is
freed, thus finally fixing the CVE.
This patch relies heavily on the fact that nbd/server.c guarantees
that it only calls nbd_blockdev_client_closed() from the main loop
(see the assertion in nbd_client_put() and the hoops used in
nbd_client_put_nonzero() to achieve that); if we did not have that
guarantee, we would also need a mutex protecting our accesses of the
list of connections to survive re-entrancy from independent iothreads.
Although I did not actually try to test old builds, it looks like this
problem has existed since at least commit 862172f45c (v2.12.0, 2017) -
even back when that patch started using a QIONetListener to handle
listening on multiple sockets, nbd_server_free() was already unaware
that the nbd_blockdev_client_closed callback can be reached later by a
client thread that has not completed handshakes (and therefore the
client's socket never got added to the list closed in
nbd_export_close_all), despite that patch intentionally tearing down
the QIONetListener to prevent new clients.
Reported-by: Alexander Ivanov <alexander.ivanov@virtuozzo.com>
Fixes: CVE-2024-7409
CC: qemu-stable@nongnu.org
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-14-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit 3e7ef738c8462c45043a1d39f702a0990406a3b3)
Jira: https://issues.redhat.com/browse/RHEL-52616
Signed-off-by: Eric Blake <eblake@redhat.com>
---
blockdev-nbd.c | 35 ++++++++++++++++++++++++++++++++++-
1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 24ba5382db..f73409ae49 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -21,12 +21,18 @@
#include "io/channel-socket.h"
#include "io/net-listener.h"
+typedef struct NBDConn {
+ QIOChannelSocket *cioc;
+ QLIST_ENTRY(NBDConn) next;
+} NBDConn;
+
typedef struct NBDServerData {
QIONetListener *listener;
QCryptoTLSCreds *tlscreds;
char *tlsauthz;
uint32_t max_connections;
uint32_t connections;
+ QLIST_HEAD(, NBDConn) conns;
} NBDServerData;
static NBDServerData *nbd_server;
@@ -51,6 +57,14 @@ int nbd_server_max_connections(void)
static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
{
+ NBDConn *conn = nbd_client_owner(client);
+
+ assert(qemu_in_main_thread() && nbd_server);
+
+ object_unref(OBJECT(conn->cioc));
+ QLIST_REMOVE(conn, next);
+ g_free(conn);
+
nbd_client_put(client);
assert(nbd_server->connections > 0);
nbd_server->connections--;
@@ -60,14 +74,20 @@ static void nbd_blockdev_client_closed(NBDClient *client, bool ignored)
static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
gpointer opaque)
{
+ NBDConn *conn = g_new0(NBDConn, 1);
+
+ assert(qemu_in_main_thread() && nbd_server);
nbd_server->connections++;
+ object_ref(OBJECT(cioc));
+ conn->cioc = cioc;
+ QLIST_INSERT_HEAD(&nbd_server->conns, conn, next);
nbd_update_server_watch(nbd_server);
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
/* TODO - expose handshake timeout as QMP option */
nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
nbd_server->tlscreds, nbd_server->tlsauthz,
- nbd_blockdev_client_closed, NULL);
+ nbd_blockdev_client_closed, conn);
}
static void nbd_update_server_watch(NBDServerData *s)
@@ -81,12 +101,25 @@ static void nbd_update_server_watch(NBDServerData *s)
static void nbd_server_free(NBDServerData *server)
{
+ NBDConn *conn, *tmp;
+
if (!server) {
return;
}
+ /*
+ * Forcefully close the listener socket, and any clients that have
+ * not yet disconnected on their own.
+ */
qio_net_listener_disconnect(server->listener);
object_unref(OBJECT(server->listener));
+ QLIST_FOREACH_SAFE(conn, &server->conns, next, tmp) {
+ qio_channel_shutdown(QIO_CHANNEL(conn->cioc), QIO_CHANNEL_SHUTDOWN_BOTH,
+ NULL);
+ }
+
+ AIO_WAIT_WHILE_UNLOCKED(NULL, server->connections > 0);
+
if (server->tlscreds) {
object_unref(OBJECT(server->tlscreds));
}
--
2.39.3

@ -1,134 +0,0 @@
From 71c525e45f3f2a421ad651bc50eff94be925b36c Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Thu, 8 Aug 2024 16:05:08 -0500
Subject: [PATCH 4/5] nbd/server: CVE-2024-7409: Drop non-negotiating clients
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
RH-Jira: RHEL-52616
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [3/4] 4394e60a8733cd57ee6c8cb140171c7f61cc13a3 (ebblake/qemu-kvm)
A client that opens a socket but does not negotiate is merely hogging
qemu's resources (an open fd and a small amount of memory); and a
malicious client that can access the port where NBD is listening can
attempt a denial of service attack by intentionally opening and
abandoning lots of unfinished connections. The previous patch put a
default bound on the number of such ongoing connections, but once that
limit is hit, no more clients can connect (including legitimate ones).
The solution is to insist that clients complete handshake within a
reasonable time limit, defaulting to 10 seconds. A client that has
not successfully completed NBD_OPT_GO by then (including the case of
where the client didn't know TLS credentials to even reach the point
of NBD_OPT_GO) is wasting our time and does not deserve to stay
connected. Later patches will allow fine-tuning the limit away from
the default value (including disabling it for doing integration
testing of the handshake process itself).
Note that this patch in isolation actually makes it more likely to see
qemu SEGV after nbd-server-stop, as any client socket still connected
when the server shuts down will now be closed after 10 seconds rather
than at the client's whims. That will be addressed in the next patch.
For a demo of this patch in action:
$ qemu-nbd -f raw -r -t -e 10 file &
$ nbdsh --opt-mode -c '
H = list()
for i in range(20):
print(i)
H.insert(i, nbd.NBD())
H[i].set_opt_mode(True)
H[i].connect_uri("nbd://localhost")
'
$ kill $!
where later connections get to start progressing once earlier ones are
forcefully dropped for taking too long, rather than hanging.
Suggested-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-13-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[eblake: rebase to changes earlier in series, reduce scope of timer]
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit b9b72cb3ce15b693148bd09cef7e50110566d8a0)
Jira: https://issues.redhat.com/browse/RHEL-52616
Signed-off-by: Eric Blake <eblake@redhat.com>
---
nbd/server.c | 28 +++++++++++++++++++++++++++-
nbd/trace-events | 1 +
2 files changed, 28 insertions(+), 1 deletion(-)
diff --git a/nbd/server.c b/nbd/server.c
index e50012499f..39285cc971 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -3186,22 +3186,48 @@ static void nbd_client_receive_next_request(NBDClient *client)
}
}
+static void nbd_handshake_timer_cb(void *opaque)
+{
+ QIOChannel *ioc = opaque;
+
+ trace_nbd_handshake_timer_cb();
+ qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL);
+}
+
static coroutine_fn void nbd_co_client_start(void *opaque)
{
NBDClient *client = opaque;
Error *local_err = NULL;
+ QEMUTimer *handshake_timer = NULL;
qemu_co_mutex_init(&client->send_lock);
- /* TODO - utilize client->handshake_max_secs */
+ /*
+ * Create a timer to bound the time spent in negotiation. If the
+ * timer expires, it is likely nbd_negotiate will fail because the
+ * socket was shutdown.
+ */
+ if (client->handshake_max_secs > 0) {
+ handshake_timer = aio_timer_new(qemu_get_aio_context(),
+ QEMU_CLOCK_REALTIME,
+ SCALE_NS,
+ nbd_handshake_timer_cb,
+ client->sioc);
+ timer_mod(handshake_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_REALTIME) +
+ client->handshake_max_secs * NANOSECONDS_PER_SECOND);
+ }
+
if (nbd_negotiate(client, &local_err)) {
if (local_err) {
error_report_err(local_err);
}
+ timer_free(handshake_timer);
client_close(client, false);
return;
}
+ timer_free(handshake_timer);
WITH_QEMU_LOCK_GUARD(&client->lock) {
nbd_client_receive_next_request(client);
}
diff --git a/nbd/trace-events b/nbd/trace-events
index 00ae3216a1..cbd0a4ab7e 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -76,6 +76,7 @@ nbd_co_receive_request_payload_received(uint64_t cookie, uint64_t len) "Payload
nbd_co_receive_ext_payload_compliance(uint64_t from, uint64_t len) "client sent non-compliant write without payload flag: from=0x%" PRIx64 ", len=0x%" PRIx64
nbd_co_receive_align_compliance(const char *op, uint64_t from, uint64_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx64 ", align=0x%" PRIx32
nbd_trip(void) "Reading request"
+nbd_handshake_timer_cb(void) "client took too long to negotiate"
# client-connection.c
nbd_connect_thread_sleep(uint64_t timeout) "timeout %" PRIu64
--
2.39.3

@ -1,95 +0,0 @@
From dc4dd11233522d8195782a2196aaae44bd924575 Mon Sep 17 00:00:00 2001
From: Kevin Wolf <kwolf@redhat.com>
Date: Thu, 14 Mar 2024 17:58:24 +0100
Subject: [PATCH 2/4] nbd/server: Fix race in draining the export
RH-Author: Kevin Wolf <kwolf@redhat.com>
RH-MergeRequest: 231: Fix deadlock and crash during storage migration
RH-Jira: RHEL-28125
RH-Acked-by: Hanna Czenczek <hreitz@redhat.com>
RH-Acked-by: Eric Blake <eblake@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Commit: [2/3] 036e1b171986099904dee468c59d77437e3d3d61 (kmwolf/centos-qemu-kvm)
When draining an NBD export, nbd_drained_begin() first sets
client->quiescing so that nbd_client_receive_next_request() won't start
any new request coroutines. Then nbd_drained_poll() tries to makes sure
that we wait for any existing request coroutines by checking that
client->nb_requests has become 0.
However, there is a small window between creating a new request
coroutine and increasing client->nb_requests. If a coroutine is in this
state, it won't be waited for and drain returns too early.
In the context of switching to a different AioContext, this means that
blk_aio_attached() will see client->recv_coroutine != NULL and fail its
assertion.
Fix this by increasing client->nb_requests immediately when starting the
coroutine. Doing this after the checks if we should create a new
coroutine is okay because client->lock is held.
Cc: qemu-stable@nongnu.org
Fixes: fd6afc501a01 ("nbd/server: Use drained block ops to quiesce the server")
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20240314165825.40261-2-kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
(cherry picked from commit 9c707525cbb1dd1e56876e45c70c0c08f2876d41)
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
---
nbd/server.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/nbd/server.c b/nbd/server.c
index 941832f178..c3484cc1eb 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -3007,8 +3007,8 @@ static coroutine_fn int nbd_handle_request(NBDClient *client,
/* Owns a reference to the NBDClient passed as opaque. */
static coroutine_fn void nbd_trip(void *opaque)
{
- NBDClient *client = opaque;
- NBDRequestData *req = NULL;
+ NBDRequestData *req = opaque;
+ NBDClient *client = req->client;
NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
int ret;
Error *local_err = NULL;
@@ -3037,8 +3037,6 @@ static coroutine_fn void nbd_trip(void *opaque)
goto done;
}
- req = nbd_request_get(client);
-
/*
* nbd_co_receive_request() returns -EAGAIN when nbd_drained_begin() has
* set client->quiescing but by the time we get back nbd_drained_end() may
@@ -3112,9 +3110,7 @@ static coroutine_fn void nbd_trip(void *opaque)
}
done:
- if (req) {
- nbd_request_put(req);
- }
+ nbd_request_put(req);
qemu_mutex_unlock(&client->lock);
@@ -3143,10 +3139,13 @@ disconnect:
*/
static void nbd_client_receive_next_request(NBDClient *client)
{
+ NBDRequestData *req;
+
if (!client->recv_coroutine && client->nb_requests < MAX_NBD_REQUESTS &&
!client->quiescing) {
nbd_client_get(client);
- client->recv_coroutine = qemu_coroutine_create(nbd_trip, client);
+ req = nbd_request_get(client);
+ client->recv_coroutine = qemu_coroutine_create(nbd_trip, req);
aio_co_schedule(client->exp->common.ctx, client->recv_coroutine);
}
}
--
2.39.3

@ -1,329 +0,0 @@
From d1dd79b558fb9b23ae14165ec8edf0085e927091 Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Mon, 8 Apr 2024 11:00:44 -0500
Subject: [PATCH 2/4] nbd/server: Mark negotiation functions as coroutine_fn
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 375: Fix regression on nbd+tls
RH-Jira: RHEL-33754
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [2/4] 6dc8ecca16df5ae5cc6dc36e7b96f991396fcf24 (ebblake/qemu-kvm)
nbd_negotiate() is already marked coroutine_fn. And given the fix in
the previous patch to have nbd_negotiate_handle_starttls not create
and wait on a g_main_loop (as that would violate coroutine
constraints), it is worth marking the rest of the related static
functions reachable only during option negotiation as also being
coroutine_fn.
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240408160214.1200629-6-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
[eblake: drop one spurious coroutine_fn marking]
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit 4fa333e08dd96395a99ea8dd9e4c73a29dd23344)
Jira: https://issues.redhat.com/browse/RHEL-33754
Signed-off-by: Eric Blake <eblake@redhat.com>
---
nbd/server.c | 102 +++++++++++++++++++++++++++++----------------------
1 file changed, 59 insertions(+), 43 deletions(-)
diff --git a/nbd/server.c b/nbd/server.c
index 98ae0e1632..892797bb11 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -195,8 +195,9 @@ static inline void set_be_option_rep(NBDOptionReply *rep, uint32_t option,
/* Send a reply header, including length, but no payload.
* Return -errno on error, 0 on success. */
-static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type,
- uint32_t len, Error **errp)
+static coroutine_fn int
+nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type,
+ uint32_t len, Error **errp)
{
NBDOptionReply rep;
@@ -211,15 +212,15 @@ static int nbd_negotiate_send_rep_len(NBDClient *client, uint32_t type,
/* Send a reply header with default 0 length.
* Return -errno on error, 0 on success. */
-static int nbd_negotiate_send_rep(NBDClient *client, uint32_t type,
- Error **errp)
+static coroutine_fn int
+nbd_negotiate_send_rep(NBDClient *client, uint32_t type, Error **errp)
{
return nbd_negotiate_send_rep_len(client, type, 0, errp);
}
/* Send an error reply.
* Return -errno on error, 0 on success. */
-static int G_GNUC_PRINTF(4, 0)
+static coroutine_fn int G_GNUC_PRINTF(4, 0)
nbd_negotiate_send_rep_verr(NBDClient *client, uint32_t type,
Error **errp, const char *fmt, va_list va)
{
@@ -259,7 +260,7 @@ nbd_sanitize_name(const char *name)
/* Send an error reply.
* Return -errno on error, 0 on success. */
-static int G_GNUC_PRINTF(4, 5)
+static coroutine_fn int G_GNUC_PRINTF(4, 5)
nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type,
Error **errp, const char *fmt, ...)
{
@@ -275,7 +276,7 @@ nbd_negotiate_send_rep_err(NBDClient *client, uint32_t type,
/* Drop remainder of the current option, and send a reply with the
* given error type and message. Return -errno on read or write
* failure; or 0 if connection is still live. */
-static int G_GNUC_PRINTF(4, 0)
+static coroutine_fn int G_GNUC_PRINTF(4, 0)
nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp,
const char *fmt, va_list va)
{
@@ -288,7 +289,7 @@ nbd_opt_vdrop(NBDClient *client, uint32_t type, Error **errp,
return ret;
}
-static int G_GNUC_PRINTF(4, 5)
+static coroutine_fn int G_GNUC_PRINTF(4, 5)
nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp,
const char *fmt, ...)
{
@@ -302,7 +303,7 @@ nbd_opt_drop(NBDClient *client, uint32_t type, Error **errp,
return ret;
}
-static int G_GNUC_PRINTF(3, 4)
+static coroutine_fn int G_GNUC_PRINTF(3, 4)
nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...)
{
int ret;
@@ -319,8 +320,9 @@ nbd_opt_invalid(NBDClient *client, Error **errp, const char *fmt, ...)
* If @check_nul, require that no NUL bytes appear in buffer.
* Return -errno on I/O error, 0 if option was completely handled by
* sending a reply about inconsistent lengths, or 1 on success. */
-static int nbd_opt_read(NBDClient *client, void *buffer, size_t size,
- bool check_nul, Error **errp)
+static coroutine_fn int
+nbd_opt_read(NBDClient *client, void *buffer, size_t size,
+ bool check_nul, Error **errp)
{
if (size > client->optlen) {
return nbd_opt_invalid(client, errp,
@@ -343,7 +345,8 @@ static int nbd_opt_read(NBDClient *client, void *buffer, size_t size,
/* Drop size bytes from the unparsed payload of the current option.
* Return -errno on I/O error, 0 if option was completely handled by
* sending a reply about inconsistent lengths, or 1 on success. */
-static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp)
+static coroutine_fn int
+nbd_opt_skip(NBDClient *client, size_t size, Error **errp)
{
if (size > client->optlen) {
return nbd_opt_invalid(client, errp,
@@ -366,8 +369,9 @@ static int nbd_opt_skip(NBDClient *client, size_t size, Error **errp)
* Return -errno on I/O error, 0 if option was completely handled by
* sending a reply about inconsistent lengths, or 1 on success.
*/
-static int nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length,
- Error **errp)
+static coroutine_fn int
+nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length,
+ Error **errp)
{
int ret;
uint32_t len;
@@ -402,8 +406,8 @@ static int nbd_opt_read_name(NBDClient *client, char **name, uint32_t *length,
/* Send a single NBD_REP_SERVER reply to NBD_OPT_LIST, including payload.
* Return -errno on error, 0 on success. */
-static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp,
- Error **errp)
+static coroutine_fn int
+nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp, Error **errp)
{
ERRP_GUARD();
size_t name_len, desc_len;
@@ -444,7 +448,8 @@ static int nbd_negotiate_send_rep_list(NBDClient *client, NBDExport *exp,
/* Process the NBD_OPT_LIST command, with a potential series of replies.
* Return -errno on error, 0 on success. */
-static int nbd_negotiate_handle_list(NBDClient *client, Error **errp)
+static coroutine_fn int
+nbd_negotiate_handle_list(NBDClient *client, Error **errp)
{
NBDExport *exp;
assert(client->opt == NBD_OPT_LIST);
@@ -459,7 +464,8 @@ static int nbd_negotiate_handle_list(NBDClient *client, Error **errp)
return nbd_negotiate_send_rep(client, NBD_REP_ACK, errp);
}
-static void nbd_check_meta_export(NBDClient *client, NBDExport *exp)
+static coroutine_fn void
+nbd_check_meta_export(NBDClient *client, NBDExport *exp)
{
if (exp != client->contexts.exp) {
client->contexts.count = 0;
@@ -468,8 +474,9 @@ static void nbd_check_meta_export(NBDClient *client, NBDExport *exp)
/* Send a reply to NBD_OPT_EXPORT_NAME.
* Return -errno on error, 0 on success. */
-static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes,
- Error **errp)
+static coroutine_fn int
+nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes,
+ Error **errp)
{
ERRP_GUARD();
g_autofree char *name = NULL;
@@ -536,9 +543,9 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, bool no_zeroes,
/* Send a single NBD_REP_INFO, with a buffer @buf of @length bytes.
* The buffer does NOT include the info type prefix.
* Return -errno on error, 0 if ready to send more. */
-static int nbd_negotiate_send_info(NBDClient *client,
- uint16_t info, uint32_t length, void *buf,
- Error **errp)
+static coroutine_fn int
+nbd_negotiate_send_info(NBDClient *client, uint16_t info, uint32_t length,
+ void *buf, Error **errp)
{
int rc;
@@ -565,7 +572,8 @@ static int nbd_negotiate_send_info(NBDClient *client,
* -errno transmission error occurred or @fatal was requested, errp is set
* 0 error message successfully sent to client, errp is not set
*/
-static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
+static coroutine_fn int
+nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
{
int ret;
@@ -583,7 +591,8 @@ static int nbd_reject_length(NBDClient *client, bool fatal, Error **errp)
/* Handle NBD_OPT_INFO and NBD_OPT_GO.
* Return -errno on error, 0 if ready for next option, and 1 to move
* into transmission phase. */
-static int nbd_negotiate_handle_info(NBDClient *client, Error **errp)
+static coroutine_fn int
+nbd_negotiate_handle_info(NBDClient *client, Error **errp)
{
int rc;
g_autofree char *name = NULL;
@@ -755,7 +764,8 @@ struct NBDTLSServerHandshakeData {
Coroutine *co;
};
-static void nbd_server_tls_handshake(QIOTask *task, void *opaque)
+static void
+nbd_server_tls_handshake(QIOTask *task, void *opaque)
{
struct NBDTLSServerHandshakeData *data = opaque;
@@ -768,8 +778,8 @@ static void nbd_server_tls_handshake(QIOTask *task, void *opaque)
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
* new channel for all further (now-encrypted) communication. */
-static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
- Error **errp)
+static coroutine_fn QIOChannel *
+nbd_negotiate_handle_starttls(NBDClient *client, Error **errp)
{
QIOChannel *ioc;
QIOChannelTLS *tioc;
@@ -821,10 +831,9 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
*
* For NBD_OPT_LIST_META_CONTEXT @context_id is ignored, 0 is used instead.
*/
-static int nbd_negotiate_send_meta_context(NBDClient *client,
- const char *context,
- uint32_t context_id,
- Error **errp)
+static coroutine_fn int
+nbd_negotiate_send_meta_context(NBDClient *client, const char *context,
+ uint32_t context_id, Error **errp)
{
NBDOptionReplyMetaContext opt;
struct iovec iov[] = {
@@ -849,8 +858,9 @@ static int nbd_negotiate_send_meta_context(NBDClient *client,
* Return true if @query matches @pattern, or if @query is empty when
* the @client is performing _LIST_.
*/
-static bool nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern,
- const char *query)
+static coroutine_fn bool
+nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern,
+ const char *query)
{
if (!*query) {
trace_nbd_negotiate_meta_query_parse("empty");
@@ -867,7 +877,8 @@ static bool nbd_meta_empty_or_pattern(NBDClient *client, const char *pattern,
/*
* Return true and adjust @str in place if it begins with @prefix.
*/
-static bool nbd_strshift(const char **str, const char *prefix)
+static coroutine_fn bool
+nbd_strshift(const char **str, const char *prefix)
{
size_t len = strlen(prefix);
@@ -883,8 +894,9 @@ static bool nbd_strshift(const char **str, const char *prefix)
* Handle queries to 'base' namespace. For now, only the base:allocation
* context is available. Return true if @query has been handled.
*/
-static bool nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta,
- const char *query)
+static coroutine_fn bool
+nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta,
+ const char *query)
{
if (!nbd_strshift(&query, "base:")) {
return false;
@@ -903,8 +915,9 @@ static bool nbd_meta_base_query(NBDClient *client, NBDMetaContexts *meta,
* and qemu:allocation-depth contexts are available. Return true if @query
* has been handled.
*/
-static bool nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta,
- const char *query)
+static coroutine_fn bool
+nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta,
+ const char *query)
{
size_t i;
@@ -968,8 +981,9 @@ static bool nbd_meta_qemu_query(NBDClient *client, NBDMetaContexts *meta,
*
* Return -errno on I/O error, 0 if option was completely handled by
* sending a reply about inconsistent lengths, or 1 on success. */
-static int nbd_negotiate_meta_query(NBDClient *client,
- NBDMetaContexts *meta, Error **errp)
+static coroutine_fn int
+nbd_negotiate_meta_query(NBDClient *client,
+ NBDMetaContexts *meta, Error **errp)
{
int ret;
g_autofree char *query = NULL;
@@ -1008,7 +1022,8 @@ static int nbd_negotiate_meta_query(NBDClient *client,
* Handle NBD_OPT_LIST_META_CONTEXT and NBD_OPT_SET_META_CONTEXT
*
* Return -errno on I/O error, or 0 if option was completely handled. */
-static int nbd_negotiate_meta_queries(NBDClient *client, Error **errp)
+static coroutine_fn int
+nbd_negotiate_meta_queries(NBDClient *client, Error **errp)
{
int ret;
g_autofree char *export_name = NULL;
@@ -1136,7 +1151,8 @@ static int nbd_negotiate_meta_queries(NBDClient *client, Error **errp)
* 1 if client sent NBD_OPT_ABORT, i.e. on valid disconnect,
* errp is not set
*/
-static int nbd_negotiate_options(NBDClient *client, Error **errp)
+static coroutine_fn int
+nbd_negotiate_options(NBDClient *client, Error **errp)
{
uint32_t flags;
bool fixedNewstyle = false;
--
2.39.3

@ -1,175 +0,0 @@
From c8c7dc7a445892a820223be2e7617a790e0400f5 Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Wed, 7 Aug 2024 08:50:01 -0500
Subject: [PATCH 2/5] nbd/server: Plumb in new args to nbd_client_add()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 386: nbd/server: fix CVE-2024-7409 (qemu crash on nbd-server-stop) [rhel-9.4.z]
RH-Jira: RHEL-52616
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/4] 658d80c80a1163fcad456a34ec20d7828273a36c (ebblake/qemu-kvm)
Upcoming patches to fix a CVE need to track an opaque pointer passed
in by the owner of a client object, as well as request for a time
limit on how fast negotiation must complete. Prepare for that by
changing the signature of nbd_client_new() and adding an accessor to
get at the opaque pointer, although for now the two servers
(qemu-nbd.c and blockdev-nbd.c) do not change behavior even though
they pass in a new default timeout value.
Suggested-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240807174943.771624-11-eblake@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
[eblake: s/LIMIT/MAX_SECS/ as suggested by Dan]
Signed-off-by: Eric Blake <eblake@redhat.com>
(cherry picked from commit fb1c2aaa981e0a2fa6362c9985f1296b74f055ac)
Jira: https://issues.redhat.com/browse/RHEL-52616
Signed-off-by: Eric Blake <eblake@redhat.com>
---
blockdev-nbd.c | 6 ++++--
include/block/nbd.h | 11 ++++++++++-
nbd/server.c | 20 +++++++++++++++++---
qemu-nbd.c | 4 +++-
4 files changed, 34 insertions(+), 7 deletions(-)
diff --git a/blockdev-nbd.c b/blockdev-nbd.c
index 213012435f..267a1de903 100644
--- a/blockdev-nbd.c
+++ b/blockdev-nbd.c
@@ -64,8 +64,10 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
nbd_update_server_watch(nbd_server);
qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server");
- nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz,
- nbd_blockdev_client_closed);
+ /* TODO - expose handshake timeout as QMP option */
+ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
+ nbd_server->tlscreds, nbd_server->tlsauthz,
+ nbd_blockdev_client_closed, NULL);
}
static void nbd_update_server_watch(NBDServerData *s)
diff --git a/include/block/nbd.h b/include/block/nbd.h
index 4e7bd6342f..1d4d65922d 100644
--- a/include/block/nbd.h
+++ b/include/block/nbd.h
@@ -33,6 +33,12 @@ typedef struct NBDMetaContexts NBDMetaContexts;
extern const BlockExportDriver blk_exp_nbd;
+/*
+ * NBD_DEFAULT_HANDSHAKE_MAX_SECS: Number of seconds in which client must
+ * succeed at NBD_OPT_GO before being forcefully dropped as too slow.
+ */
+#define NBD_DEFAULT_HANDSHAKE_MAX_SECS 10
+
/* Handshake phase structs - this struct is passed on the wire */
typedef struct NBDOption {
@@ -403,9 +409,12 @@ AioContext *nbd_export_aio_context(NBDExport *exp);
NBDExport *nbd_export_find(const char *name);
void nbd_client_new(QIOChannelSocket *sioc,
+ uint32_t handshake_max_secs,
QCryptoTLSCreds *tlscreds,
const char *tlsauthz,
- void (*close_fn)(NBDClient *, bool));
+ void (*close_fn)(NBDClient *, bool),
+ void *owner);
+void *nbd_client_owner(NBDClient *client);
void nbd_client_get(NBDClient *client);
void nbd_client_put(NBDClient *client);
diff --git a/nbd/server.c b/nbd/server.c
index 892797bb11..e50012499f 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -124,12 +124,14 @@ struct NBDMetaContexts {
struct NBDClient {
int refcount; /* atomic */
void (*close_fn)(NBDClient *client, bool negotiated);
+ void *owner;
QemuMutex lock;
NBDExport *exp;
QCryptoTLSCreds *tlscreds;
char *tlsauthz;
+ uint32_t handshake_max_secs;
QIOChannelSocket *sioc; /* The underlying data channel */
QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */
@@ -3191,6 +3193,7 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
qemu_co_mutex_init(&client->send_lock);
+ /* TODO - utilize client->handshake_max_secs */
if (nbd_negotiate(client, &local_err)) {
if (local_err) {
error_report_err(local_err);
@@ -3205,14 +3208,17 @@ static coroutine_fn void nbd_co_client_start(void *opaque)
}
/*
- * Create a new client listener using the given channel @sioc.
+ * Create a new client listener using the given channel @sioc and @owner.
* Begin servicing it in a coroutine. When the connection closes, call
- * @close_fn with an indication of whether the client completed negotiation.
+ * @close_fn with an indication of whether the client completed negotiation
+ * within @handshake_max_secs seconds (0 for unbounded).
*/
void nbd_client_new(QIOChannelSocket *sioc,
+ uint32_t handshake_max_secs,
QCryptoTLSCreds *tlscreds,
const char *tlsauthz,
- void (*close_fn)(NBDClient *, bool))
+ void (*close_fn)(NBDClient *, bool),
+ void *owner)
{
NBDClient *client;
Coroutine *co;
@@ -3225,13 +3231,21 @@ void nbd_client_new(QIOChannelSocket *sioc,
object_ref(OBJECT(client->tlscreds));
}
client->tlsauthz = g_strdup(tlsauthz);
+ client->handshake_max_secs = handshake_max_secs;
client->sioc = sioc;
qio_channel_set_delay(QIO_CHANNEL(sioc), false);
object_ref(OBJECT(client->sioc));
client->ioc = QIO_CHANNEL(sioc);
object_ref(OBJECT(client->ioc));
client->close_fn = close_fn;
+ client->owner = owner;
co = qemu_coroutine_create(nbd_co_client_start, client);
qemu_coroutine_enter(co);
}
+
+void *
+nbd_client_owner(NBDClient *client)
+{
+ return client->owner;
+}
diff --git a/qemu-nbd.c b/qemu-nbd.c
index bac0b5e3ec..01b4a63c31 100644
--- a/qemu-nbd.c
+++ b/qemu-nbd.c
@@ -389,7 +389,9 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc,
nb_fds++;
nbd_update_server_watch();
- nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed);
+ /* TODO - expose handshake timeout as command line option */
+ nbd_client_new(cioc, NBD_DEFAULT_HANDSHAKE_MAX_SECS,
+ tlscreds, tlsauthz, nbd_client_closed, NULL);
}
static void nbd_update_server_watch(void)
--
2.39.3

@ -1,207 +0,0 @@
From ef01aeba9f6c4c886719261333a04bc32484f0d0 Mon Sep 17 00:00:00 2001
From: Zhu Yangyang <zhuyangyang14@huawei.com>
Date: Mon, 8 Apr 2024 11:00:43 -0500
Subject: [PATCH 1/4] nbd/server: do not poll within a coroutine context
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 375: Fix regression on nbd+tls
RH-Jira: RHEL-33754
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/4] 88ca2c90e0c412ba8c1cfd7ea91e01ec58220e9b (ebblake/qemu-kvm)
Coroutines are not supposed to block. Instead, they should yield.
The client performs TLS upgrade outside of an AIOContext, during
synchronous handshake; this still requires g_main_loop. But the
server responds to TLS upgrade inside a coroutine, so a nested
g_main_loop is wrong. Since the two callbacks no longer share more
than the setting of data.complete and data.error, it's just as easy to
use static helpers instead of trying to share a common code path. It
is also possible to add assertions that no other code is interfering
with the eventual path to qio reaching the callback, whether or not it
required a yield or main loop.
Fixes: f95910f ("nbd: implement TLS support in the protocol negotiation")
Signed-off-by: Zhu Yangyang <zhuyangyang14@huawei.com>
[eblake: move callbacks to their use point, add assertions]
Signed-off-by: Eric Blake <eblake@redhat.com>
Message-ID: <20240408160214.1200629-5-eblake@redhat.com>
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
(cherry picked from commit ae6d91a7e9b77abb029ed3fa9fad461422286942)
Jira: https://issues.redhat.com/browse/RHEL-33754
Signed-off-by: Eric Blake <eblake@redhat.com>
---
nbd/client.c | 28 ++++++++++++++++++++++++----
nbd/common.c | 11 -----------
nbd/nbd-internal.h | 10 ----------
nbd/server.c | 28 +++++++++++++++++++++++-----
4 files changed, 47 insertions(+), 30 deletions(-)
diff --git a/nbd/client.c b/nbd/client.c
index 29ffc609a4..c89c750467 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -596,13 +596,31 @@ static int nbd_request_simple_option(QIOChannel *ioc, int opt, bool strict,
return 1;
}
+/* Callback to learn when QIO TLS upgrade is complete */
+struct NBDTLSClientHandshakeData {
+ bool complete;
+ Error *error;
+ GMainLoop *loop;
+};
+
+static void nbd_client_tls_handshake(QIOTask *task, void *opaque)
+{
+ struct NBDTLSClientHandshakeData *data = opaque;
+
+ qio_task_propagate_error(task, &data->error);
+ data->complete = true;
+ if (data->loop) {
+ g_main_loop_quit(data->loop);
+ }
+}
+
static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
QCryptoTLSCreds *tlscreds,
const char *hostname, Error **errp)
{
int ret;
QIOChannelTLS *tioc;
- struct NBDTLSHandshakeData data = { 0 };
+ struct NBDTLSClientHandshakeData data = { 0 };
ret = nbd_request_simple_option(ioc, NBD_OPT_STARTTLS, true, errp);
if (ret <= 0) {
@@ -619,18 +637,20 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc,
return NULL;
}
qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-client-tls");
- data.loop = g_main_loop_new(g_main_context_default(), FALSE);
trace_nbd_receive_starttls_tls_handshake();
qio_channel_tls_handshake(tioc,
- nbd_tls_handshake,
+ nbd_client_tls_handshake,
&data,
NULL,
NULL);
if (!data.complete) {
+ data.loop = g_main_loop_new(g_main_context_default(), FALSE);
g_main_loop_run(data.loop);
+ assert(data.complete);
+ g_main_loop_unref(data.loop);
}
- g_main_loop_unref(data.loop);
+
if (data.error) {
error_propagate(errp, data.error);
object_unref(OBJECT(tioc));
diff --git a/nbd/common.c b/nbd/common.c
index 3247c1d618..589a748cfe 100644
--- a/nbd/common.c
+++ b/nbd/common.c
@@ -47,17 +47,6 @@ int nbd_drop(QIOChannel *ioc, size_t size, Error **errp)
}
-void nbd_tls_handshake(QIOTask *task,
- void *opaque)
-{
- struct NBDTLSHandshakeData *data = opaque;
-
- qio_task_propagate_error(task, &data->error);
- data->complete = true;
- g_main_loop_quit(data->loop);
-}
-
-
const char *nbd_opt_lookup(uint32_t opt)
{
switch (opt) {
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index dfa02f77ee..91895106a9 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -72,16 +72,6 @@ static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size,
return qio_channel_write_all(ioc, buffer, size, errp) < 0 ? -EIO : 0;
}
-struct NBDTLSHandshakeData {
- GMainLoop *loop;
- bool complete;
- Error *error;
-};
-
-
-void nbd_tls_handshake(QIOTask *task,
- void *opaque);
-
int nbd_drop(QIOChannel *ioc, size_t size, Error **errp);
#endif
diff --git a/nbd/server.c b/nbd/server.c
index c3484cc1eb..98ae0e1632 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -748,6 +748,23 @@ static int nbd_negotiate_handle_info(NBDClient *client, Error **errp)
return rc;
}
+/* Callback to learn when QIO TLS upgrade is complete */
+struct NBDTLSServerHandshakeData {
+ bool complete;
+ Error *error;
+ Coroutine *co;
+};
+
+static void nbd_server_tls_handshake(QIOTask *task, void *opaque)
+{
+ struct NBDTLSServerHandshakeData *data = opaque;
+
+ qio_task_propagate_error(task, &data->error);
+ data->complete = true;
+ if (!qemu_coroutine_entered(data->co)) {
+ aio_co_wake(data->co);
+ }
+}
/* Handle NBD_OPT_STARTTLS. Return NULL to drop connection, or else the
* new channel for all further (now-encrypted) communication. */
@@ -756,7 +773,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
{
QIOChannel *ioc;
QIOChannelTLS *tioc;
- struct NBDTLSHandshakeData data = { 0 };
+ struct NBDTLSServerHandshakeData data = { 0 };
assert(client->opt == NBD_OPT_STARTTLS);
@@ -777,17 +794,18 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client,
qio_channel_set_name(QIO_CHANNEL(tioc), "nbd-server-tls");
trace_nbd_negotiate_handle_starttls_handshake();
- data.loop = g_main_loop_new(g_main_context_default(), FALSE);
+ data.co = qemu_coroutine_self();
qio_channel_tls_handshake(tioc,
- nbd_tls_handshake,
+ nbd_server_tls_handshake,
&data,
NULL,
NULL);
if (!data.complete) {
- g_main_loop_run(data.loop);
+ qemu_coroutine_yield();
+ assert(data.complete);
}
- g_main_loop_unref(data.loop);
+
if (data.error) {
object_unref(OBJECT(tioc));
error_propagate(errp, data.error);
--
2.39.3

@ -1,115 +0,0 @@
From 9ca64f73238d9f1b9f13d8e941ba42771a992afb Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Fri, 29 Dec 2023 14:06:05 +0100
Subject: [PATCH 20/20] pc/q35: set SMBIOS entry point type to 'auto' by
default
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [18/18] c7fc6ac7350bca3ff99e58620710a86218385781
JIRA: https://issues.redhat.com/browse/RHEL-21705
Use smbios-entry-point-type='auto' for newer machine types as a workaround
for Windows not detecting SMBIOS tables. Which makes QEMU pick SMBIOS tables
based on configuration (with 2.x preferred and fallback to 3.x if the former
isn't compatible with configuration)
Default compat setting of smbios-entry-point-type after series
for pc/q35 machines:
* 9.0-newer: 'auto'
* 8.1-8.2: '64'
* 8.0-older: '32'
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2008
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Conflicts: hw/i386/pc_piix.c hw/i386/pc_q35.c
due to RHEL machine types
REHL only parts:
Fix RHEL 'pc' machine types at SMBIOS 2.X
for the latest RHEL 'q35' machine type use version autoselect
which propagates to RHEL 9.4 q35 macine type while RHEL 9.2 q35 and older
are kept at SMBIOS_ENTRY_POINT_TYPE_32 (see: pc_q35_machine_rhel920_options)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/pc.c | 2 +-
hw/i386/pc_piix.c | 7 +++++++
hw/i386/pc_q35.c | 5 +++++
3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index ae6777fc1a..d6f267b220 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -2004,7 +2004,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
mc->nvdimm_supported = true;
mc->smp_props.dies_supported = true;
mc->default_ram_id = "pc.ram";
- pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64;
+ pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO;
object_class_property_add(oc, PC_MACHINE_MAX_RAM_BELOW_4G, "size",
pc_machine_get_max_ram_below_4g, pc_machine_set_max_ram_below_4g,
diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c
index 7344b35cf1..54d1c58bce 100644
--- a/hw/i386/pc_piix.c
+++ b/hw/i386/pc_piix.c
@@ -539,9 +539,14 @@ static void pc_i440fx_machine_options(MachineClass *m)
static void pc_i440fx_8_2_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
+
pc_i440fx_machine_options(m);
m->alias = "pc";
m->is_default = true;
+
+ /* For pc-i44fx-8.2 and 8.1, use SMBIOS 3.X by default */
+ pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64;
}
DEFINE_I440FX_MACHINE(v8_2, "pc-i440fx-8.2", NULL,
@@ -982,6 +987,8 @@ static void pc_machine_rhel7_options(MachineClass *m)
m->alias = "pc";
m->is_default = 1;
m->smp_props.prefer_sockets = true;
+ /* there aren't ne PC macine types in RHEL9, keep it at SMBIOS 2.X */
+ pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32;
}
static void pc_init_rhel760(MachineState *machine)
diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c
index 9a22ff5dd6..cd5fb7380e 100644
--- a/hw/i386/pc_q35.c
+++ b/hw/i386/pc_q35.c
@@ -377,8 +377,11 @@ static void pc_q35_machine_options(MachineClass *m)
static void pc_q35_8_2_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_machine_options(m);
m->alias = "q35";
+ /* For pc-q35-8.2 and 8.1, use SMBIOS 3.X by default */
+ pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_64;
}
DEFINE_Q35_MACHINE(v8_2, "pc-q35-8.2", NULL,
@@ -712,6 +715,8 @@ static void pc_q35_machine_rhel_options(MachineClass *m)
m->alias = "q35";
m->max_cpus = 710;
compat_props_add(m->compat_props, pc_rhel_compat, pc_rhel_compat_len);
+ /* use SMBIOS version autoselect by default for the latest RHEL machine */
+ pcmc->default_smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_AUTO;
}
static void pc_q35_init_rhel940(MachineState *machine)
--
2.39.3

@ -1,44 +0,0 @@
From 03cad16743d9a4d377af66611d030eca9eda326d Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Tue, 19 Mar 2024 11:42:04 +0100
Subject: [PATCH 4/4] pc: smbios: fixup manufacturer/product/version to match
downstream
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 232: pc: smbios: fixup manufacturer/product/version to match downstream
RH-Jira: RHEL-21705
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Acked-by: Ani Sinha <anisinha@redhat.com>
RH-Commit: [1/1] 9235f64acb5ff46360282e889d0aedcb13374ac1
JIRA: https://issues.redhat.com/browse/RHEL-21705
commit [1] discarded RHEL only change that customizes
SMBIOS values for manufacturer/product/version for pc/q35
machine types.
Fix it up by reverting back to ("Red Hat", "KVM", mc->desc)
tuple.
1)
Fixes: 208239eb2 (hw/i386/pc: Defer smbios_set_defaults() to machine_done)
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/fw_cfg.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index 58429bb78d..d6a24177e2 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -63,7 +63,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg,
if (pcmc->smbios_defaults) {
/* These values are guest ABI, do not change */
- smbios_set_defaults("QEMU", mc->desc, mc->name,
+ smbios_set_defaults("Red Hat", "KVM", mc->desc,
pcmc->smbios_uuid_encoded,
pcmc->smbios_stream_product,
pcmc->smbios_stream_version);
--
2.39.3

@ -1,125 +0,0 @@
From 6e39b4c13c0eacb35e81874b09e6b6411266c631 Mon Sep 17 00:00:00 2001
From: Jon Maloy <jmaloy@redhat.com>
Date: Wed, 5 Jun 2024 19:56:51 -0400
Subject: [PATCH 1/4] qcow2: Don't open data_file with BDRV_O_NO_IO
RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 2: EMBARGOED CVE-2024-4467 for rhel-9.4.z (PRDSC)
RH-Jira: https://issues.redhat.com/browse/RHEL-35610
RH-CVE: CVE-2024-4467
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
RH-Acked-by: Hanna Czenczek <hczenczek@redhat.com>
RH-Commit: [1/4] 1000359b05c706f3c5155a9481692352be333129
commit f9843ce5c519901654a7d8ba43ee95ce25ca13c2
Author: Kevin Wolf <kwolf@redhat.com>
Date: Thu Apr 11 15:06:01 2024 +0200
qcow2: Don't open data_file with BDRV_O_NO_IO
One use case for 'qemu-img info' is verifying that untrusted images
don't reference an unwanted external file, be it as a backing file or an
external data file. To make sure that calling 'qemu-img info' can't
already have undesired side effects with a malicious image, just don't
open the data file at all with BDRV_O_NO_IO. If nothing ever tries to do
I/O, we don't need to have it open.
This changes the output of iotests case 061, which used 'qemu-img info'
to show that opening an image with an invalid data file fails. After
this patch, it succeeds. Replace this part of the test with a qemu-io
call, but keep the final 'qemu-img info' to show that the invalid data
file is correctly displayed in the output.
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
Upstream: N/A, embargoed
Signed-off-by: Hanna Czenczek <hreitz@redhat.com>
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
block/qcow2.c | 17 ++++++++++++++++-
tests/qemu-iotests/061 | 6 ++++--
tests/qemu-iotests/061.out | 8 ++++++--
3 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/block/qcow2.c b/block/qcow2.c
index d91b7b91d3..b269cfc78f 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -1642,7 +1642,22 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags,
goto fail;
}
- if (open_data_file) {
+ if (open_data_file && (flags & BDRV_O_NO_IO)) {
+ /*
+ * Don't open the data file for 'qemu-img info' so that it can be used
+ * to verify that an untrusted qcow2 image doesn't refer to external
+ * files.
+ *
+ * Note: This still makes has_data_file() return true.
+ */
+ if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) {
+ s->data_file = NULL;
+ } else {
+ s->data_file = bs->file;
+ }
+ qdict_extract_subqdict(options, NULL, "data-file.");
+ qdict_del(options, "data-file");
+ } else if (open_data_file) {
/* Open external data file */
bdrv_graph_co_rdunlock();
s->data_file = bdrv_co_open_child(NULL, options, "data-file", bs,
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
index 53c7d428e3..b71ac097d1 100755
--- a/tests/qemu-iotests/061
+++ b/tests/qemu-iotests/061
@@ -326,12 +326,14 @@ $QEMU_IMG amend -o "data_file=foo" "$TEST_IMG"
echo
_make_test_img -o "compat=1.1,data_file=$TEST_IMG.data" 64M
$QEMU_IMG amend -o "data_file=foo" "$TEST_IMG"
-_img_info --format-specific
+$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
+$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io
TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts
echo
$QEMU_IMG amend -o "data_file=" --image-opts "data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG"
-_img_info --format-specific
+$QEMU_IO -c "read 0 4k" "$TEST_IMG" 2>&1 | _filter_testdir | _filter_imgfmt
+$QEMU_IO -c "open -o data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" -c "read 0 4k" | _filter_qemu_io
TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts
echo
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
index 139fc68177..24c33add7c 100644
--- a/tests/qemu-iotests/061.out
+++ b/tests/qemu-iotests/061.out
@@ -545,7 +545,9 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
qemu-img: data-file can only be set for images that use an external data file
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'foo': No such file or directory
+qemu-io: can't open device TEST_DIR/t.IMGFMT: Could not open 'foo': No such file or directory
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64 MiB (67108864 bytes)
@@ -560,7 +562,9 @@ Format specific information:
corrupt: false
extended l2: false
-qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'data-file' is required for this image
+qemu-io: can't open device TEST_DIR/t.IMGFMT: 'data-file' is required for this image
+read 4096/4096 bytes at offset 0
+4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
image: TEST_DIR/t.IMGFMT
file format: IMGFMT
virtual size: 64 MiB (67108864 bytes)
--
2.39.3

@ -1,135 +0,0 @@
From f2fe6c7a2def488633cbb67e28ac00279d6e8de4 Mon Sep 17 00:00:00 2001
From: Cornelia Huck <cohuck@redhat.com>
Date: Tue, 27 Feb 2024 11:17:39 +0100
Subject: [PATCH 1/2] qemu_init: increase NOFILE soft limit on POSIX
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Cornelia Huck <cohuck@redhat.com>
RH-MergeRequest: 226: qemu_init: increase NOFILE soft limit on POSIX
RH-Jira: RHEL-26049
RH-Acked-by: Gavin Shan <gshan@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Acked-by: Shaoqin Huang <None>
RH-Commit: [1/1] cee5404aef3f6437d45a1c43bdee73a57a528bee (cohuck/qemu-kvm-c9s)
Jira: https://issues.redhat.com/browse/RHEL-26049
In many configurations, e.g. multiple vNICs with multiple queues or
with many Ceph OSDs, the default soft limit of 1024 is not enough.
QEMU is supposed to work fine with file descriptors >= 1024 and does
not use select() on POSIX. Bump the soft limit to the allowed hard
limit to avoid issues with the aforementioned configurations.
Of course the limit could be raised from the outside, but the man page
of systemd.exec states about 'LimitNOFILE=':
> Don't use.
> [...]
> Typically applications should increase their soft limit to the hard
> limit on their own, if they are OK with working with file
> descriptors above 1023,
If the soft limit is already the same as the hard limit, avoid the
superfluous setrlimit call. This can avoid a warning with a strict
seccomp filter blocking setrlimit if NOFILE was already raised before
executing QEMU.
Buglink: https://bugzilla.proxmox.com/show_bug.cgi?id=4507
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
(cherry picked from commit 03e471c41d8b1b6eb16c9714f387449f52fe5c1d)
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
---
include/sysemu/os-posix.h | 1 +
include/sysemu/os-win32.h | 5 +++++
os-posix.c | 22 ++++++++++++++++++++++
system/vl.c | 2 ++
4 files changed, 30 insertions(+)
diff --git a/include/sysemu/os-posix.h b/include/sysemu/os-posix.h
index dff32ae185..b881ac6c6f 100644
--- a/include/sysemu/os-posix.h
+++ b/include/sysemu/os-posix.h
@@ -51,6 +51,7 @@ bool is_daemonized(void);
void os_daemonize(void);
bool os_set_runas(const char *user_id);
void os_set_chroot(const char *path);
+void os_setup_limits(void);
void os_setup_post(void);
int os_mlock(void);
diff --git a/include/sysemu/os-win32.h b/include/sysemu/os-win32.h
index 1047d260cb..b82a5d3ad9 100644
--- a/include/sysemu/os-win32.h
+++ b/include/sysemu/os-win32.h
@@ -128,6 +128,11 @@ static inline int os_mlock(void)
return -ENOSYS;
}
+static inline void os_setup_limits(void)
+{
+ return;
+}
+
#define fsync _commit
#if !defined(lseek)
diff --git a/os-posix.c b/os-posix.c
index 52ef6990ff..a4284e2c07 100644
--- a/os-posix.c
+++ b/os-posix.c
@@ -24,6 +24,7 @@
*/
#include "qemu/osdep.h"
+#include <sys/resource.h>
#include <sys/wait.h>
#include <pwd.h>
#include <grp.h>
@@ -256,6 +257,27 @@ void os_daemonize(void)
}
}
+void os_setup_limits(void)
+{
+ struct rlimit nofile;
+
+ if (getrlimit(RLIMIT_NOFILE, &nofile) < 0) {
+ warn_report("unable to query NOFILE limit: %s", strerror(errno));
+ return;
+ }
+
+ if (nofile.rlim_cur == nofile.rlim_max) {
+ return;
+ }
+
+ nofile.rlim_cur = nofile.rlim_max;
+
+ if (setrlimit(RLIMIT_NOFILE, &nofile) < 0) {
+ warn_report("unable to set NOFILE limit: %s", strerror(errno));
+ return;
+ }
+}
+
void os_setup_post(void)
{
int fd = 0;
diff --git a/system/vl.c b/system/vl.c
index 93635ffc5b..6443b6e469 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -2783,6 +2783,8 @@ void qemu_init(int argc, char **argv)
error_init(argv[0]);
qemu_init_exec_dir(argv[0]);
+ os_setup_limits();
+
qemu_init_arch_modules();
qemu_init_subsystems();
--
2.39.3

@ -1,129 +0,0 @@
From 69d6b5f98d665fbcb86e42df40bcc5e9c79b397f Mon Sep 17 00:00:00 2001
From: Eric Blake <eblake@redhat.com>
Date: Fri, 17 May 2024 21:50:14 -0500
Subject: [PATCH 3/4] qio: Inherit follow_coroutine_ctx across TLS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Eric Blake <eblake@redhat.com>
RH-MergeRequest: 375: Fix regression on nbd+tls
RH-Jira: RHEL-33754
RH-Acked-by: Kevin Wolf <kwolf@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [3/4] 14ddea7e3c81898bb3fe4be51a40749d67a5c0c0 (ebblake/qemu-kvm)
Since qemu 8.2, the combination of NBD + TLS + iothread crashes on an
assertion failure:
qemu-kvm: ../io/channel.c:534: void qio_channel_restart_read(void *): Assertion `qemu_get_current_aio_context() == qemu_coroutine_get_aio_context(co)' failed.
It turns out that when we removed AioContext locking, we did so by
having NBD tell its qio channels that it wanted to opt in to
qio_channel_set_follow_coroutine_ctx(); but while we opted in on the
main channel, we did not opt in on the TLS wrapper channel.
qemu-iotests has coverage of NBD+iothread and NBD+TLS, but apparently
no coverage of NBD+TLS+iothread, or we would have noticed this
regression sooner. (I'll add that in the next patch)
But while we could manually opt in to the TLS channel in nbd/server.c
(a one-line change), it is more generic if all qio channels that wrap
other channels inherit the follow status, in the same way that they
inherit feature bits.
CC: Stefan Hajnoczi <stefanha@redhat.com>
CC: Daniel P. Berrangé <berrange@redhat.com>
CC: qemu-stable@nongnu.org
Fixes: https://issues.redhat.com/browse/RHEL-34786
Fixes: 06e0f098 ("io: follow coroutine AioContext in qio_channel_yield()", v8.2.0)
Signed-off-by: Eric Blake <eblake@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-ID: <20240518025246.791593-5-eblake@redhat.com>
(cherry picked from commit 199e84de1c903ba5aa1f7256310bbc4a20dd930b)
Jira: https://issues.redhat.com/browse/RHEL-33754
Signed-off-by: Eric Blake <eblake@redhat.com>
---
io/channel-tls.c | 26 +++++++++++++++-----------
io/channel-websock.c | 1 +
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/io/channel-tls.c b/io/channel-tls.c
index 58fe1aceee..a8ad89c3d1 100644
--- a/io/channel-tls.c
+++ b/io/channel-tls.c
@@ -69,37 +69,40 @@ qio_channel_tls_new_server(QIOChannel *master,
const char *aclname,
Error **errp)
{
- QIOChannelTLS *ioc;
+ QIOChannelTLS *tioc;
+ QIOChannel *ioc;
- ioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
+ tioc = QIO_CHANNEL_TLS(object_new(TYPE_QIO_CHANNEL_TLS));
+ ioc = QIO_CHANNEL(tioc);
- ioc->master = master;
+ tioc->master = master;
+ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx;
if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
- qio_channel_set_feature(QIO_CHANNEL(ioc), QIO_CHANNEL_FEATURE_SHUTDOWN);
+ qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
}
object_ref(OBJECT(master));
- ioc->session = qcrypto_tls_session_new(
+ tioc->session = qcrypto_tls_session_new(
creds,
NULL,
aclname,
QCRYPTO_TLS_CREDS_ENDPOINT_SERVER,
errp);
- if (!ioc->session) {
+ if (!tioc->session) {
goto error;
}
qcrypto_tls_session_set_callbacks(
- ioc->session,
+ tioc->session,
qio_channel_tls_write_handler,
qio_channel_tls_read_handler,
- ioc);
+ tioc);
- trace_qio_channel_tls_new_server(ioc, master, creds, aclname);
- return ioc;
+ trace_qio_channel_tls_new_server(tioc, master, creds, aclname);
+ return tioc;
error:
- object_unref(OBJECT(ioc));
+ object_unref(OBJECT(tioc));
return NULL;
}
@@ -116,6 +119,7 @@ qio_channel_tls_new_client(QIOChannel *master,
ioc = QIO_CHANNEL(tioc);
tioc->master = master;
+ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx;
if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
}
diff --git a/io/channel-websock.c b/io/channel-websock.c
index a12acc27cf..de39f0d182 100644
--- a/io/channel-websock.c
+++ b/io/channel-websock.c
@@ -883,6 +883,7 @@ qio_channel_websock_new_server(QIOChannel *master)
ioc = QIO_CHANNEL(wioc);
wioc->master = master;
+ ioc->follow_coroutine_ctx = master->follow_coroutine_ctx;
if (qio_channel_has_feature(master, QIO_CHANNEL_FEATURE_SHUTDOWN)) {
qio_channel_set_feature(ioc, QIO_CHANNEL_FEATURE_SHUTDOWN);
}
--
2.39.3

@ -1,38 +0,0 @@
From aa0ad36a56cbc0528306e47112081b0db124e512 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
Date: Wed, 5 Jun 2024 10:28:20 +0400
Subject: [PATCH 3/4] rhel 9.4.0 machine type compat for virtio-gpu migration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load
RH-Jira: RHEL-36181
RH-Acked-by: Peter Xu <peterx@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [3/3] 270e932e04eb2d730550f3a56b53cbb17f5e66f8
Jira: https://issues.redhat.com/browse/RHEL-36181
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
hw/core/machine.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index a0843ab93e..c8c460c916 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -81,6 +81,8 @@ GlobalProperty hw_compat_rhel_9_4[] = {
{ "igb", "x-pcie-flr-init", "off" },
/* hw_compat_rhel_9_4 jira RHEL-24045 */
{ "virtio-mem", "dynamic-memslots", "off" },
+ /* hw_compat_rhel_9_4 from hw_compat_8_1 */
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
};
const size_t hw_compat_rhel_9_4_len = G_N_ELEMENTS(hw_compat_rhel_9_4);
--
2.39.3

@ -1,62 +0,0 @@
From 93cf5b82771f1d1e8182be168dae7a45d42069e9 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Mon, 4 Mar 2024 15:39:57 +0100
Subject: [PATCH 11/20] smbios: add smbios_add_usr_blob_size() helper
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [9/18] 8c698fb9e186d2b1d2b7f75a74305f356450ad68
JIRA: https://issues.redhat.com/browse/RHEL-21705
it will be used by follow up patch when legacy handling
is moved out into a separate file.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
---
hw/smbios/smbios.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 441517cf24..c48a290478 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -1426,6 +1426,14 @@ static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts,
return true;
}
+static void smbios_add_usr_blob_size(size_t size)
+{
+ if (!usr_blobs_sizes) {
+ usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
+ }
+ g_array_append_val(usr_blobs_sizes, size);
+}
+
void smbios_entry_add(QemuOpts *opts, Error **errp)
{
const char *val;
@@ -1473,10 +1481,12 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
smbios_type4_count++;
}
- if (!usr_blobs_sizes) {
- usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
- }
- g_array_append_val(usr_blobs_sizes, size);
+ /*
+ * preserve blob size for legacy mode so it could build its
+ * blobs flavor from 'usr_blobs'
+ */
+ smbios_add_usr_blob_size(size);
+
usr_blobs_len += size;
if (size > usr_table_max) {
usr_table_max = size;
--
2.39.3

@ -1,309 +0,0 @@
From 15d293b706ca6c9e6ad569becda8da5f70461c30 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Tue, 13 Feb 2024 09:25:11 +0100
Subject: [PATCH 09/20] smbios: avoid mangling user provided tables
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [7/18] fa2c7372f55f29e5834eee94ba98f19ea02e7a82
JIRA: https://issues.redhat.com/browse/RHEL-21705
currently smbios_entry_add() preserves internally '-smbios type='
options but tables provided with '-smbios file=' are stored directly
into blob that eventually will be exposed to VM. And then later
QEMU adds default/'-smbios type' entries on top into the same blob.
It makes impossible to generate tables more than once, hence
'immutable' guard was used.
Make it possible to regenerate final blob by storing user provided
blobs into a dedicated area (usr_blobs) and then copy it when
composing final blob. Which also makes handling of -smbios
options consistent.
As side effect of this and previous commits there is no need to
generate legacy smbios_entries at the time options are parsed.
Instead compose smbios_entries on demand from usr_blobs like
it is done for non-legacy SMBIOS tables.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Conflicts: hw/smbios/smbios.c
caused by downstream smbios_type2_required
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/smbios/smbios.c | 181 +++++++++++++++++++++++----------------------
1 file changed, 93 insertions(+), 88 deletions(-)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 0c8c439859..d8d68716d4 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -60,6 +60,14 @@ static bool smbios_uuid_encoded = true;
/* Set to true for modern Windows 10 HardwareID-6 compat */
static bool smbios_type2_required;
+/*
+ * SMBIOS tables provided by user with '-smbios file=<foo>' option
+ */
+uint8_t *usr_blobs;
+size_t usr_blobs_len;
+static GArray *usr_blobs_sizes;
+static unsigned usr_table_max;
+static unsigned usr_table_cnt;
uint8_t *smbios_tables;
size_t smbios_tables_len;
@@ -70,7 +78,6 @@ static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32;
static SmbiosEntryPoint ep;
static int smbios_type4_count = 0;
-static bool smbios_immutable;
static bool smbios_have_defaults;
static uint32_t smbios_cpuid_version, smbios_cpuid_features;
@@ -617,9 +624,8 @@ static void smbios_build_type_1_fields(void)
uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
{
- /* drop unwanted version of command-line file blob(s) */
- g_free(smbios_tables);
- smbios_tables = NULL;
+ int i;
+ size_t usr_offset;
/* also complain if fields were given for types > 1 */
if (find_next_bit(have_fields_bitmap,
@@ -629,12 +635,33 @@ uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
exit(1);
}
- if (!smbios_immutable) {
- smbios_build_type_0_fields();
- smbios_build_type_1_fields();
- smbios_validate_table(expected_t4_count);
- smbios_immutable = true;
+ g_free(smbios_entries);
+ smbios_entries_len = sizeof(uint16_t);
+ smbios_entries = g_malloc0(smbios_entries_len);
+
+ for (i = 0, usr_offset = 0; usr_blobs_sizes && i < usr_blobs_sizes->len;
+ i++)
+ {
+ struct smbios_table *table;
+ struct smbios_structure_header *header;
+ size_t size = g_array_index(usr_blobs_sizes, size_t, i);
+
+ header = (struct smbios_structure_header *)(usr_blobs + usr_offset);
+ smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
+ size + sizeof(*table));
+ table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
+ table->header.type = SMBIOS_TABLE_ENTRY;
+ table->header.length = cpu_to_le16(sizeof(*table) + size);
+ memcpy(table->data, header, size);
+ smbios_entries_len += sizeof(*table) + size;
+ (*(uint16_t *)smbios_entries) =
+ cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+ usr_offset += size;
}
+
+ smbios_build_type_0_fields();
+ smbios_build_type_1_fields();
+ smbios_validate_table(expected_t4_count);
*length = smbios_entries_len;
return smbios_entries;
}
@@ -1232,68 +1259,68 @@ void smbios_get_tables(MachineState *ms,
{
unsigned i, dimm_cnt, offset;
- /* drop unwanted (legacy) version of command-line file blob(s) */
- g_free(smbios_entries);
- smbios_entries = NULL;
+ g_free(smbios_tables);
+ smbios_tables = g_memdup2(usr_blobs, usr_blobs_len);
+ smbios_tables_len = usr_blobs_len;
+ smbios_table_max = usr_table_max;
+ smbios_table_cnt = usr_table_cnt;
- if (!smbios_immutable) {
- smbios_build_type_0_table();
- smbios_build_type_1_table();
- smbios_build_type_2_table();
- smbios_build_type_3_table();
+ smbios_build_type_0_table();
+ smbios_build_type_1_table();
+ smbios_build_type_2_table();
+ smbios_build_type_3_table();
- assert(ms->smp.sockets >= 1);
+ assert(ms->smp.sockets >= 1);
- for (i = 0; i < ms->smp.sockets; i++) {
- smbios_build_type_4_table(ms, i);
- }
+ for (i = 0; i < ms->smp.sockets; i++) {
+ smbios_build_type_4_table(ms, i);
+ }
- smbios_build_type_8_table();
- smbios_build_type_9_table(errp);
- smbios_build_type_11_table();
+ smbios_build_type_8_table();
+ smbios_build_type_9_table(errp);
+ smbios_build_type_11_table();
#define MAX_DIMM_SZ (16 * GiB)
#define GET_DIMM_SZ ((i < dimm_cnt - 1) ? MAX_DIMM_SZ \
: ((current_machine->ram_size - 1) % MAX_DIMM_SZ) + 1)
- dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) / MAX_DIMM_SZ;
+ dimm_cnt = QEMU_ALIGN_UP(current_machine->ram_size, MAX_DIMM_SZ) /
+ MAX_DIMM_SZ;
- /*
- * The offset determines if we need to keep additional space between
- * table 17 and table 19 header handle numbers so that they do
- * not overlap. For example, for a VM with larger than 8 TB guest
- * memory and DIMM like chunks of 16 GiB, the default space between
- * the two tables (T19_BASE - T17_BASE = 512) is not enough.
- */
- offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \
- dimm_cnt - (T19_BASE - T17_BASE) : 0;
+ /*
+ * The offset determines if we need to keep additional space between
+ * table 17 and table 19 header handle numbers so that they do
+ * not overlap. For example, for a VM with larger than 8 TB guest
+ * memory and DIMM like chunks of 16 GiB, the default space between
+ * the two tables (T19_BASE - T17_BASE = 512) is not enough.
+ */
+ offset = (dimm_cnt > (T19_BASE - T17_BASE)) ? \
+ dimm_cnt - (T19_BASE - T17_BASE) : 0;
- smbios_build_type_16_table(dimm_cnt);
+ smbios_build_type_16_table(dimm_cnt);
- for (i = 0; i < dimm_cnt; i++) {
- smbios_build_type_17_table(i, GET_DIMM_SZ);
- }
+ for (i = 0; i < dimm_cnt; i++) {
+ smbios_build_type_17_table(i, GET_DIMM_SZ);
+ }
- for (i = 0; i < mem_array_size; i++) {
- smbios_build_type_19_table(i, offset, mem_array[i].address,
- mem_array[i].length);
- }
+ for (i = 0; i < mem_array_size; i++) {
+ smbios_build_type_19_table(i, offset, mem_array[i].address,
+ mem_array[i].length);
+ }
- /*
- * make sure 16 bit handle numbers in the headers of tables 19
- * and 32 do not overlap.
- */
- assert((mem_array_size + offset) < (T32_BASE - T19_BASE));
+ /*
+ * make sure 16 bit handle numbers in the headers of tables 19
+ * and 32 do not overlap.
+ */
+ assert((mem_array_size + offset) < (T32_BASE - T19_BASE));
- smbios_build_type_32_table();
- smbios_build_type_38_table();
- smbios_build_type_41_table(errp);
- smbios_build_type_127_table();
+ smbios_build_type_32_table();
+ smbios_build_type_38_table();
+ smbios_build_type_41_table(errp);
+ smbios_build_type_127_table();
- smbios_validate_table(ms->smp.sockets);
- smbios_entry_point_setup();
- smbios_immutable = true;
- }
+ smbios_validate_table(ms->smp.sockets);
+ smbios_entry_point_setup();
/* return tables blob and entry point (anchor), and their sizes */
*tables = smbios_tables;
@@ -1393,13 +1420,10 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
{
const char *val;
- assert(!smbios_immutable);
-
val = qemu_opt_get(opts, "file");
if (val) {
struct smbios_structure_header *header;
- int size;
- struct smbios_table *table; /* legacy mode only */
+ size_t size;
if (!qemu_opts_validate(opts, qemu_smbios_file_opts, errp)) {
return;
@@ -1416,9 +1440,9 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
* (except in legacy mode, where the second '\0' is implicit and
* will be inserted by the BIOS).
*/
- smbios_tables = g_realloc(smbios_tables, smbios_tables_len + size);
- header = (struct smbios_structure_header *)(smbios_tables +
- smbios_tables_len);
+ usr_blobs = g_realloc(usr_blobs, usr_blobs_len + size);
+ header = (struct smbios_structure_header *)(usr_blobs +
+ usr_blobs_len);
if (load_image_size(val, (uint8_t *)header, size) != size) {
error_setg(errp, "Failed to load SMBIOS file %s", val);
@@ -1439,34 +1463,15 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
smbios_type4_count++;
}
- smbios_tables_len += size;
- if (size > smbios_table_max) {
- smbios_table_max = size;
+ if (!usr_blobs_sizes) {
+ usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
}
- smbios_table_cnt++;
-
- /* add a copy of the newly loaded blob to legacy smbios_entries */
- /* NOTE: This code runs before smbios_set_defaults(), so we don't
- * yet know which mode (legacy vs. aggregate-table) will be
- * required. We therefore add the binary blob to both legacy
- * (smbios_entries) and aggregate (smbios_tables) tables, and
- * delete the one we don't need from smbios_set_defaults(),
- * once we know which machine version has been requested.
- */
- if (!smbios_entries) {
- smbios_entries_len = sizeof(uint16_t);
- smbios_entries = g_malloc0(smbios_entries_len);
+ g_array_append_val(usr_blobs_sizes, size);
+ usr_blobs_len += size;
+ if (size > usr_table_max) {
+ usr_table_max = size;
}
- smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
- size + sizeof(*table));
- table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
- table->header.type = SMBIOS_TABLE_ENTRY;
- table->header.length = cpu_to_le16(sizeof(*table) + size);
- memcpy(table->data, header, size);
- smbios_entries_len += sizeof(*table) + size;
- (*(uint16_t *)smbios_entries) =
- cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
- /* end: add a copy of the newly loaded blob to legacy smbios_entries */
+ usr_table_cnt++;
return;
}
--
2.39.3

@ -1,517 +0,0 @@
From 7ebb314a4f81d6d1a7dd4980b757fb5e556f5837 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Tue, 13 Feb 2024 16:45:18 +0100
Subject: [PATCH 13/20] smbios: build legacy mode code only for 'pc' machine
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [11/18] 06e639be03e0d151fb9bcf5f728388edcb84219a
JIRA: https://issues.redhat.com/browse/RHEL-21705
basically moving code around without functional change.
And exposing some symbols so that they could be shared
between smbbios.c and new smbios_legacy.c
plus some meson magic to build smbios_legacy.c only
for 'pc' machine and otherwise replace it with stub
if not selected.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Conflicts: hw/smbios/smbios.c
context change due to downstream smbios_type2_required
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/Kconfig | 1 +
hw/smbios/Kconfig | 2 +
hw/smbios/meson.build | 5 +
hw/smbios/smbios.c | 163 +-----------------------------
hw/smbios/smbios_legacy.c | 179 +++++++++++++++++++++++++++++++++
hw/smbios/smbios_legacy_stub.c | 15 +++
include/hw/firmware/smbios.h | 5 +
7 files changed, 208 insertions(+), 162 deletions(-)
create mode 100644 hw/smbios/smbios_legacy.c
create mode 100644 hw/smbios/smbios_legacy_stub.c
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index a1846be6f7..a6ee052f9a 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -76,6 +76,7 @@ config I440FX
select PIIX
select DIMM
select SMBIOS
+ select SMBIOS_LEGACY
select FW_CFG_DMA
config ISAPC
diff --git a/hw/smbios/Kconfig b/hw/smbios/Kconfig
index 553adf4bfc..8d989a2f1b 100644
--- a/hw/smbios/Kconfig
+++ b/hw/smbios/Kconfig
@@ -1,2 +1,4 @@
config SMBIOS
bool
+config SMBIOS_LEGACY
+ bool
diff --git a/hw/smbios/meson.build b/hw/smbios/meson.build
index 6eeae4b35c..fcac1d7490 100644
--- a/hw/smbios/meson.build
+++ b/hw/smbios/meson.build
@@ -4,10 +4,15 @@ smbios_ss.add(when: 'CONFIG_IPMI',
if_true: files('smbios_type_38.c'),
if_false: files('smbios_type_38-stub.c'))
+smbios_ss.add(when: 'CONFIG_SMBIOS_LEGACY',
+ if_true: files('smbios_legacy.c'),
+ if_false: files('smbios_legacy_stub.c'))
+
system_ss.add_all(when: 'CONFIG_SMBIOS', if_true: smbios_ss)
system_ss.add(when: 'CONFIG_SMBIOS', if_false: files('smbios-stub.c'))
system_ss.add(when: 'CONFIG_ALL', if_true: files(
'smbios-stub.c',
'smbios_type_38-stub.c',
+ 'smbios_legacy_stub.c',
))
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index eb9927335d..e40204550e 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -31,31 +31,7 @@
#include "hw/pci/pci_device.h"
#include "smbios_build.h"
-/* legacy structures and constants for <= 2.0 machines */
-struct smbios_header {
- uint16_t length;
- uint8_t type;
-} QEMU_PACKED;
-
-struct smbios_field {
- struct smbios_header header;
- uint8_t type;
- uint16_t offset;
- uint8_t data[];
-} QEMU_PACKED;
-
-struct smbios_table {
- struct smbios_header header;
- uint8_t data[];
-} QEMU_PACKED;
-
-#define SMBIOS_FIELD_ENTRY 0
-#define SMBIOS_TABLE_ENTRY 1
-
-static uint8_t *smbios_entries;
-static size_t smbios_entries_len;
static bool smbios_uuid_encoded = true;
-/* end: legacy structures & constants for <= 2.0 machines */
/* Set to true for modern Windows 10 HardwareID-6 compat */
static bool smbios_type2_required;
@@ -65,7 +41,6 @@ static bool smbios_type2_required;
*/
uint8_t *usr_blobs;
size_t usr_blobs_len;
-static GArray *usr_blobs_sizes;
static unsigned usr_table_max;
static unsigned usr_table_cnt;
@@ -531,7 +506,7 @@ static void smbios_check_type4_count(uint32_t expected_t4_count)
}
}
-static void smbios_validate_table(void)
+void smbios_validate_table(void)
{
if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
@@ -541,134 +516,6 @@ static void smbios_validate_table(void)
}
}
-
-/* legacy setup functions for <= 2.0 machines */
-static void smbios_add_field(int type, int offset, const void *data, size_t len)
-{
- struct smbios_field *field;
-
- if (!smbios_entries) {
- smbios_entries_len = sizeof(uint16_t);
- smbios_entries = g_malloc0(smbios_entries_len);
- }
- smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
- sizeof(*field) + len);
- field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
- field->header.type = SMBIOS_FIELD_ENTRY;
- field->header.length = cpu_to_le16(sizeof(*field) + len);
-
- field->type = type;
- field->offset = cpu_to_le16(offset);
- memcpy(field->data, data, len);
-
- smbios_entries_len += sizeof(*field) + len;
- (*(uint16_t *)smbios_entries) =
- cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
-}
-
-static void smbios_maybe_add_str(int type, int offset, const char *data)
-{
- if (data) {
- smbios_add_field(type, offset, data, strlen(data) + 1);
- }
-}
-
-static void smbios_build_type_0_fields(void)
-{
- smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str),
- smbios_type0.vendor);
- smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str),
- smbios_type0.version);
- smbios_maybe_add_str(0, offsetof(struct smbios_type_0,
- bios_release_date_str),
- smbios_type0.date);
- if (smbios_type0.have_major_minor) {
- smbios_add_field(0, offsetof(struct smbios_type_0,
- system_bios_major_release),
- &smbios_type0.major, 1);
- smbios_add_field(0, offsetof(struct smbios_type_0,
- system_bios_minor_release),
- &smbios_type0.minor, 1);
- }
-}
-
-static void smbios_build_type_1_fields(void)
-{
- smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str),
- smbios_type1.manufacturer);
- smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str),
- smbios_type1.product);
- smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str),
- smbios_type1.version);
- smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str),
- smbios_type1.serial);
- smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str),
- smbios_type1.sku);
- smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
- smbios_type1.family);
- if (qemu_uuid_set) {
- /* We don't encode the UUID in the "wire format" here because this
- * function is for legacy mode and needs to keep the guest ABI, and
- * because we don't know what's the SMBIOS version advertised by the
- * BIOS.
- */
- smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
- &qemu_uuid, 16);
- }
-}
-
-uint8_t *smbios_get_table_legacy(size_t *length)
-{
- int i;
- size_t usr_offset;
-
- /* also complain if fields were given for types > 1 */
- if (find_next_bit(smbios_have_fields_bitmap,
- SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
- error_report("can't process fields for smbios "
- "types > 1 on machine versions < 2.1!");
- exit(1);
- }
-
- if (test_bit(4, smbios_have_binfile_bitmap)) {
- error_report("can't process table for smbios "
- "type 4 on machine versions < 2.1!");
- exit(1);
- }
-
- g_free(smbios_entries);
- smbios_entries_len = sizeof(uint16_t);
- smbios_entries = g_malloc0(smbios_entries_len);
-
- for (i = 0, usr_offset = 0; usr_blobs_sizes && i < usr_blobs_sizes->len;
- i++)
- {
- struct smbios_table *table;
- struct smbios_structure_header *header;
- size_t size = g_array_index(usr_blobs_sizes, size_t, i);
-
- header = (struct smbios_structure_header *)(usr_blobs + usr_offset);
- smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
- size + sizeof(*table));
- table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
- table->header.type = SMBIOS_TABLE_ENTRY;
- table->header.length = cpu_to_le16(sizeof(*table) + size);
- memcpy(table->data, header, size);
- smbios_entries_len += sizeof(*table) + size;
- (*(uint16_t *)smbios_entries) =
- cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
- usr_offset += size;
- }
-
- smbios_build_type_0_fields();
- smbios_build_type_1_fields();
- smbios_validate_table();
- *length = smbios_entries_len;
- return smbios_entries;
-}
-/* end: legacy setup functions for <= 2.0 machines */
-
-
bool smbios_skip_table(uint8_t type, bool required_table)
{
if (test_bit(type, smbios_have_binfile_bitmap)) {
@@ -1418,14 +1265,6 @@ static bool save_opt_list(size_t *ndest, char ***dest, QemuOpts *opts,
return true;
}
-static void smbios_add_usr_blob_size(size_t size)
-{
- if (!usr_blobs_sizes) {
- usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
- }
- g_array_append_val(usr_blobs_sizes, size);
-}
-
void smbios_entry_add(QemuOpts *opts, Error **errp)
{
const char *val;
diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c
new file mode 100644
index 0000000000..21f143e738
--- /dev/null
+++ b/hw/smbios/smbios_legacy.c
@@ -0,0 +1,179 @@
+/*
+ * SMBIOS legacy support
+ *
+ * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
+ * Copyright (C) 2013 Red Hat, Inc.
+ *
+ * Authors:
+ * Alex Williamson <alex.williamson@hp.com>
+ * Markus Armbruster <armbru@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2. See
+ * the COPYING file in the top-level directory.
+ *
+ * Contributions after 2012-01-13 are licensed under the terms of the
+ * GNU GPL, version 2 or (at your option) any later version.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bswap.h"
+#include "hw/firmware/smbios.h"
+#include "sysemu/sysemu.h"
+#include "qemu/error-report.h"
+
+struct smbios_header {
+ uint16_t length;
+ uint8_t type;
+} QEMU_PACKED;
+
+struct smbios_field {
+ struct smbios_header header;
+ uint8_t type;
+ uint16_t offset;
+ uint8_t data[];
+} QEMU_PACKED;
+
+struct smbios_table {
+ struct smbios_header header;
+ uint8_t data[];
+} QEMU_PACKED;
+
+#define SMBIOS_FIELD_ENTRY 0
+#define SMBIOS_TABLE_ENTRY 1
+
+static uint8_t *smbios_entries;
+static size_t smbios_entries_len;
+GArray *usr_blobs_sizes;
+
+void smbios_add_usr_blob_size(size_t size)
+{
+ if (!usr_blobs_sizes) {
+ usr_blobs_sizes = g_array_new(false, false, sizeof(size_t));
+ }
+ g_array_append_val(usr_blobs_sizes, size);
+}
+
+static void smbios_add_field(int type, int offset, const void *data, size_t len)
+{
+ struct smbios_field *field;
+
+ if (!smbios_entries) {
+ smbios_entries_len = sizeof(uint16_t);
+ smbios_entries = g_malloc0(smbios_entries_len);
+ }
+ smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
+ sizeof(*field) + len);
+ field = (struct smbios_field *)(smbios_entries + smbios_entries_len);
+ field->header.type = SMBIOS_FIELD_ENTRY;
+ field->header.length = cpu_to_le16(sizeof(*field) + len);
+
+ field->type = type;
+ field->offset = cpu_to_le16(offset);
+ memcpy(field->data, data, len);
+
+ smbios_entries_len += sizeof(*field) + len;
+ (*(uint16_t *)smbios_entries) =
+ cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+}
+
+static void smbios_maybe_add_str(int type, int offset, const char *data)
+{
+ if (data) {
+ smbios_add_field(type, offset, data, strlen(data) + 1);
+ }
+}
+
+static void smbios_build_type_0_fields(void)
+{
+ smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str),
+ smbios_type0.vendor);
+ smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str),
+ smbios_type0.version);
+ smbios_maybe_add_str(0, offsetof(struct smbios_type_0,
+ bios_release_date_str),
+ smbios_type0.date);
+ if (smbios_type0.have_major_minor) {
+ smbios_add_field(0, offsetof(struct smbios_type_0,
+ system_bios_major_release),
+ &smbios_type0.major, 1);
+ smbios_add_field(0, offsetof(struct smbios_type_0,
+ system_bios_minor_release),
+ &smbios_type0.minor, 1);
+ }
+}
+
+static void smbios_build_type_1_fields(void)
+{
+ smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str),
+ smbios_type1.manufacturer);
+ smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str),
+ smbios_type1.product);
+ smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str),
+ smbios_type1.version);
+ smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str),
+ smbios_type1.serial);
+ smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str),
+ smbios_type1.sku);
+ smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
+ smbios_type1.family);
+ if (qemu_uuid_set) {
+ /*
+ * We don't encode the UUID in the "wire format" here because this
+ * function is for legacy mode and needs to keep the guest ABI, and
+ * because we don't know what's the SMBIOS version advertised by the
+ * BIOS.
+ */
+ smbios_add_field(1, offsetof(struct smbios_type_1, uuid),
+ &qemu_uuid, 16);
+ }
+}
+
+uint8_t *smbios_get_table_legacy(size_t *length)
+{
+ int i;
+ size_t usr_offset;
+
+ /* complain if fields were given for types > 1 */
+ if (find_next_bit(smbios_have_fields_bitmap,
+ SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
+ error_report("can't process fields for smbios "
+ "types > 1 on machine versions < 2.1!");
+ exit(1);
+ }
+
+ if (test_bit(4, smbios_have_binfile_bitmap)) {
+ error_report("can't process table for smbios "
+ "type 4 on machine versions < 2.1!");
+ exit(1);
+ }
+
+ g_free(smbios_entries);
+ smbios_entries_len = sizeof(uint16_t);
+ smbios_entries = g_malloc0(smbios_entries_len);
+
+ for (i = 0, usr_offset = 0; usr_blobs_sizes && i < usr_blobs_sizes->len;
+ i++)
+ {
+ struct smbios_table *table;
+ struct smbios_structure_header *header;
+ size_t size = g_array_index(usr_blobs_sizes, size_t, i);
+
+ header = (struct smbios_structure_header *)(usr_blobs + usr_offset);
+ smbios_entries = g_realloc(smbios_entries, smbios_entries_len +
+ size + sizeof(*table));
+ table = (struct smbios_table *)(smbios_entries + smbios_entries_len);
+ table->header.type = SMBIOS_TABLE_ENTRY;
+ table->header.length = cpu_to_le16(sizeof(*table) + size);
+ memcpy(table->data, header, size);
+ smbios_entries_len += sizeof(*table) + size;
+ (*(uint16_t *)smbios_entries) =
+ cpu_to_le16(le16_to_cpu(*(uint16_t *)smbios_entries) + 1);
+ usr_offset += size;
+ }
+
+ smbios_build_type_0_fields();
+ smbios_build_type_1_fields();
+ smbios_validate_table();
+ *length = smbios_entries_len;
+ return smbios_entries;
+}
diff --git a/hw/smbios/smbios_legacy_stub.c b/hw/smbios/smbios_legacy_stub.c
new file mode 100644
index 0000000000..f29b15316c
--- /dev/null
+++ b/hw/smbios/smbios_legacy_stub.c
@@ -0,0 +1,15 @@
+/*
+ * IPMI SMBIOS firmware handling
+ *
+ * Copyright (c) 2024 Igor Mammedov, Red Hat, Inc.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/firmware/smbios.h"
+
+void smbios_add_usr_blob_size(size_t size)
+{
+}
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index 333de0d5fc..92e9aba415 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -17,6 +17,9 @@
*
*/
+extern uint8_t *usr_blobs;
+extern GArray *usr_blobs_sizes;
+
typedef struct {
const char *vendor, *version, *date;
bool have_major_minor, uefi;
@@ -323,6 +326,8 @@ struct smbios_type_127 {
struct smbios_structure_header header;
} QEMU_PACKED;
+void smbios_validate_table(void);
+void smbios_add_usr_blob_size(size_t size);
void smbios_entry_add(QemuOpts *opts, Error **errp);
void smbios_set_cpuid(uint32_t version, uint32_t features);
void smbios_set_defaults(const char *manufacturer, const char *product,
--
2.39.3

@ -1,65 +0,0 @@
From 07f6ef2d032cda3e746ac2477c0a9bc1ac636f45 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Fri, 29 Dec 2023 14:08:13 +0100
Subject: [PATCH 06/20] smbios: cleanup smbios_get_tables() from legacy
handling
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [4/18] a5e09ce1df72293fecad863edd146a8c4b1a734f
JIRA: https://issues.redhat.com/browse/RHEL-21705
smbios_get_tables() bails out right away if leagacy mode is enabled
and won't generate any SMBIOS tables. At the same time x86 specific
fw_cfg_build_smbios() will genarate legacy tables and then proceed
to preparing temporary mem_array for useless call to
smbios_get_tables() and then discard it.
Drop legacy related check in smbios_get_tables() and return from
fw_cfg_build_smbios() early if legacy tables where built without
proceeding to non legacy part of the function.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
---
hw/i386/fw_cfg.c | 1 +
hw/smbios/smbios.c | 6 ------
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index 6a5466faf0..ed72b1442d 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -76,6 +76,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
if (smbios_tables) {
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
+ return;
}
/* build the array of physical mem area from e820 table */
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 074705fa4c..b13e40bae2 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -1244,12 +1244,6 @@ void smbios_get_tables(MachineState *ms,
{
unsigned i, dimm_cnt, offset;
- if (smbios_legacy) {
- *tables = *anchor = NULL;
- *tables_len = *anchor_len = 0;
- return;
- }
-
if (!smbios_immutable) {
smbios_build_type_0_table();
smbios_build_type_1_table();
--
2.39.3

@ -1,38 +0,0 @@
From c0282a842a912aacac28a6ae229de5854c3fb5df Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Mon, 26 Feb 2024 13:20:22 +0100
Subject: [PATCH 16/20] smbios: clear smbios_type4_count before building tables
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [14/18] f809595d2934ae975c0b7d17a4a79645e062ba42
JIRA: https://issues.redhat.com/browse/RHEL-21705
it will help to keep type 4 tables accounting correct in case
SMBIOS tables are built multiple times.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
---
hw/smbios/smbios.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 7e32430b85..4521ea386c 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -1111,6 +1111,7 @@ void smbios_get_tables(MachineState *ms,
ep_type == SMBIOS_ENTRY_POINT_TYPE_64);
g_free(smbios_tables);
+ smbios_type4_count = 0;
smbios_tables = g_memdup2(usr_blobs, usr_blobs_len);
smbios_tables_len = usr_blobs_len;
smbios_table_max = usr_table_max;
--
2.39.3

@ -1,133 +0,0 @@
From 2b76d95ec07aba6d96070ee90c5015c1676be091 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Tue, 13 Feb 2024 16:25:54 +0100
Subject: [PATCH 10/20] smbios: don't check type4 structures in legacy mode
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [8/18] c1f8409ea0d916f333c9373535bf21b521c62855
JIRA: https://issues.redhat.com/browse/RHEL-21705
legacy mode doesn't support structures of type 2 and more,
and CLI has a check for '-smbios type' option, however it's
still possible to sneak in type4 as a blob with '-smbios file'
option. However doing the later makes SMBIOS tables broken
since SeaBIOS doesn't expect that.
Rather than trying to add support for type4 to legacy code
(both QEMU and SeaBIOS), simplify smbios_get_table_legacy()
by dropping not relevant check in legacy code and error out
on type4 blob.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Conflicts: include/hw/firmware/smbios.h
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Please enter the commit message for your changes. Lines starting
---
hw/i386/fw_cfg.c | 3 +--
hw/smbios/smbios.c | 18 ++++++++++++++----
include/hw/firmware/smbios.h | 2 +-
3 files changed, 16 insertions(+), 7 deletions(-)
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index bb7149c4c3..a25793a68f 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -73,8 +73,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
if (pcmc->smbios_legacy_mode) {
- smbios_tables = smbios_get_table_legacy(ms->smp.cpus,
- &smbios_tables_len);
+ smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
return;
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index d8d68716d4..441517cf24 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -530,14 +530,17 @@ opts_init(smbios_register_config);
*/
#define SMBIOS_21_MAX_TABLES_LEN 0xffff
-static void smbios_validate_table(uint32_t expected_t4_count)
+static void smbios_check_type4_count(uint32_t expected_t4_count)
{
if (smbios_type4_count && smbios_type4_count != expected_t4_count) {
error_report("Expected %d SMBIOS Type 4 tables, got %d instead",
expected_t4_count, smbios_type4_count);
exit(1);
}
+}
+static void smbios_validate_table(void)
+{
if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
error_report("SMBIOS 2.1 table length %zu exceeds %d",
@@ -622,7 +625,7 @@ static void smbios_build_type_1_fields(void)
}
}
-uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
+uint8_t *smbios_get_table_legacy(size_t *length)
{
int i;
size_t usr_offset;
@@ -635,6 +638,12 @@ uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
exit(1);
}
+ if (test_bit(4, have_binfile_bitmap)) {
+ error_report("can't process table for smbios "
+ "type 4 on machine versions < 2.1!");
+ exit(1);
+ }
+
g_free(smbios_entries);
smbios_entries_len = sizeof(uint16_t);
smbios_entries = g_malloc0(smbios_entries_len);
@@ -661,7 +670,7 @@ uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
smbios_build_type_0_fields();
smbios_build_type_1_fields();
- smbios_validate_table(expected_t4_count);
+ smbios_validate_table();
*length = smbios_entries_len;
return smbios_entries;
}
@@ -1319,7 +1328,8 @@ void smbios_get_tables(MachineState *ms,
smbios_build_type_41_table(errp);
smbios_build_type_127_table();
- smbios_validate_table(ms->smp.sockets);
+ smbios_check_type4_count(ms->smp.sockets);
+ smbios_validate_table();
smbios_entry_point_setup();
/* return tables blob and entry point (anchor), and their sizes */
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index b9fc9a0f42..d55018e5e3 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -315,7 +315,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
SmbiosEntryPointType ep_type,
const char *stream_product,
const char *stream_version);
-uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length);
+uint8_t *smbios_get_table_legacy(size_t *length);
void smbios_get_tables(MachineState *ms,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
--
2.39.3

@ -1,72 +0,0 @@
From bbb2d260e6f33380b9df28c74421055bd8dccda5 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Mon, 26 Feb 2024 12:59:33 +0100
Subject: [PATCH 19/20] smbios: error out when building type 4 table is not
possible
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [17/18] 86b1c67bfbe9c0c14a190cd1204b6ccd1de1630f
JIRA: https://issues.redhat.com/browse/RHEL-21705
If SMBIOS v2 version is requested but number of cores/threads
are more than it's possible to describe with v2, error out
instead of silently ignoring the fact and filling core/thread
count with bogus values.
This will help caller to decide if it should fallback to
SMBIOSv3 when smbios-entry-point-type='auto'
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
---
hw/smbios/smbios.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 3d9dcb0d31..637aa952f5 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -655,7 +655,8 @@ static void smbios_build_type_3_table(void)
}
static void smbios_build_type_4_table(MachineState *ms, unsigned instance,
- SmbiosEntryPointType ep_type)
+ SmbiosEntryPointType ep_type,
+ Error **errp)
{
char sock_str[128];
size_t tbl_len = SMBIOS_TYPE_4_LEN_V28;
@@ -709,6 +710,12 @@ static void smbios_build_type_4_table(MachineState *ms, unsigned instance,
if (tbl_len == SMBIOS_TYPE_4_LEN_V30) {
t->core_count2 = t->core_enabled2 = cpu_to_le16(cores_per_socket);
t->thread_count2 = cpu_to_le16(threads_per_socket);
+ } else if (t->core_count == 0xFF || t->thread_count == 0xFF) {
+ error_setg(errp, "SMBIOS 2.0 doesn't support number of processor "
+ "cores/threads more than 255, use "
+ "-machine smbios-entry-point-type=64 option to enable "
+ "SMBIOS 3.0 support");
+ return;
}
SMBIOS_BUILD_TABLE_POST;
@@ -1126,7 +1133,10 @@ static bool smbios_get_tables_ep(MachineState *ms,
assert(ms->smp.sockets >= 1);
for (i = 0; i < ms->smp.sockets; i++) {
- smbios_build_type_4_table(ms, i, ep_type);
+ smbios_build_type_4_table(ms, i, ep_type, errp);
+ if (*errp) {
+ goto err_exit;
+ }
}
smbios_build_type_8_table();
--
2.39.3

@ -1,48 +0,0 @@
From c083959c963fde33f4769fd4c6e122dd16ce6d3c Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Wed, 21 Feb 2024 16:04:36 +0100
Subject: [PATCH 17/20] smbios: extend smbios-entry-point-type with 'auto'
value
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [15/18] 202563dcf77e062a238aa2a10ec14c25d3f5a7d0
JIRA: https://issues.redhat.com/browse/RHEL-21705
later patches will use it to pick SMBIOS version at runtime
depending on configuration.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
---
qapi/machine.json | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/qapi/machine.json b/qapi/machine.json
index b6d634b30d..99f6368fa6 100644
--- a/qapi/machine.json
+++ b/qapi/machine.json
@@ -1788,10 +1788,13 @@
#
# @64: SMBIOS version 3.0 (64-bit) Entry Point
#
+# @auto: Either 2.x or 3.x SMBIOS version, 2.x if configuration can be
+# described by it and 3.x otherwise (since: 9.0)
+#
# Since: 7.0
##
{ 'enum': 'SmbiosEntryPointType',
- 'data': [ '32', '64' ] }
+ 'data': [ '32', '64', 'auto' ] }
##
# @MemorySizeConfiguration:
--
2.39.3

@ -1,281 +0,0 @@
From be0abbf3f7845847b46486704c46c5de5a2b2323 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Mon, 26 Feb 2024 13:49:14 +0100
Subject: [PATCH 15/20] smbios: get rid of global smbios_ep_type
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [13/18] 2e838ed0d03989e2e4ee08041b5ba64d5d7f5820
JIRA: https://issues.redhat.com/browse/RHEL-21705
Conflicts: hw/arm/virt.c, hw/i386/fw_cfg.c, hw/riscv/virt.c, hw/smbios/smbios.c,
include/hw/firmware/smbios.h
due to downstream specific smbios_set_defaults()
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Acked-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/arm/virt.c | 4 ++--
hw/i386/fw_cfg.c | 6 +++---
hw/i386/fw_cfg.h | 3 ++-
hw/i386/pc.c | 2 +-
hw/loongarch/virt.c | 7 ++++---
hw/smbios/smbios.c | 26 ++++++++++++++------------
hw/smbios/smbios_legacy.c | 2 +-
include/hw/firmware/smbios.h | 4 ++--
8 files changed, 29 insertions(+), 25 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index e5cfc19c08..e4a66affcb 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1695,14 +1695,14 @@ static void virt_build_smbios(VirtMachineState *vms)
smbios_set_defaults("QEMU", product,
vmc->smbios_old_sys_ver ? "1.0" : mc->name,
- true, SMBIOS_ENTRY_POINT_TYPE_64,
+ true,
NULL, NULL);
/* build the array of physical mem area from base_memmap */
mem_array.address = vms->memmap[VIRT_MEM].base;
mem_array.length = ms->ram_size;
- smbios_get_tables(ms, &mem_array, 1,
+ smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, &mem_array, 1,
&smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len,
&error_fatal);
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index bdc3cc4556..58429bb78d 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -48,7 +48,8 @@ const char *fw_cfg_arch_key_name(uint16_t key)
return NULL;
}
-void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
+void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg,
+ SmbiosEntryPointType ep_type)
{
#ifdef CONFIG_SMBIOS
uint8_t *smbios_tables, *smbios_anchor;
@@ -64,7 +65,6 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", mc->desc, mc->name,
pcmc->smbios_uuid_encoded,
- pcms->smbios_entry_point_type,
pcmc->smbios_stream_product,
pcmc->smbios_stream_version);
}
@@ -91,7 +91,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
array_count++;
}
}
- smbios_get_tables(ms, mem_array, array_count,
+ smbios_get_tables(ms, ep_type, mem_array, array_count,
&smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len,
&error_fatal);
diff --git a/hw/i386/fw_cfg.h b/hw/i386/fw_cfg.h
index 1e1de6b4a3..92e310f5fd 100644
--- a/hw/i386/fw_cfg.h
+++ b/hw/i386/fw_cfg.h
@@ -23,7 +23,8 @@
FWCfgState *fw_cfg_arch_create(MachineState *ms,
uint16_t boot_cpus,
uint16_t apic_id_limit);
-void fw_cfg_build_smbios(PCMachineState *ms, FWCfgState *fw_cfg);
+void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg,
+ SmbiosEntryPointType ep_type);
void fw_cfg_build_feature_control(MachineState *ms, FWCfgState *fw_cfg);
void fw_cfg_add_acpi_dsdt(Aml *scope, FWCfgState *fw_cfg);
diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index 16de2a59e8..ae6777fc1a 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -847,7 +847,7 @@ void pc_machine_done(Notifier *notifier, void *data)
acpi_setup();
if (x86ms->fw_cfg) {
- fw_cfg_build_smbios(pcms, x86ms->fw_cfg);
+ fw_cfg_build_smbios(pcms, x86ms->fw_cfg, pcms->smbios_entry_point_type);
fw_cfg_build_feature_control(MACHINE(pcms), x86ms->fw_cfg);
/* update FW_CFG_NB_CPUS to account for -device added CPUs */
fw_cfg_modify_i16(x86ms->fw_cfg, FW_CFG_NB_CPUS, x86ms->boot_cpus);
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index 7358a023d3..77956b5ada 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -320,10 +320,11 @@ static void virt_build_smbios(LoongArchMachineState *lams)
return;
}
- smbios_set_defaults("QEMU", product, mc->name,
- true, SMBIOS_ENTRY_POINT_TYPE_64);
+ smbios_set_defaults("QEMU", product, mc->name, true);
- smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len,
+ smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64,
+ NULL, 0,
+ &smbios_tables, &smbios_tables_len,
&smbios_anchor, &smbios_anchor_len, &error_fatal);
if (smbios_anchor) {
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index b5745c6c2d..7e32430b85 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -47,7 +47,6 @@ uint8_t *smbios_tables;
size_t smbios_tables_len;
unsigned smbios_table_max;
unsigned smbios_table_cnt;
-static SmbiosEntryPointType smbios_ep_type = SMBIOS_ENTRY_POINT_TYPE_32;
static SmbiosEntryPoint ep;
@@ -506,9 +505,9 @@ static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp)
return true;
}
-bool smbios_validate_table(Error **errp)
+bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp)
{
- if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d",
smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN);
@@ -655,14 +654,15 @@ static void smbios_build_type_3_table(void)
SMBIOS_BUILD_TABLE_POST;
}
-static void smbios_build_type_4_table(MachineState *ms, unsigned instance)
+static void smbios_build_type_4_table(MachineState *ms, unsigned instance,
+ SmbiosEntryPointType ep_type)
{
char sock_str[128];
size_t tbl_len = SMBIOS_TYPE_4_LEN_V28;
unsigned threads_per_socket;
unsigned cores_per_socket;
- if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_64) {
+ if (ep_type == SMBIOS_ENTRY_POINT_TYPE_64) {
tbl_len = SMBIOS_TYPE_4_LEN_V30;
}
@@ -991,13 +991,11 @@ void smbios_set_cpuid(uint32_t version, uint32_t features)
void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version,
bool uuid_encoded,
- SmbiosEntryPointType ep_type,
const char *stream_product,
const char *stream_version)
{
smbios_have_defaults = true;
smbios_uuid_encoded = uuid_encoded;
- smbios_ep_type = ep_type;
/*
* If @stream_product & @stream_version are non-NULL, then
@@ -1048,9 +1046,9 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
SMBIOS_SET_DEFAULT(type17.manufacturer, manufacturer);
}
-static void smbios_entry_point_setup(void)
+static void smbios_entry_point_setup(SmbiosEntryPointType ep_type)
{
- switch (smbios_ep_type) {
+ switch (ep_type) {
case SMBIOS_ENTRY_POINT_TYPE_32:
memcpy(ep.ep21.anchor_string, "_SM_", 4);
memcpy(ep.ep21.intermediate_anchor_string, "_DMI_", 5);
@@ -1100,6 +1098,7 @@ static void smbios_entry_point_setup(void)
}
void smbios_get_tables(MachineState *ms,
+ SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len,
@@ -1108,6 +1107,9 @@ void smbios_get_tables(MachineState *ms,
{
unsigned i, dimm_cnt, offset;
+ assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ||
+ ep_type == SMBIOS_ENTRY_POINT_TYPE_64);
+
g_free(smbios_tables);
smbios_tables = g_memdup2(usr_blobs, usr_blobs_len);
smbios_tables_len = usr_blobs_len;
@@ -1122,7 +1124,7 @@ void smbios_get_tables(MachineState *ms,
assert(ms->smp.sockets >= 1);
for (i = 0; i < ms->smp.sockets; i++) {
- smbios_build_type_4_table(ms, i);
+ smbios_build_type_4_table(ms, i, ep_type);
}
smbios_build_type_8_table();
@@ -1171,10 +1173,10 @@ void smbios_get_tables(MachineState *ms,
if (!smbios_check_type4_count(ms->smp.sockets, errp)) {
goto err_exit;
}
- if (!smbios_validate_table(errp)) {
+ if (!smbios_validate_table(ep_type, errp)) {
goto err_exit;
}
- smbios_entry_point_setup();
+ smbios_entry_point_setup(ep_type);
/* return tables blob and entry point (anchor), and their sizes */
*tables = smbios_tables;
diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c
index a6544bf55a..06907cd16c 100644
--- a/hw/smbios/smbios_legacy.c
+++ b/hw/smbios/smbios_legacy.c
@@ -173,7 +173,7 @@ uint8_t *smbios_get_table_legacy(size_t *length, Error **errp)
smbios_build_type_0_fields();
smbios_build_type_1_fields();
- if (!smbios_validate_table(errp)) {
+ if (!smbios_validate_table(SMBIOS_ENTRY_POINT_TYPE_32, errp)) {
goto err_exit;
}
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index 44af3a0d82..781298f594 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -326,18 +326,18 @@ struct smbios_type_127 {
struct smbios_structure_header header;
} QEMU_PACKED;
-bool smbios_validate_table(Error **errp);
+bool smbios_validate_table(SmbiosEntryPointType ep_type, Error **errp);
void smbios_add_usr_blob_size(size_t size);
void smbios_entry_add(QemuOpts *opts, Error **errp);
void smbios_set_cpuid(uint32_t version, uint32_t features);
void smbios_set_defaults(const char *manufacturer, const char *product,
const char *version,
bool uuid_encoded,
- SmbiosEntryPointType ep_type,
const char *stream_product,
const char *stream_version);
uint8_t *smbios_get_table_legacy(size_t *length, Error **errp);
void smbios_get_tables(MachineState *ms,
+ SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
uint8_t **tables, size_t *tables_len,
--
2.39.3

@ -1,198 +0,0 @@
From 0802fa7199c8085d018fc38dd4beaa5062d383d1 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Fri, 29 Dec 2023 15:37:29 +0100
Subject: [PATCH 08/20] smbios: get rid of smbios_legacy global
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [6/18] 684dd1dca8d611c6de97b26ef8c1cda6ca509d54
JIRA: https://issues.redhat.com/browse/RHEL-21705
clean up smbios_set_defaults() which is reused by legacy
and non legacy machines from being aware of 'legacy' notion
and need to turn it off. And push legacy handling up to
PC machine code where it's relevant.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Acked-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Conflicts: hw/arm/virt.c, hw/i386/fw_cfg.c, hw/loongarch/virt.c,
hw/smbios/smbios.c, include/hw/firmware/smbios.h
due to downstream specifc signature of smbios_set_defaults()
PS:
while fixing conflicts move RHEL specific
smbios_stream_product/smbios_stream_version
at the end of arguments list
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/arm/virt.c | 5 +++--
hw/i386/fw_cfg.c | 11 +++++-----
hw/loongarch/virt.c | 2 +-
hw/smbios/smbios.c | 39 ++++++++++++++++--------------------
include/hw/firmware/smbios.h | 6 +++---
5 files changed, 30 insertions(+), 33 deletions(-)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 943c563391..e5cfc19c08 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -1694,8 +1694,9 @@ static void virt_build_smbios(VirtMachineState *vms)
}
smbios_set_defaults("QEMU", product,
- vmc->smbios_old_sys_ver ? "1.0" : mc->name, false,
- true, NULL, NULL, SMBIOS_ENTRY_POINT_TYPE_64);
+ vmc->smbios_old_sys_ver ? "1.0" : mc->name,
+ true, SMBIOS_ENTRY_POINT_TYPE_64,
+ NULL, NULL);
/* build the array of physical mem area from base_memmap */
mem_array.address = vms->memmap[VIRT_MEM].base;
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index 79ff7f7225..bb7149c4c3 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -63,17 +63,18 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
if (pcmc->smbios_defaults) {
/* These values are guest ABI, do not change */
smbios_set_defaults("QEMU", mc->desc, mc->name,
- pcmc->smbios_legacy_mode, pcmc->smbios_uuid_encoded,
+ pcmc->smbios_uuid_encoded,
+ pcms->smbios_entry_point_type,
pcmc->smbios_stream_product,
- pcmc->smbios_stream_version,
- pcms->smbios_entry_point_type);
+ pcmc->smbios_stream_version);
}
/* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
- smbios_tables = smbios_get_table_legacy(ms->smp.cpus, &smbios_tables_len);
- if (smbios_tables) {
+ if (pcmc->smbios_legacy_mode) {
+ smbios_tables = smbios_get_table_legacy(ms->smp.cpus,
+ &smbios_tables_len);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
return;
diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c
index 4b7dc67a2d..7358a023d3 100644
--- a/hw/loongarch/virt.c
+++ b/hw/loongarch/virt.c
@@ -320,7 +320,7 @@ static void virt_build_smbios(LoongArchMachineState *lams)
return;
}
- smbios_set_defaults("QEMU", product, mc->name, false,
+ smbios_set_defaults("QEMU", product, mc->name,
true, SMBIOS_ENTRY_POINT_TYPE_64);
smbios_get_tables(ms, NULL, 0, &smbios_tables, &smbios_tables_len,
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 8129d396d1..0c8c439859 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -54,7 +54,6 @@ struct smbios_table {
static uint8_t *smbios_entries;
static size_t smbios_entries_len;
-static bool smbios_legacy = true;
static bool smbios_uuid_encoded = true;
/* end: legacy structures & constants for <= 2.0 machines */
@@ -618,9 +617,16 @@ static void smbios_build_type_1_fields(void)
uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
{
- if (!smbios_legacy) {
- *length = 0;
- return NULL;
+ /* drop unwanted version of command-line file blob(s) */
+ g_free(smbios_tables);
+ smbios_tables = NULL;
+
+ /* also complain if fields were given for types > 1 */
+ if (find_next_bit(have_fields_bitmap,
+ SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
+ error_report("can't process fields for smbios "
+ "types > 1 on machine versions < 2.1!");
+ exit(1);
}
if (!smbios_immutable) {
@@ -1107,31 +1113,16 @@ void smbios_set_cpuid(uint32_t version, uint32_t features)
}
void smbios_set_defaults(const char *manufacturer, const char *product,
- const char *version, bool legacy_mode,
+ const char *version,
bool uuid_encoded,
+ SmbiosEntryPointType ep_type,
const char *stream_product,
- const char *stream_version,
- SmbiosEntryPointType ep_type)
+ const char *stream_version)
{
smbios_have_defaults = true;
- smbios_legacy = legacy_mode;
smbios_uuid_encoded = uuid_encoded;
smbios_ep_type = ep_type;
- /* drop unwanted version of command-line file blob(s) */
- if (smbios_legacy) {
- g_free(smbios_tables);
- /* in legacy mode, also complain if fields were given for types > 1 */
- if (find_next_bit(have_fields_bitmap,
- SMBIOS_MAX_TYPE+1, 2) < SMBIOS_MAX_TYPE+1) {
- error_report("can't process fields for smbios "
- "types > 1 on machine versions < 2.1!");
- exit(1);
- }
- } else {
- g_free(smbios_entries);
- }
-
/*
* If @stream_product & @stream_version are non-NULL, then
* we're following rules for new Windows driver support.
@@ -1241,6 +1232,10 @@ void smbios_get_tables(MachineState *ms,
{
unsigned i, dimm_cnt, offset;
+ /* drop unwanted (legacy) version of command-line file blob(s) */
+ g_free(smbios_entries);
+ smbios_entries = NULL;
+
if (!smbios_immutable) {
smbios_build_type_0_table();
smbios_build_type_1_table();
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index 95ec64ce2c..b9fc9a0f42 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -310,11 +310,11 @@ struct smbios_type_127 {
void smbios_entry_add(QemuOpts *opts, Error **errp);
void smbios_set_cpuid(uint32_t version, uint32_t features);
void smbios_set_defaults(const char *manufacturer, const char *product,
- const char *version, bool legacy_mode,
+ const char *version,
bool uuid_encoded,
+ SmbiosEntryPointType ep_type,
const char *stream_product,
- const char *stream_version,
- SmbiosEntryPointType ep_type);
+ const char *stream_version);
uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length);
void smbios_get_tables(MachineState *ms,
const struct smbios_phys_mem_area *mem_array,
--
2.39.3

@ -1,134 +0,0 @@
From 1536f1ec00ddc1729854b381cc0d54814bb6c19f Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Fri, 29 Dec 2023 15:04:55 +0100
Subject: [PATCH 07/20] smbios: get rid of smbios_smp_sockets global
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [5/18] f59bfeb547b7febcf1de2b6179af006e7fa0bccd
JIRA: https://issues.redhat.com/browse/RHEL-21705
it makes smbios_validate_table() independent from
smbios_smp_sockets global, which in turn lets
smbios_get_tables() avoid using not related legacy code.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Conflicts:
include/hw/firmware/smbios.h due to down-stream
(d9ff466c980d Machine type related general changes)
adding custom stream_product/stream_version
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/fw_cfg.c | 2 +-
hw/smbios/smbios.c | 22 +++++++++-------------
include/hw/firmware/smbios.h | 2 +-
3 files changed, 11 insertions(+), 15 deletions(-)
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index ed72b1442d..79ff7f7225 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -72,7 +72,7 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
/* tell smbios about cpuid version and features */
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
- smbios_tables = smbios_get_table_legacy(ms, &smbios_tables_len);
+ smbios_tables = smbios_get_table_legacy(ms->smp.cpus, &smbios_tables_len);
if (smbios_tables) {
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index b13e40bae2..8129d396d1 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -73,7 +73,7 @@ static SmbiosEntryPoint ep;
static int smbios_type4_count = 0;
static bool smbios_immutable;
static bool smbios_have_defaults;
-static uint32_t smbios_cpuid_version, smbios_cpuid_features, smbios_smp_sockets;
+static uint32_t smbios_cpuid_version, smbios_cpuid_features;
static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1);
static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1);
@@ -524,14 +524,11 @@ opts_init(smbios_register_config);
*/
#define SMBIOS_21_MAX_TABLES_LEN 0xffff
-static void smbios_validate_table(MachineState *ms)
+static void smbios_validate_table(uint32_t expected_t4_count)
{
- uint32_t expect_t4_count = smbios_legacy ?
- ms->smp.cpus : smbios_smp_sockets;
-
- if (smbios_type4_count && smbios_type4_count != expect_t4_count) {
+ if (smbios_type4_count && smbios_type4_count != expected_t4_count) {
error_report("Expected %d SMBIOS Type 4 tables, got %d instead",
- expect_t4_count, smbios_type4_count);
+ expected_t4_count, smbios_type4_count);
exit(1);
}
@@ -619,7 +616,7 @@ static void smbios_build_type_1_fields(void)
}
}
-uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length)
+uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length)
{
if (!smbios_legacy) {
*length = 0;
@@ -629,7 +626,7 @@ uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length)
if (!smbios_immutable) {
smbios_build_type_0_fields();
smbios_build_type_1_fields();
- smbios_validate_table(ms);
+ smbios_validate_table(expected_t4_count);
smbios_immutable = true;
}
*length = smbios_entries_len;
@@ -1250,10 +1247,9 @@ void smbios_get_tables(MachineState *ms,
smbios_build_type_2_table();
smbios_build_type_3_table();
- smbios_smp_sockets = ms->smp.sockets;
- assert(smbios_smp_sockets >= 1);
+ assert(ms->smp.sockets >= 1);
- for (i = 0; i < smbios_smp_sockets; i++) {
+ for (i = 0; i < ms->smp.sockets; i++) {
smbios_build_type_4_table(ms, i);
}
@@ -1299,7 +1295,7 @@ void smbios_get_tables(MachineState *ms,
smbios_build_type_41_table(errp);
smbios_build_type_127_table();
- smbios_validate_table(ms);
+ smbios_validate_table(ms->smp.sockets);
smbios_entry_point_setup();
smbios_immutable = true;
}
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index f8dd07fe4c..95ec64ce2c 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -315,7 +315,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
const char *stream_product,
const char *stream_version,
SmbiosEntryPointType ep_type);
-uint8_t *smbios_get_table_legacy(MachineState *ms, size_t *length);
+uint8_t *smbios_get_table_legacy(uint32_t expected_t4_count, size_t *length);
void smbios_get_tables(MachineState *ms,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
--
2.39.3

@ -1,217 +0,0 @@
From 7271c4424c6d90f0bb34f8090eb4e192eb2b2537 Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Tue, 20 Feb 2024 10:30:28 +0100
Subject: [PATCH 14/20] smbios: handle errors consistently
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [12/18] d8a5a70602ae0665ce35e5bf87b2d8420f9189bc
JIRA: https://issues.redhat.com/browse/RHEL-21705
Current code uses mix of error_report()+exit(1)
and error_setg() to handle errors.
Use newer error_setg() everywhere, beside consistency
it will allow to detect error condition without killing
QEMU and attempt switch-over to SMBIOS3.x tables/entrypoint
in follow up patch.
while at it, clear smbios_tables pointer after freeing.
that will avoid double free if smbios_get_tables() is called
multiple times.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Conflicts: include/hw/firmware/smbios.h
due to downstream specific smbios_set_defaults()
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/i386/fw_cfg.c | 3 ++-
hw/smbios/smbios.c | 34 ++++++++++++++++++++++------------
hw/smbios/smbios_legacy.c | 22 ++++++++++++++--------
include/hw/firmware/smbios.h | 4 ++--
4 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c
index a25793a68f..bdc3cc4556 100644
--- a/hw/i386/fw_cfg.c
+++ b/hw/i386/fw_cfg.c
@@ -73,7 +73,8 @@ void fw_cfg_build_smbios(PCMachineState *pcms, FWCfgState *fw_cfg)
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
if (pcmc->smbios_legacy_mode) {
- smbios_tables = smbios_get_table_legacy(&smbios_tables_len);
+ smbios_tables = smbios_get_table_legacy(&smbios_tables_len,
+ &error_fatal);
fw_cfg_add_bytes(fw_cfg, FW_CFG_SMBIOS_ENTRIES,
smbios_tables, smbios_tables_len);
return;
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index e40204550e..b5745c6c2d 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -19,7 +19,6 @@
#include "qemu/units.h"
#include "qapi/error.h"
#include "qemu/config-file.h"
-#include "qemu/error-report.h"
#include "qemu/module.h"
#include "qemu/option.h"
#include "sysemu/sysemu.h"
@@ -497,23 +496,25 @@ opts_init(smbios_register_config);
*/
#define SMBIOS_21_MAX_TABLES_LEN 0xffff
-static void smbios_check_type4_count(uint32_t expected_t4_count)
+static bool smbios_check_type4_count(uint32_t expected_t4_count, Error **errp)
{
if (smbios_type4_count && smbios_type4_count != expected_t4_count) {
- error_report("Expected %d SMBIOS Type 4 tables, got %d instead",
- expected_t4_count, smbios_type4_count);
- exit(1);
+ error_setg(errp, "Expected %d SMBIOS Type 4 tables, got %d instead",
+ expected_t4_count, smbios_type4_count);
+ return false;
}
+ return true;
}
-void smbios_validate_table(void)
+bool smbios_validate_table(Error **errp)
{
if (smbios_ep_type == SMBIOS_ENTRY_POINT_TYPE_32 &&
smbios_tables_len > SMBIOS_21_MAX_TABLES_LEN) {
- error_report("SMBIOS 2.1 table length %zu exceeds %d",
- smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN);
- exit(1);
+ error_setg(errp, "SMBIOS 2.1 table length %zu exceeds %d",
+ smbios_tables_len, SMBIOS_21_MAX_TABLES_LEN);
+ return false;
}
+ return true;
}
bool smbios_skip_table(uint8_t type, bool required_table)
@@ -1167,15 +1168,18 @@ void smbios_get_tables(MachineState *ms,
smbios_build_type_41_table(errp);
smbios_build_type_127_table();
- smbios_check_type4_count(ms->smp.sockets);
- smbios_validate_table();
+ if (!smbios_check_type4_count(ms->smp.sockets, errp)) {
+ goto err_exit;
+ }
+ if (!smbios_validate_table(errp)) {
+ goto err_exit;
+ }
smbios_entry_point_setup();
/* return tables blob and entry point (anchor), and their sizes */
*tables = smbios_tables;
*tables_len = smbios_tables_len;
*anchor = (uint8_t *)&ep;
-
/* calculate length based on anchor string */
if (!strncmp((char *)&ep, "_SM_", 4)) {
*anchor_len = sizeof(struct smbios_21_entry_point);
@@ -1184,6 +1188,12 @@ void smbios_get_tables(MachineState *ms,
} else {
abort();
}
+
+ return;
+err_exit:
+ g_free(smbios_tables);
+ smbios_tables = NULL;
+ return;
}
static void save_opt(const char **dest, QemuOpts *opts, const char *name)
diff --git a/hw/smbios/smbios_legacy.c b/hw/smbios/smbios_legacy.c
index 21f143e738..a6544bf55a 100644
--- a/hw/smbios/smbios_legacy.c
+++ b/hw/smbios/smbios_legacy.c
@@ -19,7 +19,7 @@
#include "qemu/bswap.h"
#include "hw/firmware/smbios.h"
#include "sysemu/sysemu.h"
-#include "qemu/error-report.h"
+#include "qapi/error.h"
struct smbios_header {
uint16_t length;
@@ -128,7 +128,7 @@ static void smbios_build_type_1_fields(void)
}
}
-uint8_t *smbios_get_table_legacy(size_t *length)
+uint8_t *smbios_get_table_legacy(size_t *length, Error **errp)
{
int i;
size_t usr_offset;
@@ -136,15 +136,15 @@ uint8_t *smbios_get_table_legacy(size_t *length)
/* complain if fields were given for types > 1 */
if (find_next_bit(smbios_have_fields_bitmap,
SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
- error_report("can't process fields for smbios "
+ error_setg(errp, "can't process fields for smbios "
"types > 1 on machine versions < 2.1!");
- exit(1);
+ goto err_exit;
}
if (test_bit(4, smbios_have_binfile_bitmap)) {
- error_report("can't process table for smbios "
- "type 4 on machine versions < 2.1!");
- exit(1);
+ error_setg(errp, "can't process table for smbios "
+ "type 4 on machine versions < 2.1!");
+ goto err_exit;
}
g_free(smbios_entries);
@@ -173,7 +173,13 @@ uint8_t *smbios_get_table_legacy(size_t *length)
smbios_build_type_0_fields();
smbios_build_type_1_fields();
- smbios_validate_table();
+ if (!smbios_validate_table(errp)) {
+ goto err_exit;
+ }
+
*length = smbios_entries_len;
return smbios_entries;
+err_exit:
+ g_free(smbios_entries);
+ return NULL;
}
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index 92e9aba415..44af3a0d82 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -326,7 +326,7 @@ struct smbios_type_127 {
struct smbios_structure_header header;
} QEMU_PACKED;
-void smbios_validate_table(void);
+bool smbios_validate_table(Error **errp);
void smbios_add_usr_blob_size(size_t size);
void smbios_entry_add(QemuOpts *opts, Error **errp);
void smbios_set_cpuid(uint32_t version, uint32_t features);
@@ -336,7 +336,7 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
SmbiosEntryPointType ep_type,
const char *stream_product,
const char *stream_version);
-uint8_t *smbios_get_table_legacy(size_t *length);
+uint8_t *smbios_get_table_legacy(size_t *length, Error **errp);
void smbios_get_tables(MachineState *ms,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
--
2.39.3

@ -1,131 +0,0 @@
From 38220bc61bdb1614f34a53481f7604720c9e9e5a Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Tue, 20 Feb 2024 10:41:06 +0100
Subject: [PATCH 18/20] smbios: in case of entry point is 'auto' try to build
v2 tables 1st
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [16/18] becbcb3d8dad4842e5939bb75e21f4e737a4a325
JIRA: https://issues.redhat.com/browse/RHEL-21705
QEMU for some time now uses SMBIOS 3.0 for PC/Q35 machines by
default, however Windows has a bug in locating SMBIOS 3.0
entrypoint and fails to find tables when booted on SeaBIOS
(on UEFI SMBIOS 3.0 tables work fine since firmware hands
over tables in another way)
Missing SMBIOS tables may lead to some issues for guest
though (worst are: possible reactiveation, inability to
get virtio drivers from 'Windows Update')
It's unclear at this point if MS will fix the issue on their
side. So instead of it (or rather in addition) this patch
will try to workaround the issue.
aka, use smbios-entry-point-type=auto to make QEMU try
generating conservative SMBIOS 2.0 tables and if that
fails (due to limits/requested configuration) fallback
to SMBIOS 3.0 tables.
With this in place majority of users will use SMBIOS 2.0
tables which work fine with (Windows + legacy BIOS).
The configurations that is not to possible to describe
with SMBIOS 2.0 will switch automatically to SMBIOS 3.0
(which will trigger Windows bug but there is nothing
QEMU can do here, so go and aks Microsoft to real fix).
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
---
hw/smbios/smbios.c | 52 +++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 49 insertions(+), 3 deletions(-)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index 4521ea386c..3d9dcb0d31 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -1097,7 +1097,7 @@ static void smbios_entry_point_setup(SmbiosEntryPointType ep_type)
}
}
-void smbios_get_tables(MachineState *ms,
+static bool smbios_get_tables_ep(MachineState *ms,
SmbiosEntryPointType ep_type,
const struct smbios_phys_mem_area *mem_array,
const unsigned int mem_array_size,
@@ -1106,6 +1106,7 @@ void smbios_get_tables(MachineState *ms,
Error **errp)
{
unsigned i, dimm_cnt, offset;
+ ERRP_GUARD();
assert(ep_type == SMBIOS_ENTRY_POINT_TYPE_32 ||
ep_type == SMBIOS_ENTRY_POINT_TYPE_64);
@@ -1192,11 +1193,56 @@ void smbios_get_tables(MachineState *ms,
abort();
}
- return;
+ return true;
err_exit:
g_free(smbios_tables);
smbios_tables = NULL;
- return;
+ return false;
+}
+
+void smbios_get_tables(MachineState *ms,
+ SmbiosEntryPointType ep_type,
+ const struct smbios_phys_mem_area *mem_array,
+ const unsigned int mem_array_size,
+ uint8_t **tables, size_t *tables_len,
+ uint8_t **anchor, size_t *anchor_len,
+ Error **errp)
+{
+ Error *local_err = NULL;
+ bool is_valid;
+ ERRP_GUARD();
+
+ switch (ep_type) {
+ case SMBIOS_ENTRY_POINT_TYPE_AUTO:
+ case SMBIOS_ENTRY_POINT_TYPE_32:
+ is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_32,
+ mem_array, mem_array_size,
+ tables, tables_len,
+ anchor, anchor_len,
+ &local_err);
+ if (is_valid || ep_type != SMBIOS_ENTRY_POINT_TYPE_AUTO) {
+ break;
+ }
+ /*
+ * fall through in case AUTO endpoint is selected and
+ * SMBIOS 2.x tables can't be generated, to try if SMBIOS 3.x
+ * tables would work
+ */
+ case SMBIOS_ENTRY_POINT_TYPE_64:
+ error_free(local_err);
+ local_err = NULL;
+ is_valid = smbios_get_tables_ep(ms, SMBIOS_ENTRY_POINT_TYPE_64,
+ mem_array, mem_array_size,
+ tables, tables_len,
+ anchor, anchor_len,
+ &local_err);
+ break;
+ default:
+ abort();
+ }
+ if (!is_valid) {
+ error_propagate(errp, local_err);
+ }
}
static void save_opt(const char **dest, QemuOpts *opts, const char *name)
--
2.39.3

@ -1,330 +0,0 @@
From 36b0256e27f9d5268c5413891b4a7322819ae9db Mon Sep 17 00:00:00 2001
From: Igor Mammedov <imammedo@redhat.com>
Date: Mon, 4 Mar 2024 15:56:19 +0100
Subject: [PATCH 12/20] smbios: rename/expose structures/bitmaps used by both
legacy and modern code
RH-Author: Igor Mammedov <imammedo@redhat.com>
RH-MergeRequest: 230: Workaround Windows failing to find 64bit SMBIOS entry point with SeaBIOS
RH-Jira: RHEL-21705
RH-Acked-by: MST <mst@redhat.com>
RH-Acked-by: Ani Sinha <None>
RH-Commit: [10/18] 59fe438d5b7f6e584a6bb02597e4d4724fe2cece
JIRA: https://issues.redhat.com/browse/RHEL-21705
As a preparation to move legacy handling into a separate file,
add prefix 'smbios_' to type0/type1/have_binfile_bitmap/have_fields_bitmap
and expose them in smbios.h so that they can be reused in
legacy and modern code.
Doing it as a separate patch to avoid rename cluttering follow-up
patch which will move legacy code into a separate file.
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
Reviewed-by: Ani Sinha <anisinha@redhat.com>
Conflicts: hw/smbios/smbios.c
due to setting downstream type1.family/type1.sku defaults
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
hw/smbios/smbios.c | 117 ++++++++++++++++-------------------
include/hw/firmware/smbios.h | 16 +++++
2 files changed, 71 insertions(+), 62 deletions(-)
diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c
index c48a290478..eb9927335d 100644
--- a/hw/smbios/smbios.c
+++ b/hw/smbios/smbios.c
@@ -81,19 +81,11 @@ static int smbios_type4_count = 0;
static bool smbios_have_defaults;
static uint32_t smbios_cpuid_version, smbios_cpuid_features;
-static DECLARE_BITMAP(have_binfile_bitmap, SMBIOS_MAX_TYPE+1);
-static DECLARE_BITMAP(have_fields_bitmap, SMBIOS_MAX_TYPE+1);
+DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1);
+DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1);
-static struct {
- const char *vendor, *version, *date;
- bool have_major_minor, uefi;
- uint8_t major, minor;
-} type0;
-
-static struct {
- const char *manufacturer, *product, *version, *serial, *sku, *family;
- /* uuid is in qemu_uuid */
-} type1;
+smbios_type0_t smbios_type0;
+smbios_type1_t smbios_type1;
static struct {
const char *manufacturer, *product, *version, *serial, *asset, *location;
@@ -584,36 +576,36 @@ static void smbios_maybe_add_str(int type, int offset, const char *data)
static void smbios_build_type_0_fields(void)
{
smbios_maybe_add_str(0, offsetof(struct smbios_type_0, vendor_str),
- type0.vendor);
+ smbios_type0.vendor);
smbios_maybe_add_str(0, offsetof(struct smbios_type_0, bios_version_str),
- type0.version);
+ smbios_type0.version);
smbios_maybe_add_str(0, offsetof(struct smbios_type_0,
bios_release_date_str),
- type0.date);
- if (type0.have_major_minor) {
+ smbios_type0.date);
+ if (smbios_type0.have_major_minor) {
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_major_release),
- &type0.major, 1);
+ &smbios_type0.major, 1);
smbios_add_field(0, offsetof(struct smbios_type_0,
system_bios_minor_release),
- &type0.minor, 1);
+ &smbios_type0.minor, 1);
}
}
static void smbios_build_type_1_fields(void)
{
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, manufacturer_str),
- type1.manufacturer);
+ smbios_type1.manufacturer);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, product_name_str),
- type1.product);
+ smbios_type1.product);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, version_str),
- type1.version);
+ smbios_type1.version);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, serial_number_str),
- type1.serial);
+ smbios_type1.serial);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, sku_number_str),
- type1.sku);
+ smbios_type1.sku);
smbios_maybe_add_str(1, offsetof(struct smbios_type_1, family_str),
- type1.family);
+ smbios_type1.family);
if (qemu_uuid_set) {
/* We don't encode the UUID in the "wire format" here because this
* function is for legacy mode and needs to keep the guest ABI, and
@@ -631,14 +623,14 @@ uint8_t *smbios_get_table_legacy(size_t *length)
size_t usr_offset;
/* also complain if fields were given for types > 1 */
- if (find_next_bit(have_fields_bitmap,
+ if (find_next_bit(smbios_have_fields_bitmap,
SMBIOS_MAX_TYPE + 1, 2) < SMBIOS_MAX_TYPE + 1) {
error_report("can't process fields for smbios "
"types > 1 on machine versions < 2.1!");
exit(1);
}
- if (test_bit(4, have_binfile_bitmap)) {
+ if (test_bit(4, smbios_have_binfile_bitmap)) {
error_report("can't process table for smbios "
"type 4 on machine versions < 2.1!");
exit(1);
@@ -679,10 +671,10 @@ uint8_t *smbios_get_table_legacy(size_t *length)
bool smbios_skip_table(uint8_t type, bool required_table)
{
- if (test_bit(type, have_binfile_bitmap)) {
+ if (test_bit(type, smbios_have_binfile_bitmap)) {
return true; /* user provided their own binary blob(s) */
}
- if (test_bit(type, have_fields_bitmap)) {
+ if (test_bit(type, smbios_have_fields_bitmap)) {
return false; /* user provided fields via command line */
}
if (smbios_have_defaults && required_table) {
@@ -710,25 +702,25 @@ static void smbios_build_type_0_table(void)
{
SMBIOS_BUILD_TABLE_PRE(0, T0_BASE, false); /* optional, leave up to BIOS */
- SMBIOS_TABLE_SET_STR(0, vendor_str, type0.vendor);
- SMBIOS_TABLE_SET_STR(0, bios_version_str, type0.version);
+ SMBIOS_TABLE_SET_STR(0, vendor_str, smbios_type0.vendor);
+ SMBIOS_TABLE_SET_STR(0, bios_version_str, smbios_type0.version);
t->bios_starting_address_segment = cpu_to_le16(0xE800); /* from SeaBIOS */
- SMBIOS_TABLE_SET_STR(0, bios_release_date_str, type0.date);
+ SMBIOS_TABLE_SET_STR(0, bios_release_date_str, smbios_type0.date);
t->bios_rom_size = 0; /* hardcoded in SeaBIOS with FIXME comment */
t->bios_characteristics = cpu_to_le64(0x08); /* Not supported */
t->bios_characteristics_extension_bytes[0] = 0;
t->bios_characteristics_extension_bytes[1] = 0x14; /* TCD/SVVP | VM */
- if (type0.uefi) {
+ if (smbios_type0.uefi) {
t->bios_characteristics_extension_bytes[1] |= 0x08; /* |= UEFI */
}
- if (type0.have_major_minor) {
- t->system_bios_major_release = type0.major;
- t->system_bios_minor_release = type0.minor;
+ if (smbios_type0.have_major_minor) {
+ t->system_bios_major_release = smbios_type0.major;
+ t->system_bios_minor_release = smbios_type0.minor;
} else {
t->system_bios_major_release = 0;
t->system_bios_minor_release = 0;
@@ -758,18 +750,18 @@ static void smbios_build_type_1_table(void)
{
SMBIOS_BUILD_TABLE_PRE(1, T1_BASE, true); /* required */
- SMBIOS_TABLE_SET_STR(1, manufacturer_str, type1.manufacturer);
- SMBIOS_TABLE_SET_STR(1, product_name_str, type1.product);
- SMBIOS_TABLE_SET_STR(1, version_str, type1.version);
- SMBIOS_TABLE_SET_STR(1, serial_number_str, type1.serial);
+ SMBIOS_TABLE_SET_STR(1, manufacturer_str, smbios_type1.manufacturer);
+ SMBIOS_TABLE_SET_STR(1, product_name_str, smbios_type1.product);
+ SMBIOS_TABLE_SET_STR(1, version_str, smbios_type1.version);
+ SMBIOS_TABLE_SET_STR(1, serial_number_str, smbios_type1.serial);
if (qemu_uuid_set) {
smbios_encode_uuid(&t->uuid, &qemu_uuid);
} else {
memset(&t->uuid, 0, 16);
}
t->wake_up_type = 0x06; /* power switch */
- SMBIOS_TABLE_SET_STR(1, sku_number_str, type1.sku);
- SMBIOS_TABLE_SET_STR(1, family_str, type1.family);
+ SMBIOS_TABLE_SET_STR(1, sku_number_str, smbios_type1.sku);
+ SMBIOS_TABLE_SET_STR(1, family_str, smbios_type1.family);
SMBIOS_BUILD_TABLE_POST;
}
@@ -1184,12 +1176,12 @@ void smbios_set_defaults(const char *manufacturer, const char *product,
*
* We get 'System Manufacturer' and 'Baseboard Manufacturer'
*/
- SMBIOS_SET_DEFAULT(type1.manufacturer, manufacturer);
- SMBIOS_SET_DEFAULT(type1.product, product);
- SMBIOS_SET_DEFAULT(type1.version, version);
- SMBIOS_SET_DEFAULT(type1.family, "Red Hat Enterprise Linux");
+ SMBIOS_SET_DEFAULT(smbios_type1.manufacturer, manufacturer);
+ SMBIOS_SET_DEFAULT(smbios_type1.product, product);
+ SMBIOS_SET_DEFAULT(smbios_type1.version, version);
+ SMBIOS_SET_DEFAULT(smbios_type1.family, "Red Hat Enterprise Linux");
if (stream_version != NULL) {
- SMBIOS_SET_DEFAULT(type1.sku, stream_version);
+ SMBIOS_SET_DEFAULT(smbios_type1.sku, stream_version);
}
SMBIOS_SET_DEFAULT(type2.manufacturer, manufacturer);
if (stream_product != NULL) {
@@ -1468,13 +1460,13 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
}
if (header->type <= SMBIOS_MAX_TYPE) {
- if (test_bit(header->type, have_fields_bitmap)) {
+ if (test_bit(header->type, smbios_have_fields_bitmap)) {
error_setg(errp,
"can't load type %d struct, fields already specified!",
header->type);
return;
}
- set_bit(header->type, have_binfile_bitmap);
+ set_bit(header->type, smbios_have_binfile_bitmap);
}
if (header->type == 4) {
@@ -1505,41 +1497,42 @@ void smbios_entry_add(QemuOpts *opts, Error **errp)
return;
}
- if (test_bit(type, have_binfile_bitmap)) {
+ if (test_bit(type, smbios_have_binfile_bitmap)) {
error_setg(errp, "can't add fields, binary file already loaded!");
return;
}
- set_bit(type, have_fields_bitmap);
+ set_bit(type, smbios_have_fields_bitmap);
switch (type) {
case 0:
if (!qemu_opts_validate(opts, qemu_smbios_type0_opts, errp)) {
return;
}
- save_opt(&type0.vendor, opts, "vendor");
- save_opt(&type0.version, opts, "version");
- save_opt(&type0.date, opts, "date");
- type0.uefi = qemu_opt_get_bool(opts, "uefi", false);
+ save_opt(&smbios_type0.vendor, opts, "vendor");
+ save_opt(&smbios_type0.version, opts, "version");
+ save_opt(&smbios_type0.date, opts, "date");
+ smbios_type0.uefi = qemu_opt_get_bool(opts, "uefi", false);
val = qemu_opt_get(opts, "release");
if (val) {
- if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) {
+ if (sscanf(val, "%hhu.%hhu", &smbios_type0.major,
+ &smbios_type0.minor) != 2) {
error_setg(errp, "Invalid release");
return;
}
- type0.have_major_minor = true;
+ smbios_type0.have_major_minor = true;
}
return;
case 1:
if (!qemu_opts_validate(opts, qemu_smbios_type1_opts, errp)) {
return;
}
- save_opt(&type1.manufacturer, opts, "manufacturer");
- save_opt(&type1.product, opts, "product");
- save_opt(&type1.version, opts, "version");
- save_opt(&type1.serial, opts, "serial");
- save_opt(&type1.sku, opts, "sku");
- save_opt(&type1.family, opts, "family");
+ save_opt(&smbios_type1.manufacturer, opts, "manufacturer");
+ save_opt(&smbios_type1.product, opts, "product");
+ save_opt(&smbios_type1.version, opts, "version");
+ save_opt(&smbios_type1.serial, opts, "serial");
+ save_opt(&smbios_type1.sku, opts, "sku");
+ save_opt(&smbios_type1.family, opts, "family");
val = qemu_opt_get(opts, "uuid");
if (val) {
diff --git a/include/hw/firmware/smbios.h b/include/hw/firmware/smbios.h
index d55018e5e3..333de0d5fc 100644
--- a/include/hw/firmware/smbios.h
+++ b/include/hw/firmware/smbios.h
@@ -2,6 +2,7 @@
#define QEMU_SMBIOS_H
#include "qapi/qapi-types-machine.h"
+#include "qemu/bitmap.h"
/*
* SMBIOS Support
@@ -16,8 +17,23 @@
*
*/
+typedef struct {
+ const char *vendor, *version, *date;
+ bool have_major_minor, uefi;
+ uint8_t major, minor;
+} smbios_type0_t;
+extern smbios_type0_t smbios_type0;
+
+typedef struct {
+ const char *manufacturer, *product, *version, *serial, *sku, *family;
+ /* uuid is in qemu_uuid */
+} smbios_type1_t;
+extern smbios_type1_t smbios_type1;
#define SMBIOS_MAX_TYPE 127
+extern DECLARE_BITMAP(smbios_have_binfile_bitmap, SMBIOS_MAX_TYPE + 1);
+extern DECLARE_BITMAP(smbios_have_fields_bitmap, SMBIOS_MAX_TYPE + 1);
+
#define offsetofend(TYPE, MEMBER) \
(offsetof(TYPE, MEMBER) + sizeof_field(TYPE, MEMBER))
--
2.39.3

@ -1,81 +0,0 @@
From 6e4f68e9ba3fe75ca6f200f189f96bb402f0ee8e Mon Sep 17 00:00:00 2001
From: Fiona Ebner <f.ebner@proxmox.com>
Date: Wed, 24 Jan 2024 11:57:49 +0100
Subject: [PATCH 02/20] ui/clipboard: add asserts for update and request
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 228: ui/clipboard: mark type as not available when there is no data
RH-Jira: RHEL-19629
RH-Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-Acked-by: Gerd Hoffmann <None>
RH-Commit: [2/2] 176b4b835fd8aa226f2fa93fd334b9384080cf21 (jmaloy/jmaloy-qemu-kvm-2)
JIRA: https://issues.redhat.com/browse/RHEL-19629
CVE: CVE-2023-6683
Upstream: Merged
ui/clipboard: add asserts for update and request
commit 9c416582611b7495bdddb4c5456c7acb64b78938
Author: Fiona Ebner <f.ebner@proxmox.com>
Date: Wed Jan 24 11:57:49 2024 +0100
ui/clipboard: add asserts for update and request
Should an issue like CVE-2023-6683 ever appear again in the future,
it will be more obvious which assumption was violated.
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Message-ID: <20240124105749.204610-2-f.ebner@proxmox.com>
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
ui/clipboard.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/ui/clipboard.c b/ui/clipboard.c
index b3f6fa3c9e..4264884a6c 100644
--- a/ui/clipboard.c
+++ b/ui/clipboard.c
@@ -65,12 +65,24 @@ bool qemu_clipboard_check_serial(QemuClipboardInfo *info, bool client)
void qemu_clipboard_update(QemuClipboardInfo *info)
{
+ uint32_t type;
QemuClipboardNotify notify = {
.type = QEMU_CLIPBOARD_UPDATE_INFO,
.info = info,
};
assert(info->selection < QEMU_CLIPBOARD_SELECTION__COUNT);
+ for (type = 0; type < QEMU_CLIPBOARD_TYPE__COUNT; type++) {
+ /*
+ * If data is missing, the clipboard owner's 'request' callback needs to
+ * be set. Otherwise, there is no way to get the clipboard data and
+ * qemu_clipboard_request() cannot be called.
+ */
+ if (info->types[type].available && !info->types[type].data) {
+ assert(info->owner && info->owner->request);
+ }
+ }
+
notifier_list_notify(&clipboard_notifiers, &notify);
if (cbinfo[info->selection] != info) {
@@ -132,6 +144,8 @@ void qemu_clipboard_request(QemuClipboardInfo *info,
!info->owner)
return;
+ assert(info->owner->request);
+
info->types[type].requested = true;
info->owner->request(info, type);
}
--
2.39.3

@ -1,107 +0,0 @@
From 097516bef2993d917e76d92066ca2eb067e45394 Mon Sep 17 00:00:00 2001
From: Fiona Ebner <f.ebner@proxmox.com>
Date: Wed, 24 Jan 2024 11:57:48 +0100
Subject: [PATCH 01/20] ui/clipboard: mark type as not available when there is
no data
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Jon Maloy <jmaloy@redhat.com>
RH-MergeRequest: 228: ui/clipboard: mark type as not available when there is no data
RH-Jira: RHEL-19629
RH-Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-Acked-by: Gerd Hoffmann <None>
RH-Commit: [1/2] 74ded03d6376f9693733d673502219f76eab7099 (jmaloy/jmaloy-qemu-kvm-2)
JIRA: https://issues.redhat.com/browse/RHEL-19629
CVE: CVE-2023-6683
Upstream: Merged
commit 405484b29f6548c7b86549b0f961b906337aa68a
Author: Fiona Ebner <f.ebner@proxmox.com>
Date: Wed Jan 24 11:57:48 2024 +0100
ui/clipboard: mark type as not available when there is no data
With VNC, a client can send a non-extended VNC_MSG_CLIENT_CUT_TEXT
message with len=0. In qemu_clipboard_set_data(), the clipboard info
will be updated setting data to NULL (because g_memdup(data, size)
returns NULL when size is 0). If the client does not set the
VNC_ENCODING_CLIPBOARD_EXT feature when setting up the encodings, then
the 'request' callback for the clipboard peer is not initialized.
Later, because data is NULL, qemu_clipboard_request() can be reached
via vdagent_chr_write() and vdagent_clipboard_recv_request() and
there, the clipboard owner's 'request' callback will be attempted to
be called, but that is a NULL pointer.
In particular, this can happen when using the KRDC (22.12.3) VNC
client.
Another scenario leading to the same issue is with two clients (say
noVNC and KRDC):
The noVNC client sets the extension VNC_FEATURE_CLIPBOARD_EXT and
initializes its cbpeer.
The KRDC client does not, but triggers a vnc_client_cut_text() (note
it's not the _ext variant)). There, a new clipboard info with it as
the 'owner' is created and via qemu_clipboard_set_data() is called,
which in turn calls qemu_clipboard_update() with that info.
In qemu_clipboard_update(), the notifier for the noVNC client will be
called, i.e. vnc_clipboard_notify() and also set vs->cbinfo for the
noVNC client. The 'owner' in that clipboard info is the clipboard peer
for the KRDC client, which did not initialize the 'request' function.
That sounds correct to me, it is the owner of that clipboard info.
Then when noVNC sends a VNC_MSG_CLIENT_CUT_TEXT message (it did set
the VNC_FEATURE_CLIPBOARD_EXT feature correctly, so a check for it
passes), that clipboard info is passed to qemu_clipboard_request() and
the original segfault still happens.
Fix the issue by handling updates with size 0 differently. In
particular, mark in the clipboard info that the type is not available.
While at it, switch to g_memdup2(), because g_memdup() is deprecated.
Cc: qemu-stable@nongnu.org
Fixes: CVE-2023-6683
Reported-by: Markus Frank <m.frank@proxmox.com>
Suggested-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Tested-by: Markus Frank <m.frank@proxmox.com>
Message-ID: <20240124105749.204610-1-f.ebner@proxmox.com>
Signed-off-by: Jon Maloy <jmaloy@redhat.com>
---
ui/clipboard.c | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/ui/clipboard.c b/ui/clipboard.c
index 3d14bffaf8..b3f6fa3c9e 100644
--- a/ui/clipboard.c
+++ b/ui/clipboard.c
@@ -163,9 +163,15 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer,
}
g_free(info->types[type].data);
- info->types[type].data = g_memdup(data, size);
- info->types[type].size = size;
- info->types[type].available = true;
+ if (size) {
+ info->types[type].data = g_memdup2(data, size);
+ info->types[type].size = size;
+ info->types[type].available = true;
+ } else {
+ info->types[type].data = NULL;
+ info->types[type].size = 0;
+ info->types[type].available = false;
+ }
if (update) {
qemu_clipboard_update(info);
--
2.39.3

@ -1,196 +0,0 @@
From 523423436e83b01a83c60bc44bd08471992aaff4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
Date: Mon, 15 Jan 2024 16:34:33 +0400
Subject: [PATCH 1/4] virtio-gpu: fix scanout migration post-load
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load
RH-Jira: RHEL-36181
RH-Acked-by: Peter Xu <peterx@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [1/3] 05e9dbf7f602d62ec2f336a855daf57e6ee8f3f3
The current post-loading code for scanout has a FIXME: it doesn't take
the resource region/rect into account. But there is more, when adding
blob migration support in commit f66767f75c9, I didn't realize that blob
resources could be used for scanouts. This situationn leads to a crash
during post-load, as they don't have an associated res->image.
virtio_gpu_do_set_scanout() handle all cases, but requires the
associated virtio_gpu_framebuffer, which is currently not saved during
migration.
Add a v2 of "virtio-gpu-one-scanout" with the framebuffer fields, so we
can restore blob scanouts, as well as fixing the existing FIXME.
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Sebastian Ott <sebott@redhat.com>
(cherry picked from commit dfcf74fa68c88233209aafc5f82728d0b9a1af5c)
---
hw/display/virtio-gpu.c | 55 +++++++++++++++++++++++++++-------
include/hw/virtio/virtio-gpu.h | 1 +
2 files changed, 46 insertions(+), 10 deletions(-)
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index 1702190ead..bc485d60ea 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -598,6 +598,7 @@ static void virtio_unref_resource(pixman_image_t *image, void *data)
static void virtio_gpu_update_scanout(VirtIOGPU *g,
uint32_t scanout_id,
struct virtio_gpu_simple_resource *res,
+ struct virtio_gpu_framebuffer *fb,
struct virtio_gpu_rect *r)
{
struct virtio_gpu_simple_resource *ores;
@@ -615,9 +616,10 @@ static void virtio_gpu_update_scanout(VirtIOGPU *g,
scanout->y = r->y;
scanout->width = r->width;
scanout->height = r->height;
+ scanout->fb = *fb;
}
-static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
+static bool virtio_gpu_do_set_scanout(VirtIOGPU *g,
uint32_t scanout_id,
struct virtio_gpu_framebuffer *fb,
struct virtio_gpu_simple_resource *res,
@@ -643,7 +645,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
r->x, r->y, r->width, r->height,
fb->width, fb->height);
*error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
- return;
+ return false;
}
g->parent_obj.enable = 1;
@@ -651,11 +653,12 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
if (res->blob) {
if (console_has_gl(scanout->con)) {
if (!virtio_gpu_update_dmabuf(g, scanout_id, res, fb, r)) {
- virtio_gpu_update_scanout(g, scanout_id, res, r);
+ virtio_gpu_update_scanout(g, scanout_id, res, fb, r);
} else {
*error = VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY;
+ return false;
}
- return;
+ return true;
}
data = res->blob;
@@ -684,7 +687,7 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
scanout->ds = qemu_create_displaysurface_pixman(rect);
if (!scanout->ds) {
*error = VIRTIO_GPU_RESP_ERR_UNSPEC;
- return;
+ return false;
}
#ifdef WIN32
qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, fb->offset);
@@ -695,7 +698,8 @@ static void virtio_gpu_do_set_scanout(VirtIOGPU *g,
scanout->ds);
}
- virtio_gpu_update_scanout(g, scanout_id, res, r);
+ virtio_gpu_update_scanout(g, scanout_id, res, fb, r);
+ return true;
}
static void virtio_gpu_set_scanout(VirtIOGPU *g,
@@ -1166,8 +1170,9 @@ static void virtio_gpu_cursor_bh(void *opaque)
static const VMStateDescription vmstate_virtio_gpu_scanout = {
.name = "virtio-gpu-one-scanout",
- .version_id = 1,
- .fields = (VMStateField[]) {
+ .version_id = 2,
+ .minimum_version_id = 1,
+ .fields = (const VMStateField[]) {
VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout),
VMSTATE_UINT32(width, struct virtio_gpu_scanout),
VMSTATE_UINT32(height, struct virtio_gpu_scanout),
@@ -1178,6 +1183,12 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = {
VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout),
VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout),
VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout),
+ VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2),
VMSTATE_END_OF_LIST()
},
};
@@ -1349,6 +1360,7 @@ static int virtio_gpu_blob_save(QEMUFile *f, void *opaque, size_t size,
if (!res->blob_size) {
continue;
}
+ assert(!res->image);
qemu_put_be32(f, res->resource_id);
qemu_put_be32(f, res->blob_size);
qemu_put_be32(f, res->iov_cnt);
@@ -1411,11 +1423,11 @@ static int virtio_gpu_post_load(void *opaque, int version_id)
int i;
for (i = 0; i < g->parent_obj.conf.max_outputs; i++) {
- /* FIXME: should take scanout.r.{x,y} into account */
scanout = &g->parent_obj.scanout[i];
if (!scanout->resource_id) {
continue;
}
+
res = virtio_gpu_find_resource(g, scanout->resource_id);
if (!res) {
return -EINVAL;
@@ -1428,7 +1440,30 @@ static int virtio_gpu_post_load(void *opaque, int version_id)
qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
#endif
- dpy_gfx_replace_surface(scanout->con, scanout->ds);
+ if (scanout->fb.format != 0) {
+ uint32_t error = 0;
+ struct virtio_gpu_rect r = {
+ .x = scanout->x,
+ .y = scanout->y,
+ .width = scanout->width,
+ .height = scanout->height
+ };
+
+ if (!virtio_gpu_do_set_scanout(g, i, &scanout->fb, res, &r, &error)) {
+ return -EINVAL;
+ }
+ } else {
+ /* legacy v1 migration support */
+ if (!res->image) {
+ return -EINVAL;
+ }
+ scanout->ds = qemu_create_displaysurface_pixman(res->image);
+#ifdef WIN32
+ qemu_displaysurface_win32_set_handle(scanout->ds, res->handle, 0);
+#endif
+ dpy_gfx_replace_surface(scanout->con, scanout->ds);
+ }
+
dpy_gfx_update_full(scanout->con);
if (scanout->cursor.resource_id) {
update_cursor(g, &scanout->cursor);
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 584ba2ed73..9a13afb34e 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -81,6 +81,7 @@ struct virtio_gpu_scanout {
uint32_t resource_id;
struct virtio_gpu_update_cursor cursor;
QEMUCursor *current_cursor;
+ struct virtio_gpu_framebuffer fb;
};
struct virtio_gpu_requested_state {
--
2.39.3

@ -1,122 +0,0 @@
From 7741a7d1c1d719ff681f73a023f27d5951b98882 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= <marcandre.lureau@redhat.com>
Date: Thu, 16 May 2024 12:40:22 +0400
Subject: [PATCH 2/4] virtio-gpu: fix v2 migration
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Marc-André Lureau <marcandre.lureau@redhat.com>
RH-MergeRequest: 377: virtio-gpu: fix scanout migration post-load
RH-Jira: RHEL-36181
RH-Acked-by: Peter Xu <peterx@redhat.com>
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
RH-Commit: [2/3] 482c37c10ac7e5e8a6c36ac2eb806ac02beeefdc
Commit dfcf74fa ("virtio-gpu: fix scanout migration post-load") broke
forward/backward version migration. Versioning of nested VMSD structures
is not straightforward, as the wire format doesn't have nested
structures versions. Introduce x-scanout-vmstate-version and a field
test to save/load appropriately according to the machine version.
Fixes: dfcf74fa ("virtio-gpu: fix scanout migration post-load")
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Peter Xu <peterx@redhat.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
[fixed long lines]
Signed-off-by: Fabiano Rosas <farosas@suse.de>
[ Move the hw-compat from 8.2 to 8.1 ]
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
(cherry picked from commit 40a23ef643664b5c1021a9789f9d680b6294fb50)
---
hw/core/machine.c | 1 +
hw/display/virtio-gpu.c | 30 ++++++++++++++++++++++--------
include/hw/virtio/virtio-gpu.h | 1 +
3 files changed, 24 insertions(+), 8 deletions(-)
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 309f6ba685..a0843ab93e 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -37,6 +37,7 @@ GlobalProperty hw_compat_8_1[] = {
{ "ramfb", "x-migrate", "off" },
{ "vfio-pci-nohotplug", "x-ramfb-migrate", "off" },
{ "igb", "x-pcie-flr-init", "off" },
+ { "virtio-gpu-device", "x-scanout-vmstate-version", "1" },
};
const size_t hw_compat_8_1_len = G_N_ELEMENTS(hw_compat_8_1);
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index bc485d60ea..9d35a016ae 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -1168,10 +1168,17 @@ static void virtio_gpu_cursor_bh(void *opaque)
virtio_gpu_handle_cursor(&g->parent_obj.parent_obj, g->cursor_vq);
}
+static bool scanout_vmstate_after_v2(void *opaque, int version)
+{
+ struct VirtIOGPUBase *base = container_of(opaque, VirtIOGPUBase, scanout);
+ struct VirtIOGPU *gpu = container_of(base, VirtIOGPU, parent_obj);
+
+ return gpu->scanout_vmstate_version >= 2;
+}
+
static const VMStateDescription vmstate_virtio_gpu_scanout = {
.name = "virtio-gpu-one-scanout",
- .version_id = 2,
- .minimum_version_id = 1,
+ .version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32(resource_id, struct virtio_gpu_scanout),
VMSTATE_UINT32(width, struct virtio_gpu_scanout),
@@ -1183,12 +1190,18 @@ static const VMStateDescription vmstate_virtio_gpu_scanout = {
VMSTATE_UINT32(cursor.hot_y, struct virtio_gpu_scanout),
VMSTATE_UINT32(cursor.pos.x, struct virtio_gpu_scanout),
VMSTATE_UINT32(cursor.pos.y, struct virtio_gpu_scanout),
- VMSTATE_UINT32_V(fb.format, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.bytes_pp, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.width, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.height, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.stride, struct virtio_gpu_scanout, 2),
- VMSTATE_UINT32_V(fb.offset, struct virtio_gpu_scanout, 2),
+ VMSTATE_UINT32_TEST(fb.format, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.bytes_pp, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.width, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.height, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.stride, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
+ VMSTATE_UINT32_TEST(fb.offset, struct virtio_gpu_scanout,
+ scanout_vmstate_after_v2),
VMSTATE_END_OF_LIST()
},
};
@@ -1668,6 +1681,7 @@ static Property virtio_gpu_properties[] = {
DEFINE_PROP_BIT("blob", VirtIOGPU, parent_obj.conf.flags,
VIRTIO_GPU_FLAG_BLOB_ENABLED, false),
DEFINE_PROP_SIZE("hostmem", VirtIOGPU, parent_obj.conf.hostmem, 0),
+ DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 9a13afb34e..18500432c6 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -177,6 +177,7 @@ typedef struct VGPUDMABuf {
struct VirtIOGPU {
VirtIOGPUBase parent_obj;
+ uint8_t scanout_vmstate_version;
uint64_t conf_max_hostmem;
VirtQueue *ctrl_vq;
--
2.39.3

@ -1,162 +0,0 @@
From ccd8ffa5cd7f9bcfddeda7a9fa1ad86d4bad870e Mon Sep 17 00:00:00 2001
From: Cindy Lu <lulu@redhat.com>
Date: Fri, 12 Apr 2024 14:26:55 +0800
Subject: [PATCH] virtio-pci: fix use of a released vector
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
RH-Author: Cindy Lu <lulu@redhat.com>
RH-MergeRequest: 366: virtio-pci: fix use of a released vector
RH-Jira: RHEL-32837
RH-Acked-by: Laurent Vivier <lvivier@redhat.com>
RH-Acked-by: Jason Wang <jasowang@redhat.com>
RH-Commit: [1/1] edd26ffb8727635310aed42e42925afe87df2287
During the booting process of the non-standard image, the behavior of the
called function in qemu is as follows:
1. vhost_net_stop() was triggered by guest image. This will call the function
virtio_pci_set_guest_notifiers() with assgin= false,
virtio_pci_set_guest_notifiers( will release the irqfd for vector 0
2. virtio_reset() was triggered, this will set configure vector to VIRTIO_NO_VECTOR
3.vhost_net_start() was called (at this time, the configure vector is
still VIRTIO_NO_VECTOR) and then call virtio_pci_set_guest_notifiers() with
assgin=true, so the irqfd for vector 0 is still not "init" during this process
4. The system continues to boot and sets the vector back to 0. After that
msix_fire_vector_notifier() was triggered to unmask the vector 0 and meet the crash
To fix the issue, we need to support changing the vector after VIRTIO_CONFIG_S_DRIVER_OK is set.
(gdb) bt
0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0)
at pthread_kill.c:44
1 0x00007fc87148ec53 in __pthread_kill_internal (signo=6, threadid=<optimized out>) at pthread_kill.c:78
2 0x00007fc87143e956 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
3 0x00007fc8714287f4 in __GI_abort () at abort.c:79
4 0x00007fc87142871b in __assert_fail_base
(fmt=0x7fc8715bbde0 "%s%s%s:%u: %s%sAssertion `%s' failed.\n%n", assertion=0x5606413efd53 "ret == 0", file=0x5606413ef87d "../accel/kvm/kvm-all.c", line=1837, function=<optimized out>) at assert.c:92
5 0x00007fc871437536 in __GI___assert_fail
(assertion=0x5606413efd53 "ret == 0", file=0x5606413ef87d "../accel/kvm/kvm-all.c", line=1837, function=0x5606413f06f0 <__PRETTY_FUNCTION__.19> "kvm_irqchip_commit_routes") at assert.c:101
6 0x0000560640f884b5 in kvm_irqchip_commit_routes (s=0x560642cae1f0) at ../accel/kvm/kvm-all.c:1837
7 0x0000560640c98f8e in virtio_pci_one_vector_unmask
(proxy=0x560643c65f00, queue_no=4294967295, vector=0, msg=..., n=0x560643c6e4c8)
at ../hw/virtio/virtio-pci.c:1005
8 0x0000560640c99201 in virtio_pci_vector_unmask (dev=0x560643c65f00, vector=0, msg=...)
at ../hw/virtio/virtio-pci.c:1070
9 0x0000560640bc402e in msix_fire_vector_notifier (dev=0x560643c65f00, vector=0, is_masked=false)
at ../hw/pci/msix.c:120
10 0x0000560640bc40f1 in msix_handle_mask_update (dev=0x560643c65f00, vector=0, was_masked=true)
at ../hw/pci/msix.c:140
11 0x0000560640bc4503 in msix_table_mmio_write (opaque=0x560643c65f00, addr=12, val=0, size=4)
at ../hw/pci/msix.c:231
12 0x0000560640f26d83 in memory_region_write_accessor
(mr=0x560643c66540, addr=12, value=0x7fc86b7bc628, size=4, shift=0, mask=4294967295, attrs=...)
at ../system/memory.c:497
13 0x0000560640f270a6 in access_with_adjusted_size
(addr=12, value=0x7fc86b7bc628, size=4, access_size_min=1, access_size_max=4, access_fn=0x560640f26c8d <memory_region_write_accessor>, mr=0x560643c66540, attrs=...) at ../system/memory.c:573
14 0x0000560640f2a2b5 in memory_region_dispatch_write (mr=0x560643c66540, addr=12, data=0, op=MO_32, attrs=...)
at ../system/memory.c:1521
15 0x0000560640f37bac in flatview_write_continue
(fv=0x7fc65805e0b0, addr=4273803276, attrs=..., ptr=0x7fc871e9c028, len=4, addr1=12, l=4, mr=0x560643c66540)
at ../system/physmem.c:2714
16 0x0000560640f37d0f in flatview_write
(fv=0x7fc65805e0b0, addr=4273803276, attrs=..., buf=0x7fc871e9c028, len=4) at ../system/physmem.c:2756
17 0x0000560640f380bf in address_space_write
(as=0x560642161ae0 <address_space_memory>, addr=4273803276, attrs=..., buf=0x7fc871e9c028, len=4)
at ../system/physmem.c:2863
18 0x0000560640f3812c in address_space_rw
(as=0x560642161ae0 <address_space_memory>, addr=4273803276, attrs=..., buf=0x7fc871e9c028, len=4, is_write=true) at ../system/physmem.c:2873
--Type <RET> for more, q to quit, c to continue without paging--
19 0x0000560640f8aa55 in kvm_cpu_exec (cpu=0x560642f205e0) at ../accel/kvm/kvm-all.c:2915
20 0x0000560640f8d731 in kvm_vcpu_thread_fn (arg=0x560642f205e0) at ../accel/kvm/kvm-accel-ops.c:51
21 0x00005606411949f4 in qemu_thread_start (args=0x560642f292b0) at ../util/qemu-thread-posix.c:541
22 0x00007fc87148cdcd in start_thread (arg=<optimized out>) at pthread_create.c:442
23 0x00007fc871512630 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb)
MST: coding style and typo fixups
Fixes: f9a09ca3ea ("vhost: add support for configure interrupt")
Cc: qemu-stable@nongnu.org
Signed-off-by: Cindy Lu <lulu@redhat.com>
Message-ID: <2321ade5f601367efe7380c04e3f61379c59b48f.1713173550.git.mst@redhat.com>
Cc: Lei Yang <leiyang@redhat.com>
Cc: Jason Wang <jasowang@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Tested-by: Cindy Lu <lulu@redhat.com>
(cherry picked from commit 2ce6cff94df2650c460f809e5ad263f1d22507c0)
Signed-off-by: Cindy Lu <lulu@redhat.com>
---
hw/virtio/virtio-pci.c | 37 +++++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index e433879542..08faefe29a 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -1424,6 +1424,38 @@ static int virtio_pci_add_mem_cap(VirtIOPCIProxy *proxy,
return offset;
}
+static void virtio_pci_set_vector(VirtIODevice *vdev,
+ VirtIOPCIProxy *proxy,
+ int queue_no, uint16_t old_vector,
+ uint16_t new_vector)
+{
+ bool kvm_irqfd = (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+ msix_enabled(&proxy->pci_dev) && kvm_msi_via_irqfd_enabled();
+
+ if (new_vector == old_vector) {
+ return;
+ }
+
+ /*
+ * If the device uses irqfd and the vector changes after DRIVER_OK is
+ * set, we need to release the old vector and set up the new one.
+ * Otherwise just need to set the new vector on the device.
+ */
+ if (kvm_irqfd && old_vector != VIRTIO_NO_VECTOR) {
+ kvm_virtio_pci_vector_release_one(proxy, queue_no);
+ }
+ /* Set the new vector on the device. */
+ if (queue_no == VIRTIO_CONFIG_IRQ_IDX) {
+ vdev->config_vector = new_vector;
+ } else {
+ virtio_queue_set_vector(vdev, queue_no, new_vector);
+ }
+ /* If the new vector changed need to set it up. */
+ if (kvm_irqfd && new_vector != VIRTIO_NO_VECTOR) {
+ kvm_virtio_pci_vector_use_one(proxy, queue_no);
+ }
+}
+
int virtio_pci_add_shm_cap(VirtIOPCIProxy *proxy,
uint8_t bar, uint64_t offset, uint64_t length,
uint8_t id)
@@ -1570,7 +1602,8 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
} else {
val = VIRTIO_NO_VECTOR;
}
- vdev->config_vector = val;
+ virtio_pci_set_vector(vdev, proxy, VIRTIO_CONFIG_IRQ_IDX,
+ vdev->config_vector, val);
break;
case VIRTIO_PCI_COMMON_STATUS:
if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) {
@@ -1610,7 +1643,7 @@ static void virtio_pci_common_write(void *opaque, hwaddr addr,
} else {
val = VIRTIO_NO_VECTOR;
}
- virtio_queue_set_vector(vdev, vdev->queue_sel, val);
+ virtio_pci_set_vector(vdev, proxy, vdev->queue_sel, vector, val);
break;
case VIRTIO_PCI_COMMON_Q_ENABLE:
if (val == 1) {
--
2.39.3

@ -101,7 +101,7 @@
%global target_list %{kvm_target}-softmmu
%global block_drivers_rw_list qcow2,raw,file,host_device,nbd,iscsi,rbd,blkdebug,luks,null-co,nvme,copy-on-read,throttle,compress,virtio-blk-vhost-vdpa,virtio-blk-vfio-pci,virtio-blk-vhost-user,io_uring,nvme-io_uring
%global block_drivers_ro_list vdi,vmdk,vhdx,vpc,https
%global block_drivers_ro_list vdi,vmdk,vhdx,vpc,https,host_cdrom
%define qemudocdir %{_docdir}/%{name}
%global firmwaredirs "%{_datadir}/qemu-firmware:%{_datadir}/ipxe/qemu:%{_datadir}/seavgabios:%{_datadir}/seabios"
@ -149,7 +149,7 @@ Obsoletes: %{name}-block-ssh <= %{epoch}:%{version} \
Summary: QEMU is a machine emulator and virtualizer
Name: qemu-kvm
Version: 8.2.0
Release: 11%{?rcrel}%{?dist}%{?cc_suffix}.6
Release: 6%{?rcrel}%{?dist}%{?cc_suffix}.inferit
# Epoch because we pushed a qemu-1.0 package. AIUI this can't ever be dropped
# Epoch 15 used for RHEL 8
# Epoch 17 used for RHEL 9 (due to release versioning offset in RHEL 8.5)
@ -548,106 +548,6 @@ Patch144: kvm-virtio-blk-avoid-using-ioeventfd-state-in-irqfd-cond.patch
Patch145: kvm-hw-arm-virt-deprecate-virt-rhel9.-0-2-.0-machine-typ.patch
# For RHEL-17068 - Check/fix machine type compatibility for qemu-kvm 8.2.0 [x86_64]
Patch146: kvm-x86-rhel-9.2.0-machine-type-compat-fix.patch
# For RHEL-26049 - When max vcpu is greater than or equal to 246, qemu unable to init event notifier
Patch147: kvm-qemu_init-increase-NOFILE-soft-limit-on-POSIX.patch
# For RHEL-24614 - [RHEL9][chardev][s390x] qemu hit core dump while using TLS server from host to guest
Patch148: kvm-chardev-char-socket-Fix-TLS-io-channels-sending-too-.patch
# For RHEL-19629 - CVE-2023-6683 qemu-kvm: QEMU: VNC: NULL pointer dereference in qemu_clipboard_request() [rhel-9]
Patch149: kvm-ui-clipboard-mark-type-as-not-available-when-there-i.patch
# For RHEL-19629 - CVE-2023-6683 qemu-kvm: QEMU: VNC: NULL pointer dereference in qemu_clipboard_request() [rhel-9]
Patch150: kvm-ui-clipboard-add-asserts-for-update-and-request.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch151: kvm-hw-i386-pc-Defer-smbios_set_defaults-to-machine_done.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch152: kvm-Implement-base-of-SMBIOS-type-9-descriptor.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch153: kvm-Implement-SMBIOS-type-9-v2.6.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch154: kvm-smbios-cleanup-smbios_get_tables-from-legacy-handlin.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch155: kvm-smbios-get-rid-of-smbios_smp_sockets-global.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch156: kvm-smbios-get-rid-of-smbios_legacy-global.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch157: kvm-smbios-avoid-mangling-user-provided-tables.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch158: kvm-smbios-don-t-check-type4-structures-in-legacy-mode.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch159: kvm-smbios-add-smbios_add_usr_blob_size-helper.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch160: kvm-smbios-rename-expose-structures-bitmaps-used-by-both.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch161: kvm-smbios-build-legacy-mode-code-only-for-pc-machine.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch162: kvm-smbios-handle-errors-consistently.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch163: kvm-smbios-get-rid-of-global-smbios_ep_type.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch164: kvm-smbios-clear-smbios_type4_count-before-building-tabl.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch165: kvm-smbios-extend-smbios-entry-point-type-with-auto-valu.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch166: kvm-smbios-in-case-of-entry-point-is-auto-try-to-build-v.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch167: kvm-smbios-error-out-when-building-type-4-table-is-not-p.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch168: kvm-pc-q35-set-SMBIOS-entry-point-type-to-auto-by-defaul.patch
# For RHEL-28125 - RHEL9.4 - KVM : Live migration of guest with multiple qcow devices remains incomplete.
Patch169: kvm-mirror-Don-t-call-job_pause_point-under-graph-lock.patch
# For RHEL-28125 - RHEL9.4 - KVM : Live migration of guest with multiple qcow devices remains incomplete.
Patch170: kvm-nbd-server-Fix-race-in-draining-the-export.patch
# For RHEL-28125 - RHEL9.4 - KVM : Live migration of guest with multiple qcow devices remains incomplete.
Patch171: kvm-iotests-Add-test-for-reset-AioContext-switches-with-.patch
# For RHEL-21705 - pc-q35-rhel9.4.0 does not provide proper computer information
Patch172: kvm-pc-smbios-fixup-manufacturer-product-version-to-matc.patch
# For RHEL-24614 - [RHEL9][chardev] qemu hit core dump while using TLS server from host to guest
Patch173: kvm-chardev-lower-priority-of-the-HUP-GSource-in-socket-.patch
# For RHEL-24614 - [RHEL9][chardev] qemu hit core dump while using TLS server from host to guest
Patch174: kvm-Revert-chardev-char-socket-Fix-TLS-io-channels-sendi.patch
# For RHEL-24614 - [RHEL9][chardev] qemu hit core dump while using TLS server from host to guest
Patch175: kvm-Revert-chardev-use-a-child-source-for-qio-input-sour.patch
# For RHEL-28947 - Qemu crashing with "failed to set up stack guard page: Cannot allocate memory"
Patch176: kvm-coroutine-cap-per-thread-local-pool-size.patch
# For RHEL-28947 - Qemu crashing with "failed to set up stack guard page: Cannot allocate memory"
Patch177: kvm-coroutine-reserve-5-000-mappings.patch
# For RHEL-32837 - qemu-kvm running Vyatta hits assert when doing KVM_SET_GSI_ROUTING [rhel-9.4.z]
Patch178: kvm-virtio-pci-fix-use-of-a-released-vector.patch
# For RHEL-32990 - qemu crash with kvm_irqchip_commit_routes: Assertion `ret == 0' failed if booting with many virtio disks and vcpus [rhel-9.4.z]
Patch179: kvm-kvm-error-out-of-kvm_irqchip_add_msi_route-in-case-o.patch
# For RHEL-33754 - Qemu hang when quit dst vm after storage migration(nbd+tls) [rhel-9.4.z]
Patch180: kvm-nbd-server-do-not-poll-within-a-coroutine-context.patch
# For RHEL-33754 - Qemu hang when quit dst vm after storage migration(nbd+tls) [rhel-9.4.z]
Patch181: kvm-nbd-server-Mark-negotiation-functions-as-coroutine_f.patch
# For RHEL-33754 - Qemu hang when quit dst vm after storage migration(nbd+tls) [rhel-9.4.z]
Patch182: kvm-qio-Inherit-follow_coroutine_ctx-across-TLS.patch
# For RHEL-33754 - Qemu hang when quit dst vm after storage migration(nbd+tls) [rhel-9.4.z]
Patch183: kvm-iotests-test-NBD-TLS-iothread.patch
# For RHEL-35610
Patch184: kvm-qcow2-Don-t-open-data_file-with-BDRV_O_NO_IO.patch
# For RHEL-35610
Patch185: kvm-iotests-244-Don-t-store-data-file-with-protocol-in-i.patch
# For RHEL-35610
Patch186: kvm-iotests-270-Don-t-store-data-file-with-json-prefix-i.patch
# For RHEL-35610
Patch187: kvm-block-Parse-filenames-only-when-explicitly-requested.patch
# For RHEL-36181 - [RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z]
Patch188: kvm-virtio-gpu-fix-scanout-migration-post-load.patch
# For RHEL-36181 - [RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z]
Patch189: kvm-virtio-gpu-fix-v2-migration.patch
# For RHEL-36181 - [RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z]
Patch190: kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch
# For RHEL-43261 - qemu-kvm: linux-aio: add support for IO_CMD_FDSYNC command [rhel-9.4.z]
Patch191: kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch
# For RHEL-53565 - [RHEL9.4_to_RHEL9.5]When the VM is with only 9.4.0 (q35) machine type, still hit error of virtio-gpu issue [rhel-9.4.z]
Patch192: kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch
# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z]
Patch193: kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch
# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z]
Patch194: kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch
# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z]
Patch195: kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch
# For RHEL-52616 - CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z]
Patch196: kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch
%if %{have_clang}
BuildRequires: clang
@ -1709,105 +1609,8 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
%endif
%changelog
* Thu Aug 15 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11.el9_4.6
- kvm-Fix-scanout-version-with-pc-q35-rhel9.4.0.patch [RHEL-53565]
- kvm-nbd-server-Plumb-in-new-args-to-nbd_client_add.patch [RHEL-52616]
- kvm-nbd-server-CVE-2024-7409-Cap-default-max-connections.patch [RHEL-52616]
- kvm-nbd-server-CVE-2024-7409-Drop-non-negotiating-client.patch [RHEL-52616]
- kvm-nbd-server-CVE-2024-7409-Close-stray-clients-at-serv.patch [RHEL-52616]
- Resolves: RHEL-53565
([RHEL9.4_to_RHEL9.5]When the VM is with only 9.4.0 (q35) machine type, still hit error of virtio-gpu issue [rhel-9.4.z])
- Resolves: RHEL-52616
(CVE-2024-7409 qemu-kvm: Denial of Service via Improper Synchronization in QEMU NBD Server During Socket Closure [rhel-9.4.z])
* Mon Jul 15 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11.el9_4.5
- kvm-virtio-gpu-fix-scanout-migration-post-load.patch [RHEL-36181]
- kvm-virtio-gpu-fix-v2-migration.patch [RHEL-36181]
- kvm-rhel-9.4.0-machine-type-compat-for-virtio-gpu-migrat.patch [RHEL-36181]
- kvm-linux-aio-add-IO_CMD_FDSYNC-command-support.patch [RHEL-43261]
- Resolves: RHEL-36181
([RHEL9.5.0][stable_guest_abi]Failed to migrate VM with (qemu) qemu-kvm: Missing section footer for 0000:00:01.0/virtio-gpu qemu-kvm: load of migration failed: Invalid argument [rhel-9.4.0.z])
- Resolves: RHEL-43261
(qemu-kvm: linux-aio: add support for IO_CMD_FDSYNC command [rhel-9.4.z])
* Wed Jun 19 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11.el9_4.4
- Fixing CVE-2024-4467
- Resolves: RHEL-35610
* Fri Jun 07 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11.el9_4.3
- kvm-nbd-server-do-not-poll-within-a-coroutine-context.patch [RHEL-33754]
- kvm-nbd-server-Mark-negotiation-functions-as-coroutine_f.patch [RHEL-33754]
- kvm-qio-Inherit-follow_coroutine_ctx-across-TLS.patch [RHEL-33754]
- kvm-iotests-test-NBD-TLS-iothread.patch [RHEL-33754]
- Resolves: RHEL-33754
(Qemu hang when quit dst vm after storage migration(nbd+tls) [rhel-9.4.z])
* Mon May 20 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11.el9_4.2
- kvm-kvm-error-out-of-kvm_irqchip_add_msi_route-in-case-o.patch [RHEL-32990]
- Resolves: RHEL-32990
(qemu crash with kvm_irqchip_commit_routes: Assertion `ret == 0' failed if booting with many virtio disks and vcpus [rhel-9.4.z])
* Thu Apr 18 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11.el9_4.1
- kvm-virtio-pci-fix-use-of-a-released-vector.patch [RHEL-32837]
- Resolves: RHEL-32837
(qemu-kvm running Vyatta hits assert when doing KVM_SET_GSI_ROUTING [rhel-9.4.z])
* Tue Mar 26 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-11
- kvm-coroutine-cap-per-thread-local-pool-size.patch [RHEL-28947]
- kvm-coroutine-reserve-5-000-mappings.patch [RHEL-28947]
- Resolves: RHEL-28947
(Qemu crashing with "failed to set up stack guard page: Cannot allocate memory")
* Thu Mar 21 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-10
- kvm-chardev-lower-priority-of-the-HUP-GSource-in-socket-.patch [RHEL-24614]
- kvm-Revert-chardev-char-socket-Fix-TLS-io-channels-sendi.patch [RHEL-24614]
- kvm-Revert-chardev-use-a-child-source-for-qio-input-sour.patch [RHEL-24614]
- Resolves: RHEL-24614
([RHEL9][chardev] qemu hit core dump while using TLS server from host to guest)
* Wed Mar 20 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-9
- kvm-mirror-Don-t-call-job_pause_point-under-graph-lock.patch [RHEL-28125]
- kvm-nbd-server-Fix-race-in-draining-the-export.patch [RHEL-28125]
- kvm-iotests-Add-test-for-reset-AioContext-switches-with-.patch [RHEL-28125]
- kvm-pc-smbios-fixup-manufacturer-product-version-to-matc.patch [RHEL-21705]
- Resolves: RHEL-28125
(RHEL9.4 - KVM : Live migration of guest with multiple qcow devices remains incomplete.)
- Resolves: RHEL-21705
(pc-q35-rhel9.4.0 does not provide proper computer information)
* Mon Mar 18 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-8
- kvm-ui-clipboard-mark-type-as-not-available-when-there-i.patch [RHEL-19629]
- kvm-ui-clipboard-add-asserts-for-update-and-request.patch [RHEL-19629]
- kvm-hw-i386-pc-Defer-smbios_set_defaults-to-machine_done.patch [RHEL-21705]
- kvm-Implement-base-of-SMBIOS-type-9-descriptor.patch [RHEL-21705]
- kvm-Implement-SMBIOS-type-9-v2.6.patch [RHEL-21705]
- kvm-smbios-cleanup-smbios_get_tables-from-legacy-handlin.patch [RHEL-21705]
- kvm-smbios-get-rid-of-smbios_smp_sockets-global.patch [RHEL-21705]
- kvm-smbios-get-rid-of-smbios_legacy-global.patch [RHEL-21705]
- kvm-smbios-avoid-mangling-user-provided-tables.patch [RHEL-21705]
- kvm-smbios-don-t-check-type4-structures-in-legacy-mode.patch [RHEL-21705]
- kvm-smbios-add-smbios_add_usr_blob_size-helper.patch [RHEL-21705]
- kvm-smbios-rename-expose-structures-bitmaps-used-by-both.patch [RHEL-21705]
- kvm-smbios-build-legacy-mode-code-only-for-pc-machine.patch [RHEL-21705]
- kvm-smbios-handle-errors-consistently.patch [RHEL-21705]
- kvm-smbios-get-rid-of-global-smbios_ep_type.patch [RHEL-21705]
- kvm-smbios-clear-smbios_type4_count-before-building-tabl.patch [RHEL-21705]
- kvm-smbios-extend-smbios-entry-point-type-with-auto-valu.patch [RHEL-21705]
- kvm-smbios-in-case-of-entry-point-is-auto-try-to-build-v.patch [RHEL-21705]
- kvm-smbios-error-out-when-building-type-4-table-is-not-p.patch [RHEL-21705]
- kvm-pc-q35-set-SMBIOS-entry-point-type-to-auto-by-defaul.patch [RHEL-21705]
- Resolves: RHEL-19629
(CVE-2023-6683 qemu-kvm: QEMU: VNC: NULL pointer dereference in qemu_clipboard_request() [rhel-9])
- Resolves: RHEL-21705
(pc-q35-rhel9.4.0 does not provide proper computer information)
* Fri Mar 08 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-7
- kvm-qemu_init-increase-NOFILE-soft-limit-on-POSIX.patch [RHEL-26049]
- kvm-chardev-char-socket-Fix-TLS-io-channels-sending-too-.patch [RHEL-24614]
- Resolves: RHEL-26049
(When max vcpu is greater than or equal to 246, qemu unable to init event notifier)
- Resolves: RHEL-24614
([RHEL9][chardev][s390x] qemu hit core dump while using TLS server from host to guest)
* Tue Apr 02 2024 Arkady L. Shane <tigro@msvsphere-os.ru> - 17:8.2.0-6.inferit
- Added host_cdrom driver to whitelist (INF-741)
* Mon Feb 19 2024 Miroslav Rezanina <mrezanin@redhat.com> - 8.2.0-6
- kvm-virtio-scsi-Attach-event-vq-notifier-with-no_poll.patch [RHEL-3934]
@ -2349,6 +2152,9 @@ useradd -r -u 107 -g qemu -G kvm -d / -s /sbin/nologin \
- Resolves: bz#2180898
(Rebase to QEMU 8.0.0 for RHEL 9.3.0)
* Fri Apr 14 2023 MSVSphere Packaging Team <packager@msvsphere.ru> - 7.2.0-14
- Rebuilt for MSVSphere 9.2 beta
* Mon Mar 20 2023 Miroslav Rezanina <mrezanin@redhat.com> - 7.2.0-14
- Rebuild for 9.2 release
- Resolves: bz#2173590

Loading…
Cancel
Save