From: Dmitry R <rdmitry0911@gmail.com>
To: qemu-devel@nongnu.org
Cc: Dmitry R <rdmitry0911@gmail.com>,
"Michael S . Tsirkin" <mst@redhat.com>,
Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>,
Fabiano Rosas <farosas@suse.de>,
Laurent Vivier <lvivier@redhat.com>,
Paolo Bonzini <pbonzini@redhat.com>
Subject: [RFC PATCH v2 3/5] hw/display: add Thunderbolt VGA endpoint
Date: Sat, 11 Jul 2026 12:47:59 +0300 [thread overview]
Message-ID: <20260711094803.3589239-4-rdmitry0911@gmail.com> (raw)
In-Reply-To: <20260711094803.3589239-1-rdmitry0911@gmail.com>
Add thunderbolt-vga as a separate stdvga-compatible hotpluggable endpoint
for Thunderbolt display hotplug tests.
The regular QEMU VGA device is intentionally left untouched and remains
non-hotpluggable. thunderbolt-vga.c keeps the MIT license because it is
derived from the existing stdvga implementation in hw/display/vga-pci.c,
which carries the same license.
Signed-off-by: Dmitry R <rdmitry0911@gmail.com>
---
hw/display/meson.build | 1 +
hw/display/thunderbolt-vga.c | 275 +++++++++++++++++++++++++++++++++++
2 files changed, 276 insertions(+)
create mode 100644 hw/display/thunderbolt-vga.c
diff --git a/hw/display/meson.build b/hw/display/meson.build
index ffecedbf70..7074d2b5f9 100644
--- a/hw/display/meson.build
+++ b/hw/display/meson.build
@@ -19,6 +19,7 @@ system_ss.add(when: 'CONFIG_SSD0323', if_true: files('ssd0323.c'))
system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xenfb.c'))
system_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('vga-pci.c'))
+system_ss.add(when: 'CONFIG_VGA_PCI', if_true: files('thunderbolt-vga.c'))
system_ss.add(when: 'CONFIG_VGA_ISA', if_true: files('vga-isa.c'))
system_ss.add(when: 'CONFIG_VGA_MMIO', if_true: files('vga-mmio.c'))
system_ss.add(when: 'CONFIG_VMWARE_VGA', if_true: files('vmware_vga.c'))
diff --git a/hw/display/thunderbolt-vga.c b/hw/display/thunderbolt-vga.c
new file mode 100644
index 0000000000..4eabe357c2
--- /dev/null
+++ b/hw/display/thunderbolt-vga.c
@@ -0,0 +1,275 @@
+/*
+ * Thunderbolt hotpluggable PCI VGA device.
+ *
+ * This is a stdvga-compatible endpoint with a separate QOM type for
+ * Thunderbolt hotplug experiments. The regular "VGA" type remains unchanged.
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * SPDX-License-Identifier: MIT
+ */
+
+#include "qemu/osdep.h"
+#include "hw/core/loader.h"
+#include "hw/core/qdev-properties.h"
+#include "hw/display/edid.h"
+#include "hw/pci/pci_device.h"
+#include "qemu/module.h"
+#include "qom/object.h"
+#include "ui/console.h"
+#include "ui/pixel_ops.h"
+#include "vga_int.h"
+
+#define TYPE_PCI_VGA "pci-vga"
+#define TYPE_THUNDERBOLT_VGA "thunderbolt-vga"
+
+OBJECT_DECLARE_SIMPLE_TYPE(ThunderboltVGAState, THUNDERBOLT_VGA)
+
+enum thunderbolt_vga_pci_flags {
+ THUNDERBOLT_VGA_FLAG_ENABLE_MMIO = 1,
+ THUNDERBOLT_VGA_FLAG_ENABLE_QEXT = 2,
+ THUNDERBOLT_VGA_FLAG_ENABLE_EDID = 3,
+};
+
+struct ThunderboltVGAState {
+ PCIDevice dev;
+ VGACommonState vga;
+ uint32_t flags;
+ qemu_edid_info edid_info;
+ MemoryRegion mmio;
+ MemoryRegion mrs[4];
+ uint8_t edid[384];
+};
+
+static uint64_t thunderbolt_vga_ioport_read(void *ptr, hwaddr addr,
+ unsigned size)
+{
+ VGACommonState *s = ptr;
+ uint64_t ret = 0;
+
+ switch (size) {
+ case 1:
+ ret = vga_ioport_read(s, addr + 0x3c0);
+ break;
+ case 2:
+ ret = vga_ioport_read(s, addr + 0x3c0);
+ ret |= vga_ioport_read(s, addr + 0x3c1) << 8;
+ break;
+ }
+ return ret;
+}
+
+static void thunderbolt_vga_ioport_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VGACommonState *s = ptr;
+
+ switch (size) {
+ case 1:
+ vga_ioport_write(s, addr + 0x3c0, val);
+ break;
+ case 2:
+ vga_ioport_write(s, addr + 0x3c0, val & 0xff);
+ vga_ioport_write(s, addr + 0x3c1, (val >> 8) & 0xff);
+ break;
+ }
+}
+
+static const MemoryRegionOps thunderbolt_vga_ioport_ops = {
+ .read = thunderbolt_vga_ioport_read,
+ .write = thunderbolt_vga_ioport_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t thunderbolt_vga_bochs_read(void *ptr, hwaddr addr,
+ unsigned size)
+{
+ VGACommonState *s = ptr;
+ int index = addr >> 1;
+
+ vbe_ioport_write_index(s, 0, index);
+ return vbe_ioport_read_data(s, 0);
+}
+
+static void thunderbolt_vga_bochs_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VGACommonState *s = ptr;
+ int index = addr >> 1;
+
+ vbe_ioport_write_index(s, 0, index);
+ vbe_ioport_write_data(s, 0, val);
+}
+
+static const MemoryRegionOps thunderbolt_vga_bochs_ops = {
+ .read = thunderbolt_vga_bochs_read,
+ .write = thunderbolt_vga_bochs_write,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
+ .impl.min_access_size = 2,
+ .impl.max_access_size = 2,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t thunderbolt_vga_qext_read(void *ptr, hwaddr addr,
+ unsigned size)
+{
+ VGACommonState *s = ptr;
+
+ switch (addr) {
+ case PCI_VGA_QEXT_REG_SIZE:
+ return PCI_VGA_QEXT_SIZE;
+ case PCI_VGA_QEXT_REG_BYTEORDER:
+ return s->big_endian_fb ?
+ PCI_VGA_QEXT_BIG_ENDIAN : PCI_VGA_QEXT_LITTLE_ENDIAN;
+ default:
+ return 0;
+ }
+}
+
+static void thunderbolt_vga_qext_write(void *ptr, hwaddr addr,
+ uint64_t val, unsigned size)
+{
+ VGACommonState *s = ptr;
+
+ switch (addr) {
+ case PCI_VGA_QEXT_REG_BYTEORDER:
+ if (val == PCI_VGA_QEXT_BIG_ENDIAN) {
+ s->big_endian_fb = true;
+ }
+ if (val == PCI_VGA_QEXT_LITTLE_ENDIAN) {
+ s->big_endian_fb = false;
+ }
+ break;
+ }
+}
+
+static const MemoryRegionOps thunderbolt_vga_qext_ops = {
+ .read = thunderbolt_vga_qext_read,
+ .write = thunderbolt_vga_qext_write,
+ .valid.min_access_size = 4,
+ .valid.max_access_size = 4,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static bool thunderbolt_vga_get_big_endian_fb(Object *obj, Error **errp)
+{
+ ThunderboltVGAState *d = THUNDERBOLT_VGA(obj);
+
+ return d->vga.big_endian_fb;
+}
+
+static void thunderbolt_vga_set_big_endian_fb(Object *obj, bool value,
+ Error **errp)
+{
+ ThunderboltVGAState *d = THUNDERBOLT_VGA(obj);
+
+ d->vga.big_endian_fb = value;
+}
+
+static void thunderbolt_vga_mmio_region_init(ThunderboltVGAState *d,
+ Object *owner,
+ MemoryRegion *parent)
+{
+ VGACommonState *s = &d->vga;
+
+ memory_region_init_io(&d->mrs[0], owner, &thunderbolt_vga_ioport_ops, s,
+ "vga ioports remapped", PCI_VGA_IOPORT_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET, &d->mrs[0]);
+
+ memory_region_init_io(&d->mrs[1], owner, &thunderbolt_vga_bochs_ops, s,
+ "bochs dispi interface", PCI_VGA_BOCHS_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_BOCHS_OFFSET, &d->mrs[1]);
+
+ if (d->flags & (1 << THUNDERBOLT_VGA_FLAG_ENABLE_QEXT)) {
+ memory_region_init_io(&d->mrs[2], owner, &thunderbolt_vga_qext_ops, s,
+ "qemu extended regs", PCI_VGA_QEXT_SIZE);
+ memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET, &d->mrs[2]);
+ }
+
+ if (d->flags & (1 << THUNDERBOLT_VGA_FLAG_ENABLE_EDID)) {
+ qemu_edid_generate(d->edid, sizeof(d->edid), &d->edid_info);
+ qemu_edid_region_io(&d->mrs[3], owner, d->edid, sizeof(d->edid));
+ memory_region_add_subregion(parent, 0, &d->mrs[3]);
+ }
+}
+
+static void thunderbolt_vga_realize(PCIDevice *dev, Error **errp)
+{
+ ThunderboltVGAState *d = THUNDERBOLT_VGA(dev);
+ VGACommonState *s = &d->vga;
+
+ if (!vga_common_init(s, OBJECT(dev), errp)) {
+ return;
+ }
+ vga_init(s, OBJECT(dev), pci_address_space(dev), pci_address_space_io(dev),
+ true);
+
+ s->con = qemu_graphic_console_create(DEVICE(dev), 0, s->hw_ops, s);
+
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram);
+
+ if (d->flags & (1 << THUNDERBOLT_VGA_FLAG_ENABLE_MMIO)) {
+ memory_region_init_io(&d->mmio, OBJECT(dev), &unassigned_io_ops, NULL,
+ "vga.mmio", PCI_VGA_MMIO_SIZE);
+
+ if (d->flags & (1 << THUNDERBOLT_VGA_FLAG_ENABLE_QEXT)) {
+ pci_set_byte(&dev->config[PCI_REVISION_ID], 2);
+ }
+
+ thunderbolt_vga_mmio_region_init(d, OBJECT(dev), &d->mmio);
+ pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
+ }
+}
+
+static const Property thunderbolt_vga_properties[] = {
+ DEFINE_PROP_UINT32("vgamem_mb", ThunderboltVGAState, vga.vram_size_mb, 4),
+ DEFINE_PROP_BIT("mmio", ThunderboltVGAState, flags,
+ THUNDERBOLT_VGA_FLAG_ENABLE_MMIO, true),
+ DEFINE_PROP_BIT("qemu-extended-regs", ThunderboltVGAState, flags,
+ THUNDERBOLT_VGA_FLAG_ENABLE_QEXT, true),
+ DEFINE_PROP_BIT("edid", ThunderboltVGAState, flags,
+ THUNDERBOLT_VGA_FLAG_ENABLE_EDID, true),
+ DEFINE_PROP_UINT32("xres", ThunderboltVGAState, edid_info.prefx, 1280),
+ DEFINE_PROP_UINT32("yres", ThunderboltVGAState, edid_info.prefy, 800),
+ DEFINE_PROP_UINT32("xmax", ThunderboltVGAState, edid_info.maxx, 1280),
+ DEFINE_PROP_UINT32("ymax", ThunderboltVGAState, edid_info.maxy, 800),
+ DEFINE_PROP_UINT32("refresh_rate", ThunderboltVGAState,
+ edid_info.refresh_rate, 60000),
+ DEFINE_PROP_BOOL("global-vmstate", ThunderboltVGAState,
+ vga.global_vmstate, false),
+};
+
+static void thunderbolt_vga_class_init(ObjectClass *klass, const void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+ k->realize = thunderbolt_vga_realize;
+ k->romfile = "vgabios-stdvga.bin";
+ k->class_id = PCI_CLASS_DISPLAY_VGA;
+ device_class_set_props(dc, thunderbolt_vga_properties);
+ dc->desc = "Thunderbolt hotpluggable VGA";
+ dc->hotpluggable = true;
+
+ object_class_property_add_bool(klass, "big-endian-framebuffer",
+ thunderbolt_vga_get_big_endian_fb,
+ thunderbolt_vga_set_big_endian_fb);
+}
+
+static const TypeInfo thunderbolt_vga_info = {
+ .name = TYPE_THUNDERBOLT_VGA,
+ .parent = TYPE_PCI_VGA,
+ .instance_size = sizeof(ThunderboltVGAState),
+ .class_init = thunderbolt_vga_class_init,
+};
+
+static void thunderbolt_vga_register_types(void)
+{
+ type_register_static(&thunderbolt_vga_info);
+}
+
+type_init(thunderbolt_vga_register_types)
--
2.47.3
next prev parent reply other threads:[~2026-07-11 9:49 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-10 13:34 [RFC PATCH v1 0/1] Thunderbolt PCIe hotplug layer Dmitry R
2026-07-10 13:34 ` [RFC PATCH v1 1/1] hw/pci-bridge: add " Dmitry R
2026-07-11 9:17 ` [RFC PATCH v1 0/1] " Michael S. Tsirkin
2026-07-11 9:47 ` [RFC PATCH v2 0/5] " Dmitry R
2026-07-11 9:47 ` [RFC PATCH v2 1/5] hw/pci-bridge: add Thunderbolt root port Dmitry R
2026-07-11 9:47 ` [RFC PATCH v2 2/5] hw/pci-bridge: add Thunderbolt PCIe-to-PCI bridge Dmitry R
2026-07-11 9:47 ` Dmitry R [this message]
2026-07-11 9:48 ` [RFC PATCH v2 4/5] tests/qtest: cover Thunderbolt VGA hotplug Dmitry R
2026-07-11 9:48 ` [RFC PATCH v2 5/5] docs: document Thunderbolt PCIe hotplug layer Dmitry R
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260711094803.3589239-4-rdmitry0911@gmail.com \
--to=rdmitry0911@gmail.com \
--cc=farosas@suse.de \
--cc=lvivier@redhat.com \
--cc=mst@redhat.com \
--cc=pbonzini@redhat.com \
--cc=pierrick.bouvier@oss.qualcomm.com \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.