esp.c: handle TC underflow for DMA SCSI requests
authorMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Fri, 12 Jan 2024 12:54:04 +0000 (12:54 +0000)
committerMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Tue, 13 Feb 2024 19:37:28 +0000 (19:37 +0000)
Detect the case where the guest underflows TC by requesting a DMA transfer which
is larger than the available data. If this case is detected, immediately
complete the SCSI request and handle any remaining FIFO accesses in the STATUS
phase by raising INTR_BS once the FIFO is below the threshold.

Note that handling the premature SCSI bus phase change in the case of TC
underflow fixes booting EMILE on m68k once again.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Tested-by: Helge Deller <deller@gmx.de>
Tested-by: Thomas Huth <thuth@redhat.com>
Message-Id: <20240112125420.514425-73-mark.cave-ayland@ilande.co.uk>
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
hw/scsi/esp.c

index 8ea100ee9c935e15ffa698cf563af56911aef5c7..a3e18bb3d761b803d66828bce27f20c0e1478f9d 100644 (file)
@@ -579,6 +579,12 @@ static void esp_do_dma(ESPState *s)
             s->async_len -= len;
             s->ti_size -= len;
 
+            if (s->async_len == 0 && s->ti_size == 0 && esp_get_tc(s)) {
+                /* If the guest underflows TC then terminate SCSI request */
+                scsi_req_continue(s->current_req);
+                return;
+            }
+
             if (s->async_len == 0 && fifo8_num_used(&s->fifo) < 2) {
                 /* Defer until the scsi layer has completed */
                 scsi_req_continue(s->current_req);
@@ -596,6 +602,12 @@ static void esp_do_dma(ESPState *s)
             esp_set_tc(s, esp_get_tc(s) - len);
             esp_raise_drq(s);
 
+            if (s->async_len == 0 && s->ti_size == 0 && esp_get_tc(s)) {
+                /* If the guest underflows TC then terminate SCSI request */
+                scsi_req_continue(s->current_req);
+                return;
+            }
+
             if (s->async_len == 0 && fifo8_num_used(&s->fifo) < 2) {
                 /* Defer until the scsi layer has completed */
                 scsi_req_continue(s->current_req);
@@ -630,6 +642,15 @@ static void esp_do_dma(ESPState *s)
                 }
             }
             break;
+
+        default:
+            /* Consume remaining data if the guest underflows TC */
+            if (fifo8_num_used(&s->fifo) < 2) {
+                s->rregs[ESP_RINTR] |= INTR_BS;
+                esp_raise_irq(s);
+                esp_lower_drq(s);
+            }
+            break;
         }
         break;
 
@@ -884,7 +905,9 @@ void esp_command_complete(SCSIRequest *req, size_t resid)
     esp_set_phase(s, STAT_ST);
     s->rregs[ESP_RINTR] |= INTR_BS;
     esp_raise_irq(s);
-    esp_lower_drq(s);
+
+    /* Ensure DRQ is set correctly for TC underflow or normal completion */
+    esp_dma_ti_check(s);
 
     if (s->current_req) {
         scsi_req_unref(s->current_req);