Commit eff58d3d9681 ("hw/sd/sd: Allow multi-byte read/write for generic
paths") allowed for transferring multiple bytes at once at the choice of
the SD host implementation. Take advantage of that in the multi-block
commands CMD18 and CMD25 by transferring up to one block rather than one
byte at a time.

Note that the drive limits and write-protect groups were already checked
at the block boundary only. In between, bytes were accumulated up to one
block and the block was trustfully transferred once full. This patch
optimizes the accumulation process to fill a whole block at once if
possible, so no further checks are required.

This patch is based on 
https://lore.kernel.org/qemu-devel/[email protected]/

Signed-off-by: Christian Speich <[email protected]>
Signed-off-by: Bernhard Beschow <[email protected]>
---
 hw/sd/sd.c | 27 ++++++++++-----------------
 1 file changed, 10 insertions(+), 17 deletions(-)

diff --git a/hw/sd/sd.c b/hw/sd/sd.c
index b1b3b1226a..759db66e3d 100644
--- a/hw/sd/sd.c
+++ b/hw/sd/sd.c
@@ -2663,7 +2663,6 @@ static size_t sd_write_data(SDState *sd, const void *buf, 
size_t length)
 {
     unsigned int partition_access;
     int i;
-    const uint8_t *value = buf;
 
     if (!sd->blk || !blk_is_inserted(sd->blk)) {
         return length;
@@ -2695,12 +2694,6 @@ static size_t sd_write_data(SDState *sd, const void 
*buf, size_t length)
         break;
 
     case 25:  /* CMD25:  WRITE_MULTIPLE_BLOCK */
-        /*
-         * Only read one byte at a time. We will be called again with the
-         * remaining.
-         */
-        length = 1;
-
         if (sd->data_offset == 0) {
             /* Start of the block - let's check the address is valid */
             if (!address_in_range(sd, "WRITE_MULTIPLE_BLOCK",
@@ -2714,7 +2707,11 @@ static size_t sd_write_data(SDState *sd, const void 
*buf, size_t length)
                 }
             }
         }
-        sd->data[sd->data_offset++] = value[0];
+
+        length = MIN(sd->blk_len - sd->data_offset, length);
+        memcpy(sd->data + sd->data_offset, buf, length);
+        sd->data_offset += length;
+
         if (sd->data_offset >= sd->blk_len) {
             /* TODO: Check CRC before committing */
             sd->state = sd_programming_state;
@@ -2811,7 +2808,6 @@ static size_t sd_read_data(SDState *sd, void *buf, size_t 
length)
     const uint8_t dummy_byte = 0x00;
     unsigned int partition_access;
     uint32_t io_len;
-    uint8_t *value = buf;
 
     if (!sd->blk || !blk_is_inserted(sd->blk)) {
         memset(buf, dummy_byte, length);
@@ -2851,16 +2847,10 @@ static size_t sd_read_data(SDState *sd, void *buf, 
size_t length)
         break;
 
     case 18:  /* CMD18:  READ_MULTIPLE_BLOCK */
-        /*
-         * We will only read one byte at a time. We will be called again with
-         * the remaining buffer.
-         */
-        length = 1;
-
         if (sd->data_offset == 0) {
             if (!address_in_range(sd, "READ_MULTIPLE_BLOCK",
                                   sd->data_start, io_len)) {
-                *value = dummy_byte;
+                memset(buf, dummy_byte, length);
                 return length;
             }
             partition_access = sd->ext_csd[EXT_CSD_PART_CONFIG]
@@ -2871,7 +2861,10 @@ static size_t sd_read_data(SDState *sd, void *buf, 
size_t length)
                 sd_blk_read(sd, sd->data_start, io_len);
             }
         }
-        *value = sd->data[sd->data_offset++];
+
+        length = MIN(io_len - sd->data_offset, length);
+        memcpy(buf, sd->data + sd->data_offset, length);
+        sd->data_offset += length;
 
         if (sd->data_offset >= io_len) {
             sd->data_start += io_len;
-- 
2.56.0


Reply via email to