diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-13 10:35:25 +0800 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2025-03-13 10:35:25 +0800 |
commit | 4c33c097f3a8a8093bcbaf097c3a178051e51b3e (patch) | |
tree | 0d0e98d75bc5cd760255e0e333ea0001a96eb4fc | |
parent | 74b3445378f7d9be35b7a77757bb568080a6f929 (diff) | |
parent | 7f2a5272ff3893ef412c093aae66b7ed34ab3dfc (diff) | |
download | qemu-4c33c097f3a8a8093bcbaf097c3a178051e51b3e.zip qemu-4c33c097f3a8a8093bcbaf097c3a178051e51b3e.tar.gz qemu-4c33c097f3a8a8093bcbaf097c3a178051e51b3e.tar.bz2 |
Merge tag 'hw-misc-20250312' of https://github.com/philmd/qemu into staging
Misc HW patches
- Set correct values for MPC8569E's eSDHC (Zoltan)
- Emulate Ricoh RS5C372 RTC device (Bernhard)
- Array overflow fixes in SMSC91C111 netdev (Peter)
- Fix typo in Xen HVM (Philippe)
- Move graphic height/width/depth globals to their own file (Philippe)
- Introduce qemu_arch_available() helper (Philippe)
- Check fw_cfg's ACPI availability at runtime (Philippe)
- Remove virtio-mem dependency on CONFIG_DEVICES (Philippe)
- Sort HyperV SYNDBG API definitions (Pierrick)
- Remove need for SDHCI_VENDOR_FSL definition (Philippe)
# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmfRXiMACgkQ4+MsLN6t
# wN5zFhAAzSW/hZneD8hycKtr9nBlvZSD72cEt+b656OCbTyyucUi1sG4rMPMvHeW
# h6HP6xt2SfQxXbec6Y0pWxWUkBOQzk72s0zpttOED3oEspkrId2D+VSsSH1E+QLh
# WoG7/hVgz0bDHexWYIDdGufO4no/icwewAKmC5Kp2HbaNxIIHyWlK1+RO69/lCLN
# s3qkNesMsQyEWN28ogEMRqyCIG3oJVP76U4TVcdxIiE51WI8sP8/7V2um0AXN68m
# IV3INrfVJjGDp501elrUbD3qsYopRdxoMAvwiVojrLXin6xtS+SQjEe/hcNxzM70
# 0IQPp9WWwLjNkeFlAJF4wpwGJttFNHj+5gtH7/YRrP75jt9kAxPXkFw/OFfpVd30
# NYbeFlWDhRL1QPBs+WPBZTrfD7fRmpfMJRLF3/w61+WvnVrshlyDaoCWbR+L329F
# uOQFsBdAD7m/lkZ0mHtskS2vkZx7Itn1av4gql7T7/6cE1R7ItKy1HY9UUCtY6Gp
# 7V6XrsAE3khg2HY8IcJ73+sPLQn/GxqZFE7PqmAhgcl6RZEFQv8PNrEgFxCEYyuK
# KJjx0hRMLoigp0CEclLfOqz2d3knsI8SJbgD4iTYQc02E69lx8a4XS4N8JXoLEdh
# 3i/ndwKEFmzwNuqbU0nYsSJDiAO9ejra8O2BXZS/a4pkxC2jtdw=
# =VVr6
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 12 Mar 2025 18:12:51 HKT
# gpg: using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]
# Primary key fingerprint: FAAB E75E 1291 7221 DCFD 6BB2 E3E3 2C2C DEAD C0DE
* tag 'hw-misc-20250312' of https://github.com/philmd/qemu:
hw/sd/sdhci: Remove need for SDHCI_VENDOR_IMX definition
hw/hyperv/hyperv-proto: Move SYNDBG definitions from target/i386
hw/virtio/virtio-mem: Remove CONFIG_DEVICES include
hw/i386/fw_cfg: Check ACPI availability with acpi_builtin()
hw/acpi: Introduce acpi_builtin() helper
system: Replace arch_type global by qemu_arch_available() helper
system: Extract target-specific globals to their own compilation unit
hw/xen/hvm: Fix Aarch64 typo
hw/net/smc91c111: Don't allow data register access to overrun buffer
hw/net/smc91c111: Use MAX_PACKET_SIZE instead of magic numbers
hw/net/smc91c111: Sanitize packet length on tx
hw/net/smc91c111: Sanitize packet numbers
hw/rtc: Add Ricoh RS5C372 RTC emulation
hw/sd/sdhci: Set reset value of interrupt registers
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r-- | MAINTAINERS | 2 | ||||
-rw-r--r-- | hw/acpi/acpi-stub.c | 8 | ||||
-rw-r--r-- | hw/acpi/core.c | 5 | ||||
-rw-r--r-- | hw/arm/fsl-imx25.c | 2 | ||||
-rw-r--r-- | hw/arm/fsl-imx6.c | 2 | ||||
-rw-r--r-- | hw/arm/fsl-imx6ul.c | 2 | ||||
-rw-r--r-- | hw/arm/fsl-imx7.c | 2 | ||||
-rw-r--r-- | hw/arm/fsl-imx8mp.c | 2 | ||||
-rw-r--r-- | hw/i386/fw_cfg.c | 8 | ||||
-rw-r--r-- | hw/net/smc91c111.c | 148 | ||||
-rw-r--r-- | hw/ppc/e500.c | 1 | ||||
-rw-r--r-- | hw/rtc/Kconfig | 5 | ||||
-rw-r--r-- | hw/rtc/meson.build | 1 | ||||
-rw-r--r-- | hw/rtc/rs5c372.c | 236 | ||||
-rw-r--r-- | hw/rtc/trace-events | 4 | ||||
-rw-r--r-- | hw/scsi/scsi-disk.c | 2 | ||||
-rw-r--r-- | hw/sd/sdhci.c | 18 | ||||
-rw-r--r-- | hw/virtio/virtio-mem.c | 6 | ||||
-rw-r--r-- | include/hw/acpi/acpi.h | 3 | ||||
-rw-r--r-- | include/hw/hyperv/hyperv-proto.h | 12 | ||||
-rw-r--r-- | include/hw/sd/sdhci.h | 2 | ||||
-rw-r--r-- | include/hw/xen/arch_hvm.h | 2 | ||||
-rw-r--r-- | include/system/arch_init.h | 2 | ||||
-rw-r--r-- | system/arch_init.c | 19 | ||||
-rw-r--r-- | system/globals-target.c | 24 | ||||
-rw-r--r-- | system/meson.build | 1 | ||||
-rw-r--r-- | system/qdev-monitor.c | 4 | ||||
-rw-r--r-- | system/vl.c | 6 | ||||
-rw-r--r-- | target/i386/kvm/hyperv-proto.h | 12 | ||||
-rw-r--r-- | tests/qtest/meson.build | 1 | ||||
-rw-r--r-- | tests/qtest/rs5c372-test.c | 43 |
31 files changed, 503 insertions, 82 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 3465847..31b395f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -827,10 +827,12 @@ F: hw/arm/imx8mp-evk.c F: hw/arm/fsl-imx8mp.c F: hw/misc/imx8mp_*.c F: hw/pci-host/fsl_imx8m_phy.c +F: hw/rtc/rs5c372.c F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h F: include/hw/pci-host/fsl_imx8m_phy.h F: docs/system/arm/imx8mp-evk.rst +F: tests/qtest/rs5c372-test.c MPS2 / MPS3 M: Peter Maydell <peter.maydell@linaro.org> diff --git a/hw/acpi/acpi-stub.c b/hw/acpi/acpi-stub.c index e268ce9..fd0b62f 100644 --- a/hw/acpi/acpi-stub.c +++ b/hw/acpi/acpi-stub.c @@ -21,7 +21,15 @@ #include "qemu/osdep.h" #include "hw/acpi/acpi.h" +char unsigned *acpi_tables; +size_t acpi_tables_len; + void acpi_table_add(const QemuOpts *opts, Error **errp) { g_assert_not_reached(); } + +bool acpi_builtin(void) +{ + return false; +} diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 870391e..58f8964 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -78,6 +78,11 @@ static void acpi_register_config(void) opts_init(acpi_register_config); +bool acpi_builtin(void) +{ + return true; +} + static int acpi_checksum(const uint8_t *data, int len) { int sum, i; diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 5359a6d..02214ca 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -243,8 +243,6 @@ static void fsl_imx25_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_uint(OBJECT(&s->esdhc[i]), "capareg", IMX25_ESDHC_CAPABILITIES, &error_abort); - object_property_set_uint(OBJECT(&s->esdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->esdhc[i]), errp)) { return; } diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index dc86338..a114dc0 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -327,8 +327,6 @@ static void fsl_imx6_realize(DeviceState *dev, Error **errp) &error_abort); object_property_set_uint(OBJECT(&s->esdhc[i]), "capareg", IMX6_ESDHC_CAPABILITIES, &error_abort); - object_property_set_uint(OBJECT(&s->esdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->esdhc[i]), errp)) { return; } diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index 34c4aa1..ce8d3ef 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -531,8 +531,6 @@ static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) FSL_IMX6UL_USDHC2_IRQ, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index 3374018..ed1f10b 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -471,8 +471,6 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) FSL_IMX7_USDHC3_IRQ, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 1ea98e1..c3f6da6 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -524,8 +524,6 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) { fsl_imx8mp_memmap[FSL_IMX8MP_USDHC3].addr, FSL_IMX8MP_USDHC3_IRQ }, }; - object_property_set_uint(OBJECT(&s->usdhc[i]), "vendor", - SDHCI_VENDOR_IMX, &error_abort); if (!sysbus_realize(SYS_BUS_DEVICE(&s->usdhc[i]), errp)) { return; } diff --git a/hw/i386/fw_cfg.c b/hw/i386/fw_cfg.c index d08aefa..a7f1b60 100644 --- a/hw/i386/fw_cfg.c +++ b/hw/i386/fw_cfg.c @@ -145,10 +145,10 @@ FWCfgState *fw_cfg_arch_create(MachineState *ms, */ fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, apic_id_limit); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, ms->ram_size); -#ifdef CONFIG_ACPI - fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, - acpi_tables, acpi_tables_len); -#endif + if (acpi_builtin()) { + fw_cfg_add_bytes(fw_cfg, FW_CFG_ACPI_TABLES, + acpi_tables, acpi_tables_len); + } fw_cfg_add_i32(fw_cfg, FW_CFG_IRQ0_OVERRIDE, 1); fw_cfg_add_bytes(fw_cfg, FW_CFG_HPET, &hpet_fw_cfg, sizeof(hpet_fw_cfg)); diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 0e13dfa..9ce42b5 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -13,6 +13,7 @@ #include "net/net.h" #include "hw/irq.h" #include "hw/net/smc91c111.h" +#include "hw/registerfields.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/log.h" @@ -22,6 +23,13 @@ /* Number of 2k memory pages available. */ #define NUM_PACKETS 4 +/* + * Maximum size of a data frame, including the leading status word + * and byte count fields and the trailing CRC, last data byte + * and control byte (per figure 8-1 in the Microchip Technology + * LAN91C111 datasheet). + */ +#define MAX_PACKET_SIZE 2048 #define TYPE_SMC91C111 "smc91c111" OBJECT_DECLARE_SIMPLE_TYPE(smc91c111_state, SMC91C111) @@ -51,7 +59,7 @@ struct smc91c111_state { int tx_fifo_done_len; int tx_fifo_done[NUM_PACKETS]; /* Packet buffer memory. */ - uint8_t data[NUM_PACKETS][2048]; + uint8_t data[NUM_PACKETS][MAX_PACKET_SIZE]; uint8_t int_level; uint8_t int_mask; MemoryRegion mmio; @@ -79,7 +87,8 @@ static const VMStateDescription vmstate_smc91c111 = { VMSTATE_INT32_ARRAY(rx_fifo, smc91c111_state, NUM_PACKETS), VMSTATE_INT32(tx_fifo_done_len, smc91c111_state), VMSTATE_INT32_ARRAY(tx_fifo_done, smc91c111_state, NUM_PACKETS), - VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, NUM_PACKETS * 2048), + VMSTATE_BUFFER_UNSAFE(data, smc91c111_state, 0, + NUM_PACKETS * MAX_PACKET_SIZE), VMSTATE_UINT8(int_level, smc91c111_state), VMSTATE_UINT8(int_mask, smc91c111_state), VMSTATE_END_OF_LIST() @@ -118,6 +127,18 @@ static const VMStateDescription vmstate_smc91c111 = { #define RS_TOOSHORT 0x0400 #define RS_MULTICAST 0x0001 +FIELD(PTR, PTR, 0, 11) +FIELD(PTR, NOT_EMPTY, 11, 1) +FIELD(PTR, RESERVED, 12, 1) +FIELD(PTR, READ, 13, 1) +FIELD(PTR, AUTOINCR, 14, 1) +FIELD(PTR, RCV, 15, 1) + +static inline bool packetnum_valid(int packet_num) +{ + return packet_num >= 0 && packet_num < NUM_PACKETS; +} + /* Update interrupt status. */ static void smc91c111_update(smc91c111_state *s) { @@ -218,12 +239,33 @@ static void smc91c111_pop_tx_fifo_done(smc91c111_state *s) /* Release the memory allocated to a packet. */ static void smc91c111_release_packet(smc91c111_state *s, int packet) { + if (!packetnum_valid(packet)) { + /* + * Data sheet doesn't document behaviour in this guest error + * case, and there is no error status register to report it. + * Log and ignore the attempt. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to release invalid packet %d\n", + packet); + return; + } s->allocated &= ~(1 << packet); if (s->tx_alloc == 0x80) smc91c111_tx_alloc(s); smc91c111_flush_queued_packets(s); } +static void smc91c111_complete_tx_packet(smc91c111_state *s, int packetnum) +{ + if (s->ctr & CTR_AUTO_RELEASE) { + /* Race? */ + smc91c111_release_packet(s, packetnum); + } else if (s->tx_fifo_done_len < NUM_PACKETS) { + s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + } +} + /* Flush the TX FIFO. */ static void smc91c111_do_tx(smc91c111_state *s) { @@ -239,12 +281,25 @@ static void smc91c111_do_tx(smc91c111_state *s) return; for (i = 0; i < s->tx_fifo_len; i++) { packetnum = s->tx_fifo[i]; + /* queue_tx checked the packet number was valid */ + assert(packetnum_valid(packetnum)); p = &s->data[packetnum][0]; /* Set status word. */ *(p++) = 0x01; *(p++) = 0x40; len = *(p++); len |= ((int)*(p++)) << 8; + if (len > MAX_PACKET_SIZE) { + /* + * Datasheet doesn't say what to do here, and there is no + * relevant tx error condition listed. Log, and drop the packet. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: tx packet with bad length %d, dropping\n", + len); + smc91c111_complete_tx_packet(s, packetnum); + continue; + } len -= 6; control = p[len + 1]; if (control & 0x20) @@ -273,11 +328,7 @@ static void smc91c111_do_tx(smc91c111_state *s) } } #endif - if (s->ctr & CTR_AUTO_RELEASE) - /* Race? */ - smc91c111_release_packet(s, packetnum); - else if (s->tx_fifo_done_len < NUM_PACKETS) - s->tx_fifo_done[s->tx_fifo_done_len++] = packetnum; + smc91c111_complete_tx_packet(s, packetnum); qemu_send_packet(qemu_get_queue(s->nic), p, len); } s->tx_fifo_len = 0; @@ -287,6 +338,17 @@ static void smc91c111_do_tx(smc91c111_state *s) /* Add a packet to the TX FIFO. */ static void smc91c111_queue_tx(smc91c111_state *s, int packet) { + if (!packetnum_valid(packet)) { + /* + * Datasheet doesn't document behaviour in this error case, and + * there's no error status register we could report it in. + * Log and ignore. + */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to queue invalid packet %d\n", + packet); + return; + } if (s->tx_fifo_len == NUM_PACKETS) return; s->tx_fifo[s->tx_fifo_len++] = packet; @@ -318,6 +380,49 @@ static void smc91c111_reset(DeviceState *dev) #define SET_LOW(name, val) s->name = (s->name & 0xff00) | val #define SET_HIGH(name, val) s->name = (s->name & 0xff) | (val << 8) +/* + * The pointer register's pointer is an 11 bit value (so it exactly + * indexes a 2048-byte data frame). Add the specified offset to it, + * wrapping around at the 2048 byte mark, and return the resulting + * wrapped value. There are flag bits in the top part of the register, + * but we can ignore them here as the mask will mask them out. + */ +static int ptr_reg_add(smc91c111_state *s, int offset) +{ + return (s->ptr + offset) & R_PTR_PTR_MASK; +} + +/* + * For an access to the Data Register at @offset, return the + * required offset into the packet's data frame. This will + * perform the pointer register autoincrement if required, and + * guarantees to return an in-bounds offset. + */ +static int data_reg_ptr(smc91c111_state *s, int offset) +{ + int p; + + if (s->ptr & R_PTR_AUTOINCR_MASK) { + /* + * Autoincrement: use the current pointer value, and + * increment the pointer register's pointer field. + */ + p = FIELD_EX32(s->ptr, PTR, PTR); + s->ptr = FIELD_DP32(s->ptr, PTR, PTR, ptr_reg_add(s, 1)); + } else { + /* + * No autoincrement: register offset determines which + * byte we're addressing. Setting the pointer to the top + * of the data buffer and then using the pointer wrapping + * to read the bottom byte of the buffer is not something + * sensible guest software will do, but the datasheet + * doesn't say what the behaviour is, so we don't forbid it. + */ + p = ptr_reg_add(s, offset & 3); + } + return p; +} + static void smc91c111_writeb(void *opaque, hwaddr offset, uint32_t value) { @@ -457,12 +562,14 @@ static void smc91c111_writeb(void *opaque, hwaddr offset, n = s->rx_fifo[0]; else n = s->packet_num; - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x7ff); - } else { - p += (offset & 3); + if (!packetnum_valid(n)) { + /* Datasheet doesn't document what to do here */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to write data to invalid packet %d\n", + n); + return; } + p = data_reg_ptr(s, offset); s->data[n][p] = value; } return; @@ -605,12 +712,14 @@ static uint32_t smc91c111_readb(void *opaque, hwaddr offset) n = s->rx_fifo[0]; else n = s->packet_num; - p = s->ptr & 0x07ff; - if (s->ptr & 0x4000) { - s->ptr = (s->ptr & 0xf800) | ((s->ptr + 1) & 0x07ff); - } else { - p += (offset & 3); + if (!packetnum_valid(n)) { + /* Datasheet doesn't document what to do here */ + qemu_log_mask(LOG_GUEST_ERROR, + "smc91c111: attempt to read data from invalid packet %d\n", + n); + return 0; } + p = data_reg_ptr(s, offset); return s->data[n][p]; } case 12: /* Interrupt status. */ @@ -706,13 +815,16 @@ static ssize_t smc91c111_receive(NetClientState *nc, const uint8_t *buf, size_t if (crc) packetsize += 4; /* TODO: Flag overrun and receive errors. */ - if (packetsize > 2048) + if (packetsize > MAX_PACKET_SIZE) { return -1; + } packetnum = smc91c111_allocate_packet(s); if (packetnum == 0x80) return -1; s->rx_fifo[s->rx_fifo_len++] = packetnum; + /* allocate_packet() will not hand us back an invalid packet number */ + assert(packetnum_valid(packetnum)); p = &s->data[packetnum][0]; /* ??? Multicast packets? */ status = 0; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index fe8b9f7..69269aa 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -1043,6 +1043,7 @@ void ppce500_init(MachineState *machine) dev = qdev_new(TYPE_SYSBUS_SDHCI); qdev_prop_set_uint8(dev, "sd-spec-version", 2); qdev_prop_set_uint8(dev, "endianness", DEVICE_BIG_ENDIAN); + qdev_prop_set_uint8(dev, "vendor", SDHCI_VENDOR_FSL); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); sysbus_connect_irq(s, 0, qdev_get_gpio_in(mpicdev, MPC85XX_ESDHC_IRQ)); diff --git a/hw/rtc/Kconfig b/hw/rtc/Kconfig index 2fe04ec..315b0e4 100644 --- a/hw/rtc/Kconfig +++ b/hw/rtc/Kconfig @@ -26,3 +26,8 @@ config GOLDFISH_RTC config LS7A_RTC bool + +config RS5C372_RTC + bool + depends on I2C + default y if I2C_DEVICES diff --git a/hw/rtc/meson.build b/hw/rtc/meson.build index 8ecc2d7..6c87864 100644 --- a/hw/rtc/meson.build +++ b/hw/rtc/meson.build @@ -13,3 +13,4 @@ system_ss.add(when: 'CONFIG_GOLDFISH_RTC', if_true: files('goldfish_rtc.c')) system_ss.add(when: 'CONFIG_LS7A_RTC', if_true: files('ls7a_rtc.c')) system_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-rtc.c')) system_ss.add(when: 'CONFIG_MC146818RTC', if_true: files('mc146818rtc.c')) +system_ss.add(when: 'CONFIG_RS5C372_RTC', if_true: files('rs5c372.c')) diff --git a/hw/rtc/rs5c372.c b/hw/rtc/rs5c372.c new file mode 100644 index 0000000..5542f74 --- /dev/null +++ b/hw/rtc/rs5c372.c @@ -0,0 +1,236 @@ +/* + * Ricoh RS5C372, R222x I2C RTC + * + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * Based on hw/rtc/ds1338.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/i2c/i2c.h" +#include "hw/qdev-properties.h" +#include "hw/resettable.h" +#include "migration/vmstate.h" +#include "qemu/bcd.h" +#include "qom/object.h" +#include "system/rtc.h" +#include "trace.h" + +#define NVRAM_SIZE 0x10 + +/* Flags definitions */ +#define SECONDS_CH 0x80 +#define HOURS_PM 0x20 +#define CTRL2_24 0x20 + +#define TYPE_RS5C372 "rs5c372" +OBJECT_DECLARE_SIMPLE_TYPE(RS5C372State, RS5C372) + +struct RS5C372State { + I2CSlave parent_obj; + + int64_t offset; + uint8_t wday_offset; + uint8_t nvram[NVRAM_SIZE]; + uint8_t ptr; + uint8_t tx_format; + bool addr_byte; +}; + +static void capture_current_time(RS5C372State *s) +{ + /* + * Capture the current time into the secondary registers which will be + * actually read by the data transfer operation. + */ + struct tm now; + qemu_get_timedate(&now, s->offset); + s->nvram[0] = to_bcd(now.tm_sec); + s->nvram[1] = to_bcd(now.tm_min); + if (s->nvram[0xf] & CTRL2_24) { + s->nvram[2] = to_bcd(now.tm_hour); + } else { + int tmp = now.tm_hour; + if (tmp % 12 == 0) { + tmp += 12; + } + if (tmp <= 12) { + s->nvram[2] = to_bcd(tmp); + } else { + s->nvram[2] = HOURS_PM | to_bcd(tmp - 12); + } + } + s->nvram[3] = (now.tm_wday + s->wday_offset) % 7 + 1; + s->nvram[4] = to_bcd(now.tm_mday); + s->nvram[5] = to_bcd(now.tm_mon + 1); + s->nvram[6] = to_bcd(now.tm_year - 100); +} + +static void inc_regptr(RS5C372State *s) +{ + s->ptr = (s->ptr + 1) & (NVRAM_SIZE - 1); +} + +static int rs5c372_event(I2CSlave *i2c, enum i2c_event event) +{ + RS5C372State *s = RS5C372(i2c); + + switch (event) { + case I2C_START_RECV: + /* + * In h/w, capture happens on any START condition, not just a + * START_RECV, but there is no need to actually capture on + * START_SEND, because the guest can't get at that data + * without going through a START_RECV which would overwrite it. + */ + capture_current_time(s); + s->ptr = 0xf; + break; + case I2C_START_SEND: + s->addr_byte = true; + break; + default: + break; + } + + return 0; +} + +static uint8_t rs5c372_recv(I2CSlave *i2c) +{ + RS5C372State *s = RS5C372(i2c); + uint8_t res; + + res = s->nvram[s->ptr]; + + trace_rs5c372_recv(s->ptr, res); + + inc_regptr(s); + return res; +} + +static int rs5c372_send(I2CSlave *i2c, uint8_t data) +{ + RS5C372State *s = RS5C372(i2c); + + if (s->addr_byte) { + s->ptr = data >> 4; + s->tx_format = data & 0xf; + s->addr_byte = false; + return 0; + } + + trace_rs5c372_send(s->ptr, data); + + if (s->ptr < 7) { + /* Time register. */ + struct tm now; + qemu_get_timedate(&now, s->offset); + switch (s->ptr) { + case 0: + now.tm_sec = from_bcd(data & 0x7f); + break; + case 1: + now.tm_min = from_bcd(data & 0x7f); + break; + case 2: + if (s->nvram[0xf] & CTRL2_24) { + now.tm_hour = from_bcd(data & 0x3f); + } else { + int tmp = from_bcd(data & (HOURS_PM - 1)); + if (data & HOURS_PM) { + tmp += 12; + } + if (tmp % 12 == 0) { + tmp -= 12; + } + now.tm_hour = tmp; + } + break; + case 3: + { + /* + * The day field is supposed to contain a value in the range + * 1-7. Otherwise behavior is undefined. + */ + int user_wday = (data & 7) - 1; + s->wday_offset = (user_wday - now.tm_wday + 7) % 7; + } + break; + case 4: + now.tm_mday = from_bcd(data & 0x3f); + break; + case 5: + now.tm_mon = from_bcd(data & 0x1f) - 1; + break; + case 6: + now.tm_year = from_bcd(data) + 100; + break; + } + s->offset = qemu_timedate_diff(&now); + } else { + s->nvram[s->ptr] = data; + } + inc_regptr(s); + return 0; +} + +static void rs5c372_reset_hold(Object *obj, ResetType type) +{ + RS5C372State *s = RS5C372(obj); + + /* The clock is running and synchronized with the host */ + s->offset = 0; + s->wday_offset = 0; + memset(s->nvram, 0, NVRAM_SIZE); + s->ptr = 0; + s->addr_byte = false; +} + +static const VMStateDescription rs5c372_vmstate = { + .name = "rs5c372", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_I2C_SLAVE(parent_obj, RS5C372State), + VMSTATE_INT64(offset, RS5C372State), + VMSTATE_UINT8_V(wday_offset, RS5C372State, 2), + VMSTATE_UINT8_ARRAY(nvram, RS5C372State, NVRAM_SIZE), + VMSTATE_UINT8(ptr, RS5C372State), + VMSTATE_UINT8(tx_format, RS5C372State), + VMSTATE_BOOL(addr_byte, RS5C372State), + VMSTATE_END_OF_LIST() + } +}; + +static void rs5c372_init(Object *obj) +{ + qdev_prop_set_uint8(DEVICE(obj), "address", 0x32); +} + +static void rs5c372_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + k->event = rs5c372_event; + k->recv = rs5c372_recv; + k->send = rs5c372_send; + dc->vmsd = &rs5c372_vmstate; + rc->phases.hold = rs5c372_reset_hold; +} + +static const TypeInfo rs5c372_types[] = { + { + .name = TYPE_RS5C372, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(RS5C372State), + .instance_init = rs5c372_init, + .class_init = rs5c372_class_init, + }, +}; + +DEFINE_TYPES(rs5c372_types) diff --git a/hw/rtc/trace-events b/hw/rtc/trace-events index 8012afe..b9f2852 100644 --- a/hw/rtc/trace-events +++ b/hw/rtc/trace-events @@ -35,3 +35,7 @@ m48txx_nvram_mem_write(uint32_t addr, uint32_t value) "mem write addr:0x%04x val # goldfish_rtc.c goldfish_rtc_read(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 goldfish_rtc_write(uint64_t addr, uint64_t value) "addr 0x%02" PRIx64 " value 0x%08" PRIx64 + +# rs5c372.c +rs5c372_recv(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] -> 0x%02" PRIx8 +rs5c372_send(uint32_t addr, uint8_t value) "[0x%" PRIx32 "] <- 0x%02" PRIx8 diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e7f738b..7c87b20 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3165,7 +3165,7 @@ static void scsi_property_add_specifics(DeviceClass *dc) ObjectClass *oc = OBJECT_CLASS(dc); /* The loadparm property is only supported on s390x */ - if (arch_type & QEMU_ARCH_S390X) { + if (qemu_arch_available(QEMU_ARCH_S390X)) { object_class_property_add_str(oc, "loadparm", scsi_property_get_loadparm, scsi_property_set_loadparm); diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 1f45a77..69baf73 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -307,6 +307,10 @@ static void sdhci_reset(SDHCIState *s) s->data_count = 0; s->stopped_state = sdhc_not_stopped; s->pending_insert_state = false; + if (s->vendor == SDHCI_VENDOR_FSL) { + s->norintstsen = 0x013f; + s->errintstsen = 0x117f; + } } static void sdhci_poweron_reset(DeviceState *dev) @@ -1731,16 +1735,10 @@ usdhc_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) case USDHC_VENDOR_SPEC: s->vendor_spec = value; - switch (s->vendor) { - case SDHCI_VENDOR_IMX: - if (value & USDHC_IMX_FRC_SDCLK_ON) { - s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; - } else { - s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; - } - break; - default: - break; + if (value & USDHC_IMX_FRC_SDCLK_ON) { + s->prnsts &= ~SDHC_IMX_CLOCK_GATE_OFF; + } else { + s->prnsts |= SDHC_IMX_CLOCK_GATE_OFF; } break; diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 7b140ad..5f57ecc 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -28,7 +28,7 @@ #include "migration/misc.h" #include "hw/boards.h" #include "hw/qdev-properties.h" -#include CONFIG_DEVICES +#include "hw/acpi/acpi.h" #include "trace.h" static const VMStateDescription vmstate_virtio_mem_device_early; @@ -883,10 +883,8 @@ static uint64_t virtio_mem_get_features(VirtIODevice *vdev, uint64_t features, MachineState *ms = MACHINE(qdev_get_machine()); VirtIOMEM *vmem = VIRTIO_MEM(vdev); - if (ms->numa_state) { -#if defined(CONFIG_ACPI) + if (ms->numa_state && acpi_builtin()) { virtio_add_feature(&features, VIRTIO_MEM_F_ACPI_PXM); -#endif } assert(vmem->unplugged_inaccessible != ON_OFF_AUTO_AUTO); if (vmem->unplugged_inaccessible == ON_OFF_AUTO_ON) { diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index e0e51e8..d1a4fa2 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -150,6 +150,9 @@ struct ACPIREGS { Notifier wakeup; }; +/* Return whether ACPI subsystem is built in */ +bool acpi_builtin(void); + /* PM_TMR */ void acpi_pm_tmr_update(ACPIREGS *ar, bool enable); void acpi_pm_tmr_calc_overflow_time(ACPIREGS *ar); diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 4a22973..fffc5ce 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -62,6 +62,18 @@ #define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005 /* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 +#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + +/* * Message flags */ #define HV_MESSAGE_FLAG_PENDING 0x1 diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index 38c08e2..51fb30e 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -109,7 +109,7 @@ struct SDHCIState { typedef struct SDHCIState SDHCIState; #define SDHCI_VENDOR_NONE 0 -#define SDHCI_VENDOR_IMX 1 +#define SDHCI_VENDOR_FSL 2 /* * Controller does not provide transfer-complete interrupt when not diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h index c7c5152..df39c81 100644 --- a/include/hw/xen/arch_hvm.h +++ b/include/hw/xen/arch_hvm.h @@ -1,5 +1,5 @@ #if defined(TARGET_I386) || defined(TARGET_X86_64) #include "hw/i386/xen_arch_hvm.h" -#elif defined(TARGET_ARM) || defined(TARGET_ARM_64) +#elif defined(TARGET_ARM) || defined(TARGET_AARCH64) #include "hw/arm/xen_arch_hvm.h" #endif diff --git a/include/system/arch_init.h b/include/system/arch_init.h index d8b7744..51e24c3 100644 --- a/include/system/arch_init.h +++ b/include/system/arch_init.h @@ -25,6 +25,6 @@ enum { QEMU_ARCH_LOONGARCH = (1 << 23), }; -extern const uint32_t arch_type; +bool qemu_arch_available(unsigned qemu_arch_mask); #endif diff --git a/system/arch_init.c b/system/arch_init.c index b1baed1..e857368 100644 --- a/system/arch_init.c +++ b/system/arch_init.c @@ -24,18 +24,7 @@ #include "qemu/osdep.h" #include "system/arch_init.h" -#ifdef TARGET_SPARC -int graphic_width = 1024; -int graphic_height = 768; -int graphic_depth = 8; -#elif defined(TARGET_M68K) -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 8; -#else -int graphic_width = 800; -int graphic_height = 600; -int graphic_depth = 32; -#endif - -const uint32_t arch_type = QEMU_ARCH; +bool qemu_arch_available(unsigned qemu_arch_mask) +{ + return qemu_arch_mask & QEMU_ARCH; +} diff --git a/system/globals-target.c b/system/globals-target.c new file mode 100644 index 0000000..9897205 --- /dev/null +++ b/system/globals-target.c @@ -0,0 +1,24 @@ +/* + * Global variables that should not exist (target specific) + * + * Copyright (c) 2003-2008 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "system/system.h" + +#ifdef TARGET_SPARC +int graphic_width = 1024; +int graphic_height = 768; +int graphic_depth = 8; +#elif defined(TARGET_M68K) +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 8; +#else +int graphic_width = 800; +int graphic_height = 600; +int graphic_depth = 32; +#endif diff --git a/system/meson.build b/system/meson.build index c83d80f..eec07a9 100644 --- a/system/meson.build +++ b/system/meson.build @@ -1,6 +1,7 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'arch_init.c', 'ioport.c', + 'globals-target.c', 'memory.c', 'physmem.c', )]) diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 856c9e8..5588ed2 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -132,7 +132,7 @@ static const char *qdev_class_get_alias(DeviceClass *dc) for (i = 0; qdev_alias_table[i].typename; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } @@ -218,7 +218,7 @@ static const char *find_typename_by_alias(const char *alias) for (i = 0; qdev_alias_table[i].alias; i++) { if (qdev_alias_table[i].arch_mask && - !(qdev_alias_table[i].arch_mask & arch_type)) { + !qemu_arch_available(qdev_alias_table[i].arch_mask)) { continue; } diff --git a/system/vl.c b/system/vl.c index 04f7846..ec93988 100644 --- a/system/vl.c +++ b/system/vl.c @@ -878,11 +878,11 @@ static void help(int exitcode) g_get_prgname()); #define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \ - if ((arch_mask) & arch_type) \ + if (qemu_arch_available(arch_mask)) \ fputs(opt_help, stdout); #define ARCHHEADING(text, arch_mask) \ - if ((arch_mask) & arch_type) \ + if (qemu_arch_available(arch_mask)) \ puts(stringify(text)); #define DEFHEADING(text) ARCHHEADING(text, QEMU_ARCH_ALL) @@ -2929,7 +2929,7 @@ void qemu_init(int argc, char **argv) const QEMUOption *popt; popt = lookup_opt(argc, argv, &optarg, &optind); - if (!(popt->arch_mask & arch_type)) { + if (!qemu_arch_available(popt->arch_mask)) { error_report("Option not supported for this target"); exit(1); } diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 464fbf0..a9f056f 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -152,18 +152,6 @@ #define HV_X64_MSR_STIMER3_COUNT 0x400000B7 /* - * Hyper-V Synthetic debug options MSR - */ -#define HV_X64_MSR_SYNDBG_CONTROL 0x400000F1 -#define HV_X64_MSR_SYNDBG_STATUS 0x400000F2 -#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x400000F3 -#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x400000F4 -#define HV_X64_MSR_SYNDBG_PENDING_BUFFER 0x400000F5 -#define HV_X64_MSR_SYNDBG_OPTIONS 0x400000FF - -#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) - -/* * Guest crash notification MSRs */ #define HV_X64_MSR_CRASH_P0 0x40000100 diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 3ecb23e..5a8c1f1 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -297,6 +297,7 @@ qos_test_ss.add( 'pca9552-test.c', 'pci-test.c', 'pcnet-test.c', + 'rs5c372-test.c', 'sdhci-test.c', 'spapr-phb-test.c', 'tmp105-test.c', diff --git a/tests/qtest/rs5c372-test.c b/tests/qtest/rs5c372-test.c new file mode 100644 index 0000000..0f6a9b6 --- /dev/null +++ b/tests/qtest/rs5c372-test.c @@ -0,0 +1,43 @@ +/* + * QTest testcase for the RS5C372 RTC + * + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * Based on ds1338-test.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bcd.h" +#include "libqos/i2c.h" + +#define RS5C372_ADDR 0x32 + +static void rs5c372_read_date(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = obj; + + uint8_t resp[0x10]; + time_t now = time(NULL); + struct tm *utc = gmtime(&now); + + i2c_read_block(i2cdev, 0, resp, sizeof(resp)); + + /* check retrieved time against local time */ + g_assert_cmpuint(from_bcd(resp[5]), == , utc->tm_mday); + g_assert_cmpuint(from_bcd(resp[6]), == , 1 + utc->tm_mon); + g_assert_cmpuint(2000 + from_bcd(resp[7]), == , 1900 + utc->tm_year); +} + +static void rs5c372_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { }; + add_qi2c_address(&opts, &(QI2CAddress) { RS5C372_ADDR }); + + qos_node_create_driver("rs5c372", i2c_device_create); + qos_node_consumes("rs5c372", "i2c-bus", &opts); + qos_add_test("read_date", "rs5c372", rs5c372_read_date, NULL); +} + +libqos_init(rs5c372_register_nodes); |