summary refs log tree commit diff stats
path: root/hw/scsi/esp.c
diff options
context:
space:
mode:
authorStefan Hajnoczi <stefanha@redhat.com>2025-07-16 07:06:14 -0400
committerStefan Hajnoczi <stefanha@redhat.com>2025-07-16 07:06:14 -0400
commita56ac09f5c37f57059c2a2c5ae6aeff7f7241a84 (patch)
tree29494e48da4989384a5e3c83cbc6991ea56e91cf /hw/scsi/esp.c
parent4d67fdff7afabb167a67f79d27900bf568409b25 (diff)
parent6f8ce26bb00db66e6cec632f16df3cd13e4df934 (diff)
downloadfocaccia-qemu-a56ac09f5c37f57059c2a2c5ae6aeff7f7241a84.tar.gz
focaccia-qemu-a56ac09f5c37f57059c2a2c5ae6aeff7f7241a84.zip
Merge tag 'mips-20250715' of https://github.com/philmd/qemu into staging
MIPS patches queue

- Implement CRC32[c] (Release 6) instructions
- Convert Octeon LX instructions to decodetree
- Restrict ITU to TCG
- Fix ESP issue affecting WinNT (INACCESSIBLE_BOOT_DEVICE BSOD)
- Add missing FDT dependency for some MicroBlaze machines
- Remove execute bit on hppa-firmware blobs

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmh18qYACgkQ4+MsLN6t
# wN6BKBAAnGgPvk1/8Pi0SJG9Swr60JE6VodL+89xgVglopcYAxN0hpcpq+W2ywgK
# uaOWNzfsyZZY7Zst35nLJKZrRjV6pXqOPqxLNIsJ8GHiVOXPUQTJIkBjgLs1b9kc
# +tR85U/JKTExP16FXQWzbSTqCOYsVoNtm6boYVjzno8BGv2C2ymDNr7a8oN9FWsS
# It/+5SpxwosmYf7jdbCRAwP/TRTFDGtV1JTjaEuZ19qj024hcBDTQ4qdQu7iIXF9
# eI7a9trIoGGUF7644z+XkYbSd2dghKqOaPmKDDSnW+pva26NpoG2EI4C9pkcvfrh
# 4hvpAiwNbZ7erChIi5vlZsw84fUCptMVaoZWk+mu3Rif440FM02OA51dgUN3DCMV
# jddW5CzRcYlKxU//uMVdnmIY7T+KSEJJXUKOXa6vh623PdD4I14H9vrcP5t6lavE
# 2G16+OJHMB0pQkDFgwiz4f1nglIxO4ujMP0Ow5wRDarCCI/4BWkMDAuWWAv05tXH
# zKsfHi5RSL2UtbNr0yd1e6Ph9ofm99RYOMGl0G8vyWbR4hVIAhfF8+qq0EfH9JCk
# t7vIigoU5FbP4JNaxJoNIIigFpyedC2nUPHYkeM4A2e5xW5oC/KGsp4XzzyfmJ3Y
# wpWAYFyB/7qAk8MfeAH6DTJ5s1Sz7xY0K0CrjIPnhCoLpXURwpA=
# =bhsu
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 15 Jul 2025 02:18:14 EDT
# 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 'mips-20250715' of https://github.com/philmd/qemu:
  esp.c: only allow ESP commands permitted in the current asc_mode
  esp.c: add asc_mode property to indicate the current ESP mode
  esp.c: only call dma_memory_write function if transfer length is non-zero
  esp.c: only call dma_memory_read function if transfer length is non-zero
  esp.h: remove separate ESPState typedef
  esp.c: improve comment in esp_transfer_data()
  esp.c: only raise IRQ in esp_transfer_data() for CMD_SEL, CMD_SELATN and CMD_TI commands
  hw/microblaze: Add missing FDT dependency
  hw/intc/loongarch_extioi: Remove unnecessary 'qemu/typedefs.h' include
  hw/mips: Restrict ITU to TCG
  roms: re-remove execute bit from hppa-firmware*
  tests/tcg/mips: Add tests for MIPS CRC32[c] instructions
  target/mips: Have gen_[d]lsa() callers add 1 to shift amount argument
  target/mips: Convert Octeon LX instructions to decodetree
  target/mips: Extract generic gen_lx() helper
  target/mips: Extract gen_base_index_addr() helper
  target/mips: Add support for emulation of CRC32 instructions

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
Diffstat (limited to 'hw/scsi/esp.c')
-rw-r--r--hw/scsi/esp.c94
1 files changed, 81 insertions, 13 deletions
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index f24991fd16..1d264c40e5 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -275,6 +275,7 @@ static int esp_select(ESPState *s)
     if (!s->current_dev) {
         /* No such drive */
         s->rregs[ESP_RSTAT] = 0;
+        s->asc_mode = ESP_ASC_MODE_DIS;
         s->rregs[ESP_RINTR] = INTR_DC;
         esp_raise_irq(s);
         return -1;
@@ -284,6 +285,7 @@ static int esp_select(ESPState *s)
      * Note that we deliberately don't raise the IRQ here: this will be done
      * either in esp_transfer_data() or esp_command_complete()
      */
+    s->asc_mode = ESP_ASC_MODE_INI;
     return 0;
 }
 
@@ -308,6 +310,7 @@ static void do_command_phase(ESPState *s)
     if (!current_lun) {
         /* No such drive */
         s->rregs[ESP_RSTAT] = 0;
+        s->asc_mode = ESP_ASC_MODE_DIS;
         s->rregs[ESP_RINTR] = INTR_DC;
         s->rregs[ESP_RSEQ] = SEQ_0;
         esp_raise_irq(s);
@@ -487,8 +490,10 @@ static void esp_do_dma(ESPState *s)
     case STAT_MO:
         if (s->dma_memory_read) {
             len = MIN(len, fifo8_num_free(&s->cmdfifo));
-            s->dma_memory_read(s->dma_opaque, buf, len);
-            esp_set_tc(s, esp_get_tc(s) - len);
+            if (len) {
+                s->dma_memory_read(s->dma_opaque, buf, len);
+                esp_set_tc(s, esp_get_tc(s) - len);
+            }
         } else {
             len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo));
             len = MIN(fifo8_num_free(&s->cmdfifo), len);
@@ -541,9 +546,11 @@ static void esp_do_dma(ESPState *s)
         trace_esp_do_dma(cmdlen, len);
         if (s->dma_memory_read) {
             len = MIN(len, fifo8_num_free(&s->cmdfifo));
-            s->dma_memory_read(s->dma_opaque, buf, len);
-            fifo8_push_all(&s->cmdfifo, buf, len);
-            esp_set_tc(s, esp_get_tc(s) - len);
+            if (len) {
+                s->dma_memory_read(s->dma_opaque, buf, len);
+                fifo8_push_all(&s->cmdfifo, buf, len);
+                esp_set_tc(s, esp_get_tc(s) - len);
+            }
         } else {
             len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo));
             len = MIN(fifo8_num_free(&s->cmdfifo), len);
@@ -572,8 +579,10 @@ static void esp_do_dma(ESPState *s)
         switch (s->rregs[ESP_CMD]) {
         case CMD_TI | CMD_DMA:
             if (s->dma_memory_read) {
-                s->dma_memory_read(s->dma_opaque, s->async_buf, len);
-                esp_set_tc(s, esp_get_tc(s) - len);
+                if (len) {
+                    s->dma_memory_read(s->dma_opaque, s->async_buf, len);
+                    esp_set_tc(s, esp_get_tc(s) - len);
+                }
             } else {
                 /* Copy FIFO data to device */
                 len = MIN(s->async_len, ESP_FIFO_SZ);
@@ -625,7 +634,9 @@ static void esp_do_dma(ESPState *s)
         switch (s->rregs[ESP_CMD]) {
         case CMD_TI | CMD_DMA:
             if (s->dma_memory_write) {
-                s->dma_memory_write(s->dma_opaque, s->async_buf, len);
+                if (len) {
+                    s->dma_memory_write(s->dma_opaque, s->async_buf, len);
+                }
             } else {
                 /* Copy device data to FIFO */
                 len = MIN(len, fifo8_num_free(&s->fifo));
@@ -675,6 +686,7 @@ static void esp_do_dma(ESPState *s)
                 buf[0] = s->status;
 
                 if (s->dma_memory_write) {
+                    /* Length already non-zero */
                     s->dma_memory_write(s->dma_opaque, buf, len);
                 } else {
                     esp_fifo_push_buf(s, buf, len);
@@ -709,6 +721,7 @@ static void esp_do_dma(ESPState *s)
                 buf[0] = 0;
 
                 if (s->dma_memory_write) {
+                    /* Length already non-zero */
                     s->dma_memory_write(s->dma_opaque, buf, len);
                 } else {
                     esp_fifo_push_buf(s, buf, len);
@@ -1012,6 +1025,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len)
              */
              s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC;
              s->rregs[ESP_RSEQ] = SEQ_CD;
+             esp_raise_irq(s);
              break;
 
         case CMD_SELATNS | CMD_DMA:
@@ -1022,20 +1036,21 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len)
              */
              s->rregs[ESP_RINTR] |= INTR_BS;
              s->rregs[ESP_RSEQ] = SEQ_MO;
+             esp_raise_irq(s);
              break;
 
         case CMD_TI | CMD_DMA:
         case CMD_TI:
             /*
-             * Bus service interrupt raised because of initial change to
-             * DATA phase
+             * If the final COMMAND phase data was transferred using a TI
+             * command, clear ESP_CMD to terminate the TI command and raise
+             * the completion interrupt
              */
             s->rregs[ESP_CMD] = 0;
             s->rregs[ESP_RINTR] |= INTR_BS;
+            esp_raise_irq(s);
             break;
         }
-
-        esp_raise_irq(s);
     }
 
     /*
@@ -1090,6 +1105,7 @@ void esp_hard_reset(ESPState *s)
     fifo8_reset(&s->cmdfifo);
     s->dma = 0;
     s->dma_cb = NULL;
+    s->asc_mode = ESP_ASC_MODE_DIS;
 
     s->rregs[ESP_CFG1] = 7;
 }
@@ -1113,6 +1129,38 @@ static void parent_esp_reset(ESPState *s, int irq, int level)
     }
 }
 
+static bool esp_cmd_is_valid(ESPState *s, uint8_t cmd)
+{
+    uint8_t cmd_group = (cmd & CMD_GRP_MASK) >> 4;
+
+    /* Always allow misc commands */
+    if (cmd_group == CMD_GRP_MISC) {
+        return true;
+    }
+
+    switch (s->asc_mode) {
+    case ESP_ASC_MODE_DIS:
+        /* Disconnected mode: only allow disconnected commands */
+        if (cmd_group == CMD_GRP_DISC) {
+            return true;
+        }
+        break;
+
+    case ESP_ASC_MODE_INI:
+        /* Initiator mode: allow initiator commands */
+        if (cmd_group == CMD_GRP_INIT) {
+            return true;
+        }
+        break;
+
+    default:
+        g_assert_not_reached();
+    }
+
+    trace_esp_invalid_cmd(cmd, s->asc_mode);
+    return false;
+}
+
 static void esp_run_cmd(ESPState *s)
 {
     uint8_t cmd = s->rregs[ESP_CMD];
@@ -1158,6 +1206,7 @@ static void esp_run_cmd(ESPState *s)
         break;
     case CMD_MSGACC:
         trace_esp_mem_writeb_cmd_msgacc(cmd);
+        s->asc_mode = ESP_ASC_MODE_DIS;
         s->rregs[ESP_RINTR] |= INTR_DC;
         s->rregs[ESP_RSEQ] = 0;
         s->rregs[ESP_RFLAGS] = 0;
@@ -1268,6 +1317,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
         break;
     case ESP_CMD:
         s->rregs[saddr] = val;
+        if (!esp_cmd_is_valid(s, s->rregs[saddr])) {
+            s->rregs[ESP_RSTAT] |= INTR_IL;
+            esp_raise_irq(s);
+            break;
+        }
         esp_run_cmd(s);
         break;
     case ESP_WBUSID ... ESP_WSYNO:
@@ -1325,6 +1379,14 @@ static bool esp_is_between_version_5_and_6(void *opaque, int version_id)
     return version_id >= 5 && version_id <= 6;
 }
 
+static bool esp_is_version_8(void *opaque, int version_id)
+{
+    ESPState *s = ESP(opaque);
+
+    version_id = MIN(version_id, s->mig_version_id);
+    return version_id >= 8;
+}
+
 int esp_pre_save(void *opaque)
 {
     ESPState *s = ESP(object_resolve_path_component(
@@ -1356,13 +1418,18 @@ static int esp_post_load(void *opaque, int version_id)
         }
     }
 
+    if (version_id < 8) {
+        /* Assume initiator mode to allow all commands to continue */
+        s->asc_mode = ESP_ASC_MODE_INI;
+    }
+
     s->mig_version_id = vmstate_esp.version_id;
     return 0;
 }
 
 const VMStateDescription vmstate_esp = {
     .name = "esp",
-    .version_id = 7,
+    .version_id = 8,
     .minimum_version_id = 3,
     .post_load = esp_post_load,
     .fields = (const VMStateField[]) {
@@ -1394,6 +1461,7 @@ const VMStateDescription vmstate_esp = {
                            esp_is_between_version_5_and_6),
         VMSTATE_UINT8_TEST(lun, ESPState, esp_is_version_6),
         VMSTATE_BOOL(drq_state, ESPState),
+        VMSTATE_UINT8_TEST(asc_mode, ESPState, esp_is_version_8),
         VMSTATE_END_OF_LIST()
     },
 };