@samitouri / QOSamiQemu / commits / fdb2f65c51

amd_iommu: Fix endianness handling for command buffer entries

AMD IOMMU command buffer entries are stored in guest memory in little-endian format. Convert command buffer with le64_to_cpu() after dma_memory_read(), so that command handlers can all operate using host native endianness. Remove the cpu_to_le*() conversions from command handlers, since the values are used internally by device emulation and do not need translation. Conversion is only necessary when reading or writing to guest memory e.g. writing completion-wait data and event log entries. The flow for command buffer handling is: - Retrieve command buffer (cmd[]) from guest memory (via dma_memory_read()) - Convert command buffer to host endianness (via le64_to_cpu()) - All handlers decode fields from cmd[] in host-endian format - All emulation code uses decoded values in host-endian format - Use cpu_to_le*() when writing back data to guest memory Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Alejandro Jimenez <alejandro.j.jimenez@oracle.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Reviewed-by: Michael S. Tsirkin <mst@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com> Message-ID: <20260630220806.1758748-6-alejandro.j.jimenez@oracle.com>

Alejandro Jimenez committed Jun 30, 2026 at 22:08 UTC fdb2f65c513257c783aca6e031350e6d29f1e186
1 file changed +29 -10
hw/i386/amd_iommu.c
+29 -10
@@ -278,6 +278,7 @@ static uint32_t get_next_eventlog_entry(AMDVIState *s)
278
279 static void amdvi_log_event(AMDVIState *s, uint64_t *evt)
280 {
281 + uint64_t le_evt[2];
282 uint32_t evtlog_tail_next;
283
284 /* event logging not enabled */
@@ -298,8 +299,14 @@ static void amdvi_log_event(AMDVIState *s, uint64_t *evt)
299 return;
300 }
301
302 + /*
303 + * Convert event buffer to little-endian before writing it to guest memory.
304 + */
305 + le_evt[0] = cpu_to_le64(evt[0]);
306 + le_evt[1] = cpu_to_le64(evt[1]);
307 +
308 if (dma_memory_write(&address_space_memory, s->evtlog + s->evtlog_tail,
302 - evt, AMDVI_EVENT_LEN, MEMTXATTRS_UNSPECIFIED)) {
309 + le_evt, AMDVI_EVENT_LEN, MEMTXATTRS_UNSPECIFIED)) {
310 trace_amdvi_evntlog_fail(s->evtlog, s->evtlog_tail);
311 }
312
@@ -545,15 +552,18 @@ static void amdvi_update_iotlb(AMDVIState *s, uint16_t devid,
552 static void amdvi_completion_wait(AMDVIState *s, uint64_t *cmd)
553 {
554 /* pad the last 3 bits */
548 - hwaddr addr = cpu_to_le64(extract64(cmd[0], 3, 49)) << 3;
549 - uint64_t data = cpu_to_le64(cmd[1]);
555 + hwaddr addr = extract64(cmd[0], 3, 49) << 3;
556 + uint64_t data = cmd[1];
557 +
558 + /* Format the data to be written to guest memory as little-endian */
559 + uint64_t le_data = cpu_to_le64(data);
560
561 if (extract64(cmd[0], 52, 8)) {
562 amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4),
563 s->cmdbuf + s->cmdbuf_head);
564 }
565 if (extract64(cmd[0], 0, 1)) {
556 - if (dma_memory_write(&address_space_memory, addr, &data,
566 + if (dma_memory_write(&address_space_memory, addr, &le_data,
567 AMDVI_COMPLETION_DATA_SIZE,
568 MEMTXATTRS_UNSPECIFIED)) {
569 trace_amdvi_completion_wait_fail(addr);
@@ -1281,7 +1291,7 @@ static void amdvi_update_addr_translation_mode(AMDVIState *s, uint16_t devid)
1291 /* log error without aborting since linux seems to be using reserved bits */
1292 static void amdvi_inval_devtab_entry(AMDVIState *s, uint64_t *cmd)
1293 {
1284 - uint16_t devid = cpu_to_le16((uint16_t)extract64(cmd[0], 0, 16));
1294 + uint16_t devid = extract64(cmd[0], 0, 16);
1295
1296 trace_amdvi_devtab_inval(PCI_BUS_NUM(devid), PCI_SLOT(devid),
1297 PCI_FUNC(devid));
@@ -1448,9 +1458,9 @@ static void amdvi_sync_domain(AMDVIState *s, uint16_t domid, uint64_t addr,
1458 /* we don't have devid - we can't remove pages by address */
1459 static void amdvi_inval_pages(AMDVIState *s, uint64_t *cmd)
1460 {
1451 - uint16_t domid = cpu_to_le16((uint16_t)extract64(cmd[0], 32, 16));
1452 - uint64_t addr = cpu_to_le64(extract64(cmd[1], 12, 52)) << 12;
1453 - uint16_t flags = cpu_to_le16((uint16_t)extract64(cmd[1], 0, 3));
1461 + uint16_t domid = extract64(cmd[0], 32, 16);
1462 + uint64_t addr = extract64(cmd[1], 12, 52) << 12;
1463 + uint16_t flags = extract64(cmd[1], 0, 3);
1464
1465 if (extract64(cmd[0], 20, 12) || extract64(cmd[0], 48, 12) ||
1466 extract64(cmd[1], 3, 9)) {
@@ -1497,7 +1507,7 @@ static void amdvi_inval_inttable(AMDVIState *s, uint64_t *cmd)
1507 static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd)
1508 {
1509
1500 - uint16_t devid = cpu_to_le16(extract64(cmd[0], 0, 16));
1510 + uint16_t devid = extract64(cmd[0], 0, 16);
1511 if (extract64(cmd[1], 1, 1) || extract64(cmd[1], 3, 1) ||
1512 extract64(cmd[1], 6, 6)) {
1513 amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4),
@@ -1509,7 +1519,7 @@ static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd)
1519 g_hash_table_foreach_remove(s->iotlb, amdvi_iotlb_remove_by_devid,
1520 &devid);
1521 } else {
1512 - amdvi_iotlb_remove_page(s, cpu_to_le64(extract64(cmd[1], 12, 52)) << 12,
1522 + amdvi_iotlb_remove_page(s, extract64(cmd[1], 12, 52) << 12,
1523 devid);
1524 }
1525 trace_amdvi_iotlb_inval();
@@ -1527,6 +1537,15 @@ static void amdvi_cmdbuf_exec(AMDVIState *s)
1537 return;
1538 }
1539
1540 + /*
1541 + * Commands in guest memory are little-endian. Convert once after reading
1542 + * so that command handlers can decode values in host native endianness.
1543 + * Convert back to little-endian only when writing data to guest memory via
1544 + * dma_memory_write().
1545 + */
1546 + cmd[0] = le64_to_cpu(cmd[0]);
1547 + cmd[1] = le64_to_cpu(cmd[1]);
1548 +
1549 switch (extract64(cmd[0], 60, 4)) {
1550 case AMDVI_CMD_COMPLETION_WAIT:
1551 amdvi_completion_wait(s, cmd);