Rather than requiring users of TYPE_SERIAL to initialize the MMIO region
themselves, make it generic enough to be configured via properties. This
makes TYPE_SERIAL more self-contained and prepares it for being turned
into a SysBusDevice.
Note that for TYPE_SERIAL_MM, if the regshift property has the default value
zero, serial_io_ops rather than serial_mm_ops is now used. This seems to be
fine since in both cases, registers are one byte wide and there are no gaps
between registers. Therefore, neither the endianness nor the access size
should make a difference. Running `make check` and `make check-functional`
seems to confirm this.
Signed-off-by: Bernhard Beschow <shentey@gmail.com>
---
include/hw/char/serial-mm.h | 3 --
include/hw/char/serial.h | 4 +-
hw/char/diva-gsp.c | 5 ---
hw/char/serial-isa.c | 1 -
hw/char/serial-mm.c | 51 -------------------------
hw/char/serial-pci-multi.c | 5 ---
hw/char/serial-pci.c | 1 -
hw/char/serial.c | 76 ++++++++++++++++++++++++++++++-------
8 files changed, 66 insertions(+), 80 deletions(-)
diff --git a/include/hw/char/serial-mm.h b/include/hw/char/serial-mm.h
index 0076bdc061..4c18e2a609 100644
--- a/include/hw/char/serial-mm.h
+++ b/include/hw/char/serial-mm.h
@@ -39,9 +39,6 @@ struct SerialMM {
SysBusDevice parent;
SerialState serial;
-
- uint8_t regshift;
- uint8_t endianness;
};
SerialMM *serial_mm_init(MemoryRegion *address_space,
diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
index ea82ffac47..0cf641a860 100644
--- a/include/hw/char/serial.h
+++ b/include/hw/char/serial.h
@@ -62,6 +62,9 @@ struct SerialState {
guint watch_tag;
bool wakeup;
+ uint8_t regshift;
+ uint8_t endianness;
+
/* Time when the last byte was successfully sent out of the tsr */
uint64_t last_xmit_ts;
Fifo8 recv_fifo;
@@ -80,7 +83,6 @@ struct SerialState {
};
extern const VMStateDescription vmstate_serial;
-extern const MemoryRegionOps serial_io_ops;
#define TYPE_SERIAL "serial"
OBJECT_DECLARE_SIMPLE_TYPE(SerialState, SERIAL)
diff --git a/hw/char/diva-gsp.c b/hw/char/diva-gsp.c
index f9aa6e326d..b0a2437c85 100644
--- a/hw/char/diva-gsp.c
+++ b/hw/char/diva-gsp.c
@@ -47,7 +47,6 @@ typedef struct PCIDivaSerialState {
MemoryRegion mailboxbar; /* for hardware mailbox */
uint32_t subvendor;
uint32_t ports;
- char *name[PCI_SERIAL_MAX_PORTS];
SerialState state[PCI_SERIAL_MAX_PORTS];
uint32_t level[PCI_SERIAL_MAX_PORTS];
qemu_irq *irqs;
@@ -64,7 +63,6 @@ static void diva_pci_exit(PCIDevice *dev)
s = pci->state + i;
memory_region_del_subregion(&pci->membar, &s->io);
qdev_unrealize(DEVICE(s));
- g_free(pci->name[i]);
}
qemu_free_irqs(pci->irqs, pci->ports);
}
@@ -136,9 +134,6 @@ static void diva_pci_realize(PCIDevice *dev, Error **errp)
return;
}
s->irq = pci->irqs[i];
- pci->name[i] = g_strdup_printf("uart #%zu", i + 1);
- memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s,
- pci->name[i], 8);
/* calculate offset of given port based on bitmask */
while ((portmask & BIT(0)) == 0) {
diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
index a4be0492c5..3a48b2495e 100644
--- a/hw/char/serial-isa.c
+++ b/hw/char/serial-isa.c
@@ -80,7 +80,6 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp)
qdev_realize(DEVICE(s), NULL, errp);
qdev_set_legacy_instance_id(dev, isa->iobase, 3);
- memory_region_init_io(&s->io, OBJECT(isa), &serial_io_ops, s, "serial", 8);
isa_register_ioport(isadev, &s->io, isa->iobase);
}
diff --git a/hw/char/serial-mm.c b/hw/char/serial-mm.c
index 0e0be26fa9..1dba4fc694 100644
--- a/hw/char/serial-mm.c
+++ b/hw/char/serial-mm.c
@@ -30,44 +30,6 @@
#include "qapi/error.h"
#include "hw/core/qdev-properties.h"
-static uint64_t serial_mm_read(void *opaque, hwaddr addr, unsigned size)
-{
- SerialMM *s = SERIAL_MM(opaque);
- return serial_io_ops.read(&s->serial, addr >> s->regshift, 1);
-}
-
-static void serial_mm_write(void *opaque, hwaddr addr,
- uint64_t value, unsigned size)
-{
- SerialMM *s = SERIAL_MM(opaque);
- value &= 255;
- serial_io_ops.write(&s->serial, addr >> s->regshift, value, 1);
-}
-
-static const MemoryRegionOps serial_mm_ops[] = {
- [DEVICE_NATIVE_ENDIAN] = {
- .read = serial_mm_read,
- .write = serial_mm_write,
- .endianness = DEVICE_NATIVE_ENDIAN,
- .valid.max_access_size = 8,
- .impl.max_access_size = 8,
- },
- [DEVICE_LITTLE_ENDIAN] = {
- .read = serial_mm_read,
- .write = serial_mm_write,
- .endianness = DEVICE_LITTLE_ENDIAN,
- .valid.max_access_size = 8,
- .impl.max_access_size = 8,
- },
- [DEVICE_BIG_ENDIAN] = {
- .read = serial_mm_read,
- .write = serial_mm_write,
- .endianness = DEVICE_BIG_ENDIAN,
- .valid.max_access_size = 8,
- .impl.max_access_size = 8,
- },
-};
-
static void serial_mm_realize(DeviceState *dev, Error **errp)
{
SerialMM *smm = SERIAL_MM(dev);
@@ -77,9 +39,6 @@ static void serial_mm_realize(DeviceState *dev, Error **errp)
return;
}
- memory_region_init_io(&s->io, OBJECT(dev),
- &serial_mm_ops[smm->endianness], smm, "serial",
- 8 << smm->regshift);
sysbus_init_mmio(SYS_BUS_DEVICE(smm), &s->io);
sysbus_init_irq(SYS_BUS_DEVICE(smm), &smm->serial.irq);
}
@@ -125,20 +84,10 @@ static void serial_mm_instance_init(Object *o)
qdev_alias_all_properties(DEVICE(&smm->serial), o);
}
-static const Property serial_mm_properties[] = {
- /*
- * Set the spacing between adjacent memory-mapped UART registers.
- * Each register will be at (1 << regshift) bytes after the previous one.
- */
- DEFINE_PROP_UINT8("regshift", SerialMM, regshift, 0),
- DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN),
-};
-
static void serial_mm_class_init(ObjectClass *oc, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
- device_class_set_props(dc, serial_mm_properties);
dc->realize = serial_mm_realize;
dc->vmsd = &vmstate_serial_mm;
}
diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c
index 7782452018..eda0d43bcf 100644
--- a/hw/char/serial-pci-multi.c
+++ b/hw/char/serial-pci-multi.c
@@ -42,7 +42,6 @@ typedef struct PCIMultiSerialState {
PCIDevice dev;
MemoryRegion iobar;
uint32_t ports;
- char *name[PCI_SERIAL_MAX_PORTS];
SerialState state[PCI_SERIAL_MAX_PORTS];
uint32_t level[PCI_SERIAL_MAX_PORTS];
IRQState irqs[PCI_SERIAL_MAX_PORTS];
@@ -58,7 +57,6 @@ static void multi_serial_pci_exit(PCIDevice *dev)
s = pci->state + i;
memory_region_del_subregion(&pci->iobar, &s->io);
qdev_unrealize(DEVICE(s));
- g_free(pci->name[i]);
}
}
@@ -108,9 +106,6 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
return;
}
s->irq = &pci->irqs[i];
- pci->name[i] = g_strdup_printf("uart #%zu", i + 1);
- memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s,
- pci->name[i], 8);
memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
pci->ports++;
}
diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c
index d8cacc9085..9a0bf2d890 100644
--- a/hw/char/serial-pci.c
+++ b/hw/char/serial-pci.c
@@ -56,7 +56,6 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp)
pci->dev.config[PCI_INTERRUPT_PIN] = 1;
s->irq = pci_allocate_irq(&pci->dev);
- memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8);
pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
}
diff --git a/hw/char/serial.c b/hw/char/serial.c
index 0f3469a1e8..49227830e1 100644
--- a/hw/char/serial.c
+++ b/hw/char/serial.c
@@ -921,10 +921,67 @@ static int serial_be_change(void *opaque)
return 0;
}
+static const MemoryRegionOps serial_io_ops = {
+ .read = serial_ioport_read,
+ .write = serial_ioport_write,
+ .valid = {
+ .unaligned = 1,
+ },
+ .impl = {
+ .min_access_size = 1,
+ .max_access_size = 1,
+ },
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static uint64_t serial_mm_read(void *opaque, hwaddr addr, unsigned size)
+{
+ SerialState *s = opaque;
+
+ return serial_ioport_read(s, addr >> s->regshift, 1);
+}
+
+static void serial_mm_write(void *opaque, hwaddr addr,
+ uint64_t value, unsigned size)
+{
+ SerialState *s = opaque;
+
+ serial_ioport_write(s, addr >> s->regshift, value & 0xff, 1);
+}
+
+static const MemoryRegionOps serial_mm_ops[] = {
+ [DEVICE_NATIVE_ENDIAN] = {
+ .read = serial_mm_read,
+ .write = serial_mm_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .valid.max_access_size = 8,
+ .impl.max_access_size = 8,
+ },
+ [DEVICE_LITTLE_ENDIAN] = {
+ .read = serial_mm_read,
+ .write = serial_mm_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid.max_access_size = 8,
+ .impl.max_access_size = 8,
+ },
+ [DEVICE_BIG_ENDIAN] = {
+ .read = serial_mm_read,
+ .write = serial_mm_write,
+ .endianness = DEVICE_BIG_ENDIAN,
+ .valid.max_access_size = 8,
+ .impl.max_access_size = 8,
+ },
+};
+
static void serial_realize(DeviceState *dev, Error **errp)
{
SerialState *s = SERIAL(dev);
+ memory_region_init_io(&s->io, OBJECT(s),
+ s->regshift ? &serial_mm_ops[s->endianness]
+ : &serial_io_ops,
+ s, "serial", 8 << s->regshift);
+
s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s);
s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s);
@@ -952,23 +1009,16 @@ static void serial_unrealize(DeviceState *dev)
qemu_unregister_reset(serial_reset, s);
}
-const MemoryRegionOps serial_io_ops = {
- .read = serial_ioport_read,
- .write = serial_ioport_write,
- .valid = {
- .unaligned = 1,
- },
- .impl = {
- .min_access_size = 1,
- .max_access_size = 1,
- },
- .endianness = DEVICE_LITTLE_ENDIAN,
-};
-
static const Property serial_properties[] = {
DEFINE_PROP_CHR("chardev", SerialState, chr),
DEFINE_PROP_UINT32("baudbase", SerialState, baudbase, 115200),
DEFINE_PROP_BOOL("wakeup", SerialState, wakeup, false),
+ /*
+ * Set the spacing between adjacent memory-mapped UART registers.
+ * Each register will be at (1 << regshift) bytes after the previous one.
+ */
+ DEFINE_PROP_UINT8("regshift", SerialState, regshift, 0),
+ DEFINE_PROP_UINT8("endianness", SerialState, endianness, DEVICE_NATIVE_ENDIAN),
};
static void serial_class_init(ObjectClass *klass, const void *data)
--
2.53.0
Am 5. März 2026 22:09:09 UTC schrieb Bernhard Beschow <shentey@gmail.com>:
>Rather than requiring users of TYPE_SERIAL to initialize the MMIO region
>themselves, make it generic enough to be configured via properties. This
>makes TYPE_SERIAL more self-contained and prepares it for being turned
>into a SysBusDevice.
>
>Note that for TYPE_SERIAL_MM, if the regshift property has the default value
>zero, serial_io_ops rather than serial_mm_ops is now used. This seems to be
>fine since in both cases, registers are one byte wide and there are no gaps
>between registers. Therefore, neither the endianness nor the access size
>should make a difference.
Hmm, the access size of 1 is now actually stricter. Before, it was possible to read or write all registers at once with one 64 bit access. I wonder if this is used or useful in practice or if the access size of 8 was chosen to avoid a combinatoric explosion of MemoryRegionOps.
Should we make a bet here and fix regressions, if any, as they happen to avoid "cargo culting"?
Best regards,
Bernhard
> Running `make check` and `make check-functional`
>seems to confirm this.
>
>Signed-off-by: Bernhard Beschow <shentey@gmail.com>
>---
> include/hw/char/serial-mm.h | 3 --
> include/hw/char/serial.h | 4 +-
> hw/char/diva-gsp.c | 5 ---
> hw/char/serial-isa.c | 1 -
> hw/char/serial-mm.c | 51 -------------------------
> hw/char/serial-pci-multi.c | 5 ---
> hw/char/serial-pci.c | 1 -
> hw/char/serial.c | 76 ++++++++++++++++++++++++++++++-------
> 8 files changed, 66 insertions(+), 80 deletions(-)
>
>diff --git a/include/hw/char/serial-mm.h b/include/hw/char/serial-mm.h
>index 0076bdc061..4c18e2a609 100644
>--- a/include/hw/char/serial-mm.h
>+++ b/include/hw/char/serial-mm.h
>@@ -39,9 +39,6 @@ struct SerialMM {
> SysBusDevice parent;
>
> SerialState serial;
>-
>- uint8_t regshift;
>- uint8_t endianness;
> };
>
> SerialMM *serial_mm_init(MemoryRegion *address_space,
>diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h
>index ea82ffac47..0cf641a860 100644
>--- a/include/hw/char/serial.h
>+++ b/include/hw/char/serial.h
>@@ -62,6 +62,9 @@ struct SerialState {
> guint watch_tag;
> bool wakeup;
>
>+ uint8_t regshift;
>+ uint8_t endianness;
>+
> /* Time when the last byte was successfully sent out of the tsr */
> uint64_t last_xmit_ts;
> Fifo8 recv_fifo;
>@@ -80,7 +83,6 @@ struct SerialState {
> };
>
> extern const VMStateDescription vmstate_serial;
>-extern const MemoryRegionOps serial_io_ops;
>
> #define TYPE_SERIAL "serial"
> OBJECT_DECLARE_SIMPLE_TYPE(SerialState, SERIAL)
>diff --git a/hw/char/diva-gsp.c b/hw/char/diva-gsp.c
>index f9aa6e326d..b0a2437c85 100644
>--- a/hw/char/diva-gsp.c
>+++ b/hw/char/diva-gsp.c
>@@ -47,7 +47,6 @@ typedef struct PCIDivaSerialState {
> MemoryRegion mailboxbar; /* for hardware mailbox */
> uint32_t subvendor;
> uint32_t ports;
>- char *name[PCI_SERIAL_MAX_PORTS];
> SerialState state[PCI_SERIAL_MAX_PORTS];
> uint32_t level[PCI_SERIAL_MAX_PORTS];
> qemu_irq *irqs;
>@@ -64,7 +63,6 @@ static void diva_pci_exit(PCIDevice *dev)
> s = pci->state + i;
> memory_region_del_subregion(&pci->membar, &s->io);
> qdev_unrealize(DEVICE(s));
>- g_free(pci->name[i]);
> }
> qemu_free_irqs(pci->irqs, pci->ports);
> }
>@@ -136,9 +134,6 @@ static void diva_pci_realize(PCIDevice *dev, Error **errp)
> return;
> }
> s->irq = pci->irqs[i];
>- pci->name[i] = g_strdup_printf("uart #%zu", i + 1);
>- memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s,
>- pci->name[i], 8);
>
> /* calculate offset of given port based on bitmask */
> while ((portmask & BIT(0)) == 0) {
>diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c
>index a4be0492c5..3a48b2495e 100644
>--- a/hw/char/serial-isa.c
>+++ b/hw/char/serial-isa.c
>@@ -80,7 +80,6 @@ static void serial_isa_realizefn(DeviceState *dev, Error **errp)
> qdev_realize(DEVICE(s), NULL, errp);
> qdev_set_legacy_instance_id(dev, isa->iobase, 3);
>
>- memory_region_init_io(&s->io, OBJECT(isa), &serial_io_ops, s, "serial", 8);
> isa_register_ioport(isadev, &s->io, isa->iobase);
> }
>
>diff --git a/hw/char/serial-mm.c b/hw/char/serial-mm.c
>index 0e0be26fa9..1dba4fc694 100644
>--- a/hw/char/serial-mm.c
>+++ b/hw/char/serial-mm.c
>@@ -30,44 +30,6 @@
> #include "qapi/error.h"
> #include "hw/core/qdev-properties.h"
>
>-static uint64_t serial_mm_read(void *opaque, hwaddr addr, unsigned size)
>-{
>- SerialMM *s = SERIAL_MM(opaque);
>- return serial_io_ops.read(&s->serial, addr >> s->regshift, 1);
>-}
>-
>-static void serial_mm_write(void *opaque, hwaddr addr,
>- uint64_t value, unsigned size)
>-{
>- SerialMM *s = SERIAL_MM(opaque);
>- value &= 255;
>- serial_io_ops.write(&s->serial, addr >> s->regshift, value, 1);
>-}
>-
>-static const MemoryRegionOps serial_mm_ops[] = {
>- [DEVICE_NATIVE_ENDIAN] = {
>- .read = serial_mm_read,
>- .write = serial_mm_write,
>- .endianness = DEVICE_NATIVE_ENDIAN,
>- .valid.max_access_size = 8,
>- .impl.max_access_size = 8,
>- },
>- [DEVICE_LITTLE_ENDIAN] = {
>- .read = serial_mm_read,
>- .write = serial_mm_write,
>- .endianness = DEVICE_LITTLE_ENDIAN,
>- .valid.max_access_size = 8,
>- .impl.max_access_size = 8,
>- },
>- [DEVICE_BIG_ENDIAN] = {
>- .read = serial_mm_read,
>- .write = serial_mm_write,
>- .endianness = DEVICE_BIG_ENDIAN,
>- .valid.max_access_size = 8,
>- .impl.max_access_size = 8,
>- },
>-};
>-
> static void serial_mm_realize(DeviceState *dev, Error **errp)
> {
> SerialMM *smm = SERIAL_MM(dev);
>@@ -77,9 +39,6 @@ static void serial_mm_realize(DeviceState *dev, Error **errp)
> return;
> }
>
>- memory_region_init_io(&s->io, OBJECT(dev),
>- &serial_mm_ops[smm->endianness], smm, "serial",
>- 8 << smm->regshift);
> sysbus_init_mmio(SYS_BUS_DEVICE(smm), &s->io);
> sysbus_init_irq(SYS_BUS_DEVICE(smm), &smm->serial.irq);
> }
>@@ -125,20 +84,10 @@ static void serial_mm_instance_init(Object *o)
> qdev_alias_all_properties(DEVICE(&smm->serial), o);
> }
>
>-static const Property serial_mm_properties[] = {
>- /*
>- * Set the spacing between adjacent memory-mapped UART registers.
>- * Each register will be at (1 << regshift) bytes after the previous one.
>- */
>- DEFINE_PROP_UINT8("regshift", SerialMM, regshift, 0),
>- DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN),
>-};
>-
> static void serial_mm_class_init(ObjectClass *oc, const void *data)
> {
> DeviceClass *dc = DEVICE_CLASS(oc);
>
>- device_class_set_props(dc, serial_mm_properties);
> dc->realize = serial_mm_realize;
> dc->vmsd = &vmstate_serial_mm;
> }
>diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c
>index 7782452018..eda0d43bcf 100644
>--- a/hw/char/serial-pci-multi.c
>+++ b/hw/char/serial-pci-multi.c
>@@ -42,7 +42,6 @@ typedef struct PCIMultiSerialState {
> PCIDevice dev;
> MemoryRegion iobar;
> uint32_t ports;
>- char *name[PCI_SERIAL_MAX_PORTS];
> SerialState state[PCI_SERIAL_MAX_PORTS];
> uint32_t level[PCI_SERIAL_MAX_PORTS];
> IRQState irqs[PCI_SERIAL_MAX_PORTS];
>@@ -58,7 +57,6 @@ static void multi_serial_pci_exit(PCIDevice *dev)
> s = pci->state + i;
> memory_region_del_subregion(&pci->iobar, &s->io);
> qdev_unrealize(DEVICE(s));
>- g_free(pci->name[i]);
> }
> }
>
>@@ -108,9 +106,6 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp)
> return;
> }
> s->irq = &pci->irqs[i];
>- pci->name[i] = g_strdup_printf("uart #%zu", i + 1);
>- memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s,
>- pci->name[i], 8);
> memory_region_add_subregion(&pci->iobar, 8 * i, &s->io);
> pci->ports++;
> }
>diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c
>index d8cacc9085..9a0bf2d890 100644
>--- a/hw/char/serial-pci.c
>+++ b/hw/char/serial-pci.c
>@@ -56,7 +56,6 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp)
> pci->dev.config[PCI_INTERRUPT_PIN] = 1;
> s->irq = pci_allocate_irq(&pci->dev);
>
>- memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8);
> pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io);
> }
>
>diff --git a/hw/char/serial.c b/hw/char/serial.c
>index 0f3469a1e8..49227830e1 100644
>--- a/hw/char/serial.c
>+++ b/hw/char/serial.c
>@@ -921,10 +921,67 @@ static int serial_be_change(void *opaque)
> return 0;
> }
>
>+static const MemoryRegionOps serial_io_ops = {
>+ .read = serial_ioport_read,
>+ .write = serial_ioport_write,
>+ .valid = {
>+ .unaligned = 1,
>+ },
>+ .impl = {
>+ .min_access_size = 1,
>+ .max_access_size = 1,
>+ },
>+ .endianness = DEVICE_LITTLE_ENDIAN,
>+};
>+
>+static uint64_t serial_mm_read(void *opaque, hwaddr addr, unsigned size)
>+{
>+ SerialState *s = opaque;
>+
>+ return serial_ioport_read(s, addr >> s->regshift, 1);
>+}
>+
>+static void serial_mm_write(void *opaque, hwaddr addr,
>+ uint64_t value, unsigned size)
>+{
>+ SerialState *s = opaque;
>+
>+ serial_ioport_write(s, addr >> s->regshift, value & 0xff, 1);
>+}
>+
>+static const MemoryRegionOps serial_mm_ops[] = {
>+ [DEVICE_NATIVE_ENDIAN] = {
>+ .read = serial_mm_read,
>+ .write = serial_mm_write,
>+ .endianness = DEVICE_NATIVE_ENDIAN,
>+ .valid.max_access_size = 8,
>+ .impl.max_access_size = 8,
>+ },
>+ [DEVICE_LITTLE_ENDIAN] = {
>+ .read = serial_mm_read,
>+ .write = serial_mm_write,
>+ .endianness = DEVICE_LITTLE_ENDIAN,
>+ .valid.max_access_size = 8,
>+ .impl.max_access_size = 8,
>+ },
>+ [DEVICE_BIG_ENDIAN] = {
>+ .read = serial_mm_read,
>+ .write = serial_mm_write,
>+ .endianness = DEVICE_BIG_ENDIAN,
>+ .valid.max_access_size = 8,
>+ .impl.max_access_size = 8,
>+ },
>+};
>+
> static void serial_realize(DeviceState *dev, Error **errp)
> {
> SerialState *s = SERIAL(dev);
>
>+ memory_region_init_io(&s->io, OBJECT(s),
>+ s->regshift ? &serial_mm_ops[s->endianness]
>+ : &serial_io_ops,
>+ s, "serial", 8 << s->regshift);
>+
> s->modem_status_poll = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) serial_update_msl, s);
>
> s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, (QEMUTimerCB *) fifo_timeout_int, s);
>@@ -952,23 +1009,16 @@ static void serial_unrealize(DeviceState *dev)
> qemu_unregister_reset(serial_reset, s);
> }
>
>-const MemoryRegionOps serial_io_ops = {
>- .read = serial_ioport_read,
>- .write = serial_ioport_write,
>- .valid = {
>- .unaligned = 1,
>- },
>- .impl = {
>- .min_access_size = 1,
>- .max_access_size = 1,
>- },
>- .endianness = DEVICE_LITTLE_ENDIAN,
>-};
>-
> static const Property serial_properties[] = {
> DEFINE_PROP_CHR("chardev", SerialState, chr),
> DEFINE_PROP_UINT32("baudbase", SerialState, baudbase, 115200),
> DEFINE_PROP_BOOL("wakeup", SerialState, wakeup, false),
>+ /*
>+ * Set the spacing between adjacent memory-mapped UART registers.
>+ * Each register will be at (1 << regshift) bytes after the previous one.
>+ */
>+ DEFINE_PROP_UINT8("regshift", SerialState, regshift, 0),
>+ DEFINE_PROP_UINT8("endianness", SerialState, endianness, DEVICE_NATIVE_ENDIAN),
> };
>
> static void serial_class_init(ObjectClass *klass, const void *data)
© 2016 - 2026 Red Hat, Inc.