QEMU-Devel Archive on lore.kernel.org
 help / color / Atom feed
From: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
To: qemu-devel@nongnu.org, pbonzini@redhat.com, fam@euphon.net,
	laurent@vivier.eu
Subject: [PATCH v3 41/42] esp: implement non-DMA transfers in PDMA mode
Date: Thu,  4 Mar 2021 22:11:02 +0000
Message-ID: <20210304221103.6369-42-mark.cave-ayland@ilande.co.uk> (raw)
In-Reply-To: <20210304221103.6369-1-mark.cave-ayland@ilande.co.uk>

The MacOS toolbox ROM uses non-DMA TI commands to handle the first/last byte
of an unaligned 16-bit transfer to memory.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Reviewed-by: Laurent Vivier <laurent@vivier.eu>
---
 hw/scsi/esp.c         | 133 ++++++++++++++++++++++++++++++------------
 include/hw/scsi/esp.h |   1 +
 2 files changed, 98 insertions(+), 36 deletions(-)

diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 8a9b1500de..f828e70865 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -296,6 +296,7 @@ static void do_busid_cmd(ESPState *s, uint8_t busid)
     if (datalen != 0) {
         s->rregs[ESP_RSTAT] = STAT_TC;
         s->rregs[ESP_RSEQ] = SEQ_CD;
+        s->ti_cmd = 0;
         esp_set_tc(s, 0);
         if (datalen > 0) {
             /*
@@ -645,6 +646,71 @@ static void esp_do_dma(ESPState *s)
     esp_lower_drq(s);
 }
 
+static void esp_do_nodma(ESPState *s)
+{
+    int to_device = ((s->rregs[ESP_RSTAT] & 7) == STAT_DO);
+    uint32_t cmdlen, n;
+    int len;
+
+    if (s->do_cmd) {
+        cmdlen = fifo8_num_used(&s->cmdfifo);
+        trace_esp_handle_ti_cmd(cmdlen);
+        s->ti_size = 0;
+        if ((s->rregs[ESP_RSTAT] & 7) == STAT_CD) {
+            /* No command received */
+            if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) {
+                return;
+            }
+
+            /* Command has been received */
+            s->do_cmd = 0;
+            do_cmd(s);
+        } else {
+            /*
+             * Extra message out bytes received: update cmdfifo_cdb_offset
+             * and then switch to commmand phase
+             */
+            s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo);
+            s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD;
+            s->rregs[ESP_RSEQ] = SEQ_CD;
+            s->rregs[ESP_RINTR] |= INTR_BS;
+            esp_raise_irq(s);
+        }
+        return;
+    }
+
+    if (s->async_len == 0) {
+        /* Defer until data is available.  */
+        return;
+    }
+
+    if (to_device) {
+        len = MIN(fifo8_num_used(&s->fifo), ESP_FIFO_SZ);
+        memcpy(s->async_buf, fifo8_pop_buf(&s->fifo, len, &n), len);
+        s->async_buf += len;
+        s->async_len -= len;
+        s->ti_size += len;
+    } else {
+        len = MIN(s->ti_size, s->async_len);
+        len = MIN(len, fifo8_num_free(&s->fifo));
+        fifo8_push_all(&s->fifo, s->async_buf, len);
+        s->async_buf += len;
+        s->async_len -= len;
+        s->ti_size -= len;
+    }
+
+    if (s->async_len == 0) {
+        scsi_req_continue(s->current_req);
+
+        if (to_device || s->ti_size == 0) {
+            return;
+        }
+    }
+
+    s->rregs[ESP_RINTR] |= INTR_BS;
+    esp_raise_irq(s);
+}
+
 void esp_command_complete(SCSIRequest *req, size_t resid)
 {
     ESPState *s = req->hba_private;
@@ -701,56 +767,51 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len)
         return;
     }
 
-    if (dmalen) {
-        esp_do_dma(s);
-    } else if (s->ti_size <= 0) {
+    if (s->ti_cmd == 0) {
         /*
-         * If this was the last part of a DMA transfer then the
-         * completion interrupt is deferred to here.
+         * Always perform the initial transfer upon reception of the next TI
+         * command to ensure the DMA/non-DMA status of the command is correct.
+         * It is not possible to use s->dma directly in the section below as
+         * some OSs send non-DMA NOP commands after a DMA transfer. Hence if the
+         * async data transfer is delayed then s->dma is set incorrectly.
          */
-        esp_dma_done(s);
-        esp_lower_drq(s);
+        return;
+    }
+
+    if (s->ti_cmd & CMD_DMA) {
+        if (dmalen) {
+            esp_do_dma(s);
+        } else if (s->ti_size <= 0) {
+            /*
+             * If this was the last part of a DMA transfer then the
+             * completion interrupt is deferred to here.
+             */
+            esp_dma_done(s);
+            esp_lower_drq(s);
+        }
+    } else {
+        esp_do_nodma(s);
     }
 }
 
 static void handle_ti(ESPState *s)
 {
-    uint32_t dmalen, cmdlen;
+    uint32_t dmalen;
 
     if (s->dma && !s->dma_enabled) {
         s->dma_cb = handle_ti;
         return;
     }
 
-    dmalen = esp_get_tc(s);
+    s->ti_cmd = s->rregs[ESP_CMD];
     if (s->dma) {
+        dmalen = esp_get_tc(s);
         trace_esp_handle_ti(dmalen);
         s->rregs[ESP_RSTAT] &= ~STAT_TC;
         esp_do_dma(s);
-    } else if (s->do_cmd) {
-        cmdlen = fifo8_num_used(&s->cmdfifo);
-        trace_esp_handle_ti_cmd(cmdlen);
-        s->ti_size = 0;
-        if ((s->rregs[ESP_RSTAT] & 7) == STAT_CD) {
-            /* No command received */
-            if (s->cmdfifo_cdb_offset == fifo8_num_used(&s->cmdfifo)) {
-                return;
-            }
-
-            /* Command has been received */
-            s->do_cmd = 0;
-            do_cmd(s);
-        } else {
-            /*
-             * Extra message out bytes received: update cmdfifo_cdb_offset
-             * and then switch to commmand phase
-             */
-            s->cmdfifo_cdb_offset = fifo8_num_used(&s->cmdfifo);
-            s->rregs[ESP_RSTAT] = STAT_TC | STAT_CD;
-            s->rregs[ESP_RSEQ] = SEQ_CD;
-            s->rregs[ESP_RINTR] |= INTR_BS;
-            esp_raise_irq(s);
-        }
+    } else {
+        trace_esp_handle_ti(s->ti_size);
+        esp_do_nodma(s);
     }
 }
 
@@ -789,12 +850,12 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr)
 
     switch (saddr) {
     case ESP_FIFO:
-        if ((s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) {
+        if (s->dma_memory_read && s->dma_memory_write &&
+                (s->rregs[ESP_RSTAT] & STAT_PIO_MASK) == 0) {
             /* Data out.  */
             qemu_log_mask(LOG_UNIMP, "esp: PIO data read not implemented\n");
             s->rregs[ESP_FIFO] = 0;
         } else {
-            s->ti_size--;
             s->rregs[ESP_FIFO] = esp_fifo_pop(s);
         }
         val = s->rregs[ESP_FIFO];
@@ -846,7 +907,6 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
         if (s->do_cmd) {
             esp_cmdfifo_push(s, val);
         } else {
-            s->ti_size++;
             esp_fifo_push(s, val);
         }
 
@@ -1047,6 +1107,7 @@ const VMStateDescription vmstate_esp = {
         VMSTATE_UINT8_TEST(cmdfifo_cdb_offset, ESPState, esp_is_version_5),
         VMSTATE_FIFO8_TEST(fifo, ESPState, esp_is_version_5),
         VMSTATE_FIFO8_TEST(cmdfifo, ESPState, esp_is_version_5),
+        VMSTATE_UINT8_TEST(ti_cmd, ESPState, esp_is_version_5),
         VMSTATE_END_OF_LIST()
     },
 };
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
index 2fe8d20ab5..95088490aa 100644
--- a/include/hw/scsi/esp.h
+++ b/include/hw/scsi/esp.h
@@ -40,6 +40,7 @@ struct ESPState {
     uint32_t do_cmd;
 
     bool data_in_ready;
+    uint8_t ti_cmd;
     int dma_enabled;
 
     uint32_t async_len;
-- 
2.20.1



  parent reply index

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-03-04 22:10 [PATCH v3 00/42] esp: consolidate PDMA transfer buffers and other fixes Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 01/42] esp: checkpatch fixes Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 02/42] esp: rename existing ESP QOM type to SYSBUS_ESP Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 03/42] esp: QOMify the internal ESP device state Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 04/42] esp: add vmstate_esp version to embedded ESPState Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 05/42] esp: add trace event when receiving a TI command Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 06/42] esp: fix esp_reg_read() trace event Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 07/42] esp: add PDMA trace events Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 08/42] esp: determine transfer direction directly from SCSI phase Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 09/42] esp: introduce esp_get_tc() and esp_set_tc() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 10/42] esp: introduce esp_get_stc() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 11/42] esp: apply transfer length adjustment when STC is zero at TC load time Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 12/42] esp: remove dma_counter from ESPState Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 13/42] esp: remove dma_left " Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 14/42] esp: remove minlen restriction in handle_ti Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 15/42] esp: introduce esp_pdma_read() and esp_pdma_write() functions Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 16/42] esp: use pdma_origin directly in esp_pdma_read()/esp_pdma_write() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 17/42] esp: move pdma_len and TC logic into esp_pdma_read()/esp_pdma_write() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 18/42] esp: accumulate SCSI commands for PDMA transfers in cmdbuf instead of pdma_buf Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 19/42] esp: remove buf parameter from do_cmd() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 20/42] esp: remove the buf and buflen parameters from get_cmd() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 21/42] esp: remove redundant pdma_start from ESPState Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 22/42] esp: move PDMA length adjustments into esp_pdma_read()/esp_pdma_write() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 23/42] esp: use ti_wptr/ti_rptr to manage the current FIFO position for PDMA Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 24/42] esp: use in-built TC to determine PDMA transfer length Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 25/42] esp: remove CMD pdma_origin Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 26/42] esp: rename get_cmd_cb() to esp_select() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 27/42] esp: fix PDMA target selection Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 28/42] esp: use FIFO for PDMA transfers between initiator and device Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 29/42] esp: remove pdma_origin from ESPState Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 30/42] esp: add 4 byte PDMA read and write transfers Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 31/42] esp: implement FIFO flush command Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 32/42] esp: latch individual bits in ESP_RINTR register Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 33/42] esp: defer command completion interrupt on incoming data transfers Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 34/42] esp: remove old deferred command completion mechanism Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 35/42] esp: raise interrupt after every non-DMA byte transferred to the FIFO Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 36/42] esp: add maxlen parameter to get_cmd() Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 37/42] esp: transition to message out phase after SATN and stop command Mark Cave-Ayland
2021-03-04 22:10 ` [PATCH v3 38/42] esp: convert ti_buf from array to Fifo8 Mark Cave-Ayland
2021-03-04 22:11 ` [PATCH v3 39/42] esp: convert cmdbuf " Mark Cave-Ayland
2021-03-04 22:11 ` [PATCH v3 40/42] esp: add trivial implementation of the ESP_RFLAGS register Mark Cave-Ayland
2021-03-04 22:11 ` Mark Cave-Ayland [this message]
2021-03-04 22:11 ` [PATCH v3 42/42] esp: add support for unaligned accesses Mark Cave-Ayland
2021-03-04 22:58 ` [PATCH v3 00/42] esp: consolidate PDMA transfer buffers and other fixes Philippe Mathieu-Daudé
2021-03-05  7:31   ` Mark Cave-Ayland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20210304221103.6369-42-mark.cave-ayland@ilande.co.uk \
    --to=mark.cave-ayland@ilande.co.uk \
    --cc=fam@euphon.net \
    --cc=laurent@vivier.eu \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

QEMU-Devel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/qemu-devel/0 qemu-devel/git/0.git
	git clone --mirror https://lore.kernel.org/qemu-devel/1 qemu-devel/git/1.git
	git clone --mirror https://lore.kernel.org/qemu-devel/2 qemu-devel/git/2.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 qemu-devel qemu-devel/ https://lore.kernel.org/qemu-devel \
		qemu-devel@nongnu.org
	public-inbox-index qemu-devel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.nongnu.qemu-devel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git