From e8714ab84bc76b89091fb2ccb41c34e69e4ae9a2 Mon Sep 17 00:00:00 2001 From: Anees Iqbal Date: Mon, 17 Aug 2026 13:43:03 -0400 Subject: [PATCH] vmapple: add socket backend for booter USB transport --- hw/vmapple/bdif.c | 165 +++++++++++++++++++++++++++++++++++++++- hw/vmapple/trace-events | 2 + 2 files changed, 165 insertions(+), 2 deletions(-) diff --git a/hw/vmapple/bdif.c b/hw/vmapple/bdif.c index 4dc10c151d..a37ccf4614 100644 --- a/hw/vmapple/bdif.c +++ b/hw/vmapple/bdif.c @@ -20,6 +20,7 @@ #include "qapi/error.h" #include "system/block-backend.h" #include "system/dma.h" +#include "chardev/char-fe.h" OBJECT_DECLARE_SIMPLE_TYPE(VMAppleBdifState, VMAPPLE_BDIF) @@ -29,6 +30,13 @@ struct VMAppleBdifState { BlockBackend *aux; BlockBackend *root; MemoryRegion mmio; + CharFrontend usbdev; + GByteArray *usb_input; + uint32_t usb_queue; + bool usb_rx_ready; + bool usb_rx_posted; + uint64_t usb_rx_addr; + uint32_t usb_rx_len; }; #define VMAPPLE_BDIF_SIZE 0x00200000 @@ -42,11 +50,13 @@ struct VMAppleBdifState { #define REG_STATUS_ACTIVE BIT(0) #define REG_CFG 0x4 #define REG_CFG_ACTIVE BIT(1) +#define REG_CFG_USB 0x1a01 #define REG_UNK1 0x8 #define REG_BUSY 0x10 #define REG_BUSY_READY BIT(0) #define REG_UNK2 0x400 #define REG_CMD 0x408 +#define REG_QUEUE 0x410 #define REG_NEXT_DEVICE 0x420 #define REG_UNK3 0x434 @@ -77,6 +87,7 @@ typedef struct VblkReq { static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size) { + VMAppleBdifState *s = opaque; uint64_t ret = -1; uint64_t devid = offset & REG_DEVID_MASK; @@ -85,13 +96,21 @@ static uint64_t bdif_read(void *opaque, hwaddr offset, unsigned size) ret = REG_STATUS_ACTIVE; break; case REG_CFG: - ret = REG_CFG_ACTIVE; + ret = devid == DEVID_USB ? REG_CFG_USB : REG_CFG_ACTIVE; break; case REG_UNK1: ret = 0x420; break; case REG_BUSY: - ret = REG_BUSY_READY; + if (devid == DEVID_USB && s->usb_queue == 0) { + ret = s->usb_rx_ready ? REG_BUSY_READY : 0; + if (s->usb_rx_ready) { + s->usb_rx_posted = false; + } + s->usb_rx_ready = false; + } else { + ret = REG_BUSY_READY; + } break; case REG_UNK2: ret = 0x1; @@ -200,6 +219,119 @@ out: MEMTXATTRS_UNSPECIFIED); } +typedef struct VUsbDesc { + uint64_t addr; + uint32_t len; + uint16_t next; + uint16_t flags; +} QEMU_PACKED VUsbDesc; + +#define VUSB_MAX_PACKET (128 * KiB) + +static void vusb_try_deliver(VMAppleBdifState *s) +{ + uint32_t len; + MemTxResult dma_result; + + if (!s->usb_rx_posted || s->usb_rx_ready || s->usb_input->len < 4) { + return; + } + + memcpy(&len, s->usb_input->data, sizeof(len)); + len = le32_to_cpu(len); + if (len > VUSB_MAX_PACKET) { + qemu_log_mask(LOG_GUEST_ERROR, + "vmapple-bdif: oversized USB packet: %u\n", len); + g_byte_array_set_size(s->usb_input, 0); + return; + } + if (s->usb_input->len < sizeof(len) + len) { + return; + } + if (len > s->usb_rx_len) { + qemu_log_mask(LOG_GUEST_ERROR, + "vmapple-bdif: USB packet %u exceeds guest buffer %u\n", + len, s->usb_rx_len); + g_byte_array_remove_range(s->usb_input, 0, sizeof(len) + len); + return; + } + + dma_result = dma_memory_write(&address_space_memory, s->usb_rx_addr, + s->usb_input->data + sizeof(len), len, + MEMTXATTRS_UNSPECIFIED); + if (dma_result == MEMTX_OK) { + s->usb_rx_ready = true; + } + g_byte_array_remove_range(s->usb_input, 0, sizeof(len) + len); +} + +static int vusb_can_receive(void *opaque) +{ + VMAppleBdifState *s = opaque; + + return VUSB_MAX_PACKET - MIN(s->usb_input->len, VUSB_MAX_PACKET); +} + +static void vusb_receive(void *opaque, const uint8_t *buf, int size) +{ + VMAppleBdifState *s = opaque; + + g_byte_array_append(s->usb_input, buf, size); + vusb_try_deliver(s); +} + +static void vusb_cmd(VMAppleBdifState *s, uint64_t gp_addr) +{ + uint64_t words[16]; + VUsbDesc desc; + MemTxResult dma_result; + unsigned i; + + dma_result = dma_memory_read(&address_space_memory, gp_addr, words, + sizeof(words), MEMTXATTRS_UNSPECIFIED); + trace_bdif_usb_cmd(gp_addr, dma_result); + if (dma_result != MEMTX_OK) { + return; + } + + for (i = 0; i < ARRAY_SIZE(words); i++) { + trace_bdif_usb_word(i * sizeof(words[i]), le64_to_cpu(words[i])); + } + + memcpy(&desc, words, sizeof(desc)); + desc.addr = le64_to_cpu(desc.addr); + desc.len = le32_to_cpu(desc.len); + desc.next = le16_to_cpu(desc.next); + desc.flags = le16_to_cpu(desc.flags); + + if (s->usb_queue == 0) { + s->usb_rx_addr = desc.addr; + s->usb_rx_len = desc.len; + s->usb_rx_posted = true; + vusb_try_deliver(s); + } else if (s->usb_queue == 1 && desc.len <= 4096) { + g_autofree uint8_t *buf = g_malloc0(desc.len); + + if (dma_memory_read(&address_space_memory, desc.addr, buf, desc.len, + MEMTXATTRS_UNSPECIFIED) == MEMTX_OK) { + uint32_t wire_len = cpu_to_le32(desc.len); + + for (i = 0; i < DIV_ROUND_UP(desc.len, sizeof(uint64_t)); i++) { + uint64_t word = 0; + size_t chunk = MIN(sizeof(word), desc.len - i * sizeof(word)); + + memcpy(&word, buf + i * sizeof(word), chunk); + trace_bdif_usb_word(i * sizeof(word), le64_to_cpu(word)); + } + if (qemu_chr_fe_backend_connected(&s->usbdev)) { + qemu_chr_fe_write_all(&s->usbdev, (uint8_t *)&wire_len, + sizeof(wire_len)); + qemu_chr_fe_write_all(&s->usbdev, buf, desc.len); + } + } + } +} + static void bdif_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { @@ -209,6 +341,11 @@ static void bdif_write(void *opaque, hwaddr offset, trace_bdif_write(offset, size, value); switch (offset & ~REG_DEVID_MASK) { + case REG_QUEUE: + if (devid == DEVID_USB) { + s->usb_queue = value; + } + break; case REG_CMD: switch (devid) { case DEVID_ROOT: @@ -217,6 +354,9 @@ static void bdif_write(void *opaque, hwaddr offset, case DEVID_AUX: vblk_cmd(devid, s->aux, value, 0x0); break; + case DEVID_USB: + vusb_cmd(s, value); + break; } break; } @@ -243,11 +383,30 @@ static void bdif_init(Object *obj) memory_region_init_io(&s->mmio, obj, &bdif_ops, obj, "VMApple Backdoor Interface", VMAPPLE_BDIF_SIZE); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + s->usb_input = g_byte_array_new(); +} + +static void bdif_realize(DeviceState *dev, Error **errp) +{ + VMAppleBdifState *s = VMAPPLE_BDIF(dev); + + if (qemu_chr_fe_backend_connected(&s->usbdev)) { + qemu_chr_fe_set_handlers(&s->usbdev, vusb_can_receive, vusb_receive, + NULL, NULL, s, NULL, true); + } +} + +static void bdif_finalize(Object *obj) +{ + VMAppleBdifState *s = VMAPPLE_BDIF(obj); + + g_clear_pointer(&s->usb_input, g_byte_array_unref); } static const Property bdif_properties[] = { DEFINE_PROP_DRIVE("aux", VMAppleBdifState, aux), DEFINE_PROP_DRIVE("root", VMAppleBdifState, root), + DEFINE_PROP_CHR("usbdev", VMAppleBdifState, usbdev), }; static void bdif_class_init(ObjectClass *klass, const void *data) @@ -255,6 +414,7 @@ static void bdif_class_init(ObjectClass *klass, const void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "VMApple Backdoor Interface"; + dc->realize = bdif_realize; device_class_set_props(dc, bdif_properties); } @@ -263,6 +423,7 @@ static const TypeInfo bdif_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(VMAppleBdifState), .instance_init = bdif_init, + .instance_finalize = bdif_finalize, .class_init = bdif_class_init, }; diff --git a/hw/vmapple/trace-events b/hw/vmapple/trace-events index 93380ede14..9c58451bc3 100644 --- a/hw/vmapple/trace-events +++ b/hw/vmapple/trace-events @@ -19,3 +19,5 @@ aes_dump_data(const char *desc, const char *hex) "%s%s" bdif_read(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 bdif_write(uint64_t offset, uint32_t size, uint64_t value) "offset=0x%"PRIx64" size=0x%x value=0x%"PRIx64 bdif_vblk_read(const char *dev, uint64_t addr, uint64_t offset, uint32_t len, int r) "dev=%s addr=0x%"PRIx64" off=0x%"PRIx64" size=0x%x r=%d" +bdif_usb_cmd(uint64_t addr, int dma_result) "addr=0x%"PRIx64" dma_result=%d" +bdif_usb_word(uint32_t offset, uint64_t value) "offset=0x%x value=0x%"PRIx64 -- 2.54.0