SD/MMC patches queue

- Fix build error when DEBUG_SD is on
 - Perform SD ERASE operation
 - SDHCI ADMA heap buffer overflow
   (CVE-2020-17380, CVE-2020-25085, CVE-2021-3409)
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmBY0EcACgkQ4+MsLN6t
 wN438w//TlfUYqwYFLQ5DXtjyWev2tTfRuZrVM7+9pbeZrIN9nG+EPd8eimgr/Mp
 fmJ/xnhjq7nCZlTk+clpr3uSkchFKwHYHugEAKA2J4+bByWHSqgkIHtt6aW5zuEj
 oFSfmjx/hfKuZoYZPLj3I035kXbolZn3q8NwtGL83u5Jw+9dFWhxEoFkDqFeWKtn
 lP7pHFXrW5+kvz33f6KILnlkjPe5ues7IUgi/QbtDwUd2gw6kgIo9OQa1nCjfzNu
 m/ITEL1g8Vcj1X0HUVUAJcOWNjbesq/KTjaf4ZitJAAvvk1l95G3wT1HiUZTKaf8
 iOoCtp5BqfCqV3koJO91kceD7bYf9TokueBcPdow7c2ekbbi30LL51uQEAeJRpHC
 sut3jfmmQWC5shNf6KKmUJsNSRlTHD5xQx5UTxwsNQuyb5KSNuF/LP3iFGAsCouh
 bu4DQfofAm5MCjzz1gkoGiJBGLrvJVnghbxF8anBvFL+jdYkUfpuuGXwyQm5fuJB
 l+k25wBLHbWzpf+9EUiDpOObXEhb8jyxB8ihTHE7sRTktBzRgnRkFQ3RCcybOypt
 dpb1ITN647XH9mGjgzxKjL+UuQb4loUl1Qih4kFuHRlVS7VncmulrJ2Z7D78WZMk
 f7ObM0kC5bakWOAP/QoMfytlRDPtorpL+bTFN6NEOpyTro+4Pcs=
 =gvrj
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/philmd/tags/sdmmc-20210322' into staging

SD/MMC patches queue

- Fix build error when DEBUG_SD is on
- Perform SD ERASE operation
- SDHCI ADMA heap buffer overflow
  (CVE-2020-17380, CVE-2020-25085, CVE-2021-3409)

# gpg: Signature made Mon 22 Mar 2021 17:13:43 GMT
# 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

* remotes/philmd/tags/sdmmc-20210322:
  hw/sd: sdhci: Reset the data pointer of s->fifo_buffer[] when a different block size is programmed
  hw/sd: sdhci: Limit block size only when SDHC_BLKSIZE register is writable
  hw/sd: sdhci: Correctly set the controller status for ADMA
  hw/sd: sdhci: Don't write to SDHC_SYSAD register when transfer is in progress
  hw/sd: sdhci: Don't transfer any data when command time out
  hw/sd: sd: Actually perform the erase operation
  hw/sd: sd: Fix build error when DEBUG_SD is on

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2021-03-22 18:50:25 +00:00
commit 5ca634afcf
2 changed files with 50 additions and 26 deletions

View file

@ -47,6 +47,7 @@
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/log.h" #include "qemu/log.h"
#include "qemu/module.h" #include "qemu/module.h"
#include "qemu-common.h"
#include "sdmmc-internal.h" #include "sdmmc-internal.h"
#include "trace.h" #include "trace.h"
@ -762,10 +763,12 @@ static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len)
static void sd_erase(SDState *sd) static void sd_erase(SDState *sd)
{ {
int i;
uint64_t erase_start = sd->erase_start; uint64_t erase_start = sd->erase_start;
uint64_t erase_end = sd->erase_end; uint64_t erase_end = sd->erase_end;
bool sdsc = true; bool sdsc = true;
uint64_t wpnum;
uint64_t erase_addr;
int erase_len = 1 << HWBLOCK_SHIFT;
trace_sdcard_erase(sd->erase_start, sd->erase_end); trace_sdcard_erase(sd->erase_start, sd->erase_end);
if (sd->erase_start == INVALID_ADDRESS if (sd->erase_start == INVALID_ADDRESS
@ -794,17 +797,19 @@ static void sd_erase(SDState *sd)
sd->erase_end = INVALID_ADDRESS; sd->erase_end = INVALID_ADDRESS;
sd->csd[14] |= 0x40; sd->csd[14] |= 0x40;
/* Only SDSC cards support write protect groups */ memset(sd->data, 0xff, erase_len);
if (sdsc) { for (erase_addr = erase_start; erase_addr <= erase_end;
erase_start = sd_addr_to_wpnum(erase_start); erase_addr += erase_len) {
erase_end = sd_addr_to_wpnum(erase_end); if (sdsc) {
/* Only SDSC cards support write protect groups */
for (i = erase_start; i <= erase_end; i++) { wpnum = sd_addr_to_wpnum(erase_addr);
assert(i < sd->wpgrps_size); assert(wpnum < sd->wpgrps_size);
if (test_bit(i, sd->wp_groups)) { if (test_bit(wpnum, sd->wp_groups)) {
sd->card_status |= WP_ERASE_SKIP; sd->card_status |= WP_ERASE_SKIP;
continue;
} }
} }
BLK_WRITE_BLOCK(erase_addr, erase_len);
} }
} }

View file

@ -326,6 +326,7 @@ static void sdhci_send_command(SDHCIState *s)
SDRequest request; SDRequest request;
uint8_t response[16]; uint8_t response[16];
int rlen; int rlen;
bool timeout = false;
s->errintsts = 0; s->errintsts = 0;
s->acmd12errsts = 0; s->acmd12errsts = 0;
@ -349,6 +350,7 @@ static void sdhci_send_command(SDHCIState *s)
trace_sdhci_response16(s->rspreg[3], s->rspreg[2], trace_sdhci_response16(s->rspreg[3], s->rspreg[2],
s->rspreg[1], s->rspreg[0]); s->rspreg[1], s->rspreg[0]);
} else { } else {
timeout = true;
trace_sdhci_error("timeout waiting for command response"); trace_sdhci_error("timeout waiting for command response");
if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) { if (s->errintstsen & SDHC_EISEN_CMDTIMEOUT) {
s->errintsts |= SDHC_EIS_CMDTIMEOUT; s->errintsts |= SDHC_EIS_CMDTIMEOUT;
@ -369,7 +371,7 @@ static void sdhci_send_command(SDHCIState *s)
sdhci_update_irq(s); sdhci_update_irq(s);
if (s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) { if (!timeout && s->blksize && (s->cmdreg & SDHC_CMD_DATA_PRESENT)) {
s->data_count = 0; s->data_count = 0;
sdhci_data_transfer(s); sdhci_data_transfer(s);
} }
@ -766,7 +768,9 @@ static void sdhci_do_adma(SDHCIState *s)
switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) {
case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */
s->prnsts |= SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE;
if (s->trnmod & SDHC_TRNS_READ) { if (s->trnmod & SDHC_TRNS_READ) {
s->prnsts |= SDHC_DOING_READ;
while (length) { while (length) {
if (s->data_count == 0) { if (s->data_count == 0) {
sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size); sdbus_read_data(&s->sdbus, s->fifo_buffer, block_size);
@ -794,6 +798,7 @@ static void sdhci_do_adma(SDHCIState *s)
} }
} }
} else { } else {
s->prnsts |= SDHC_DOING_WRITE;
while (length) { while (length) {
begin = s->data_count; begin = s->data_count;
if ((length + begin) < block_size) { if ((length + begin) < block_size) {
@ -1119,31 +1124,45 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size)
switch (offset & ~0x3) { switch (offset & ~0x3) {
case SDHC_SYSAD: case SDHC_SYSAD:
s->sdmasysad = (s->sdmasysad & mask) | value; if (!TRANSFERRING_DATA(s->prnsts)) {
MASKED_WRITE(s->sdmasysad, mask, value); s->sdmasysad = (s->sdmasysad & mask) | value;
/* Writing to last byte of sdmasysad might trigger transfer */ MASKED_WRITE(s->sdmasysad, mask, value);
if (!(mask & 0xFF000000) && TRANSFERRING_DATA(s->prnsts) && s->blkcnt && /* Writing to last byte of sdmasysad might trigger transfer */
s->blksize && SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) { if (!(mask & 0xFF000000) && s->blkcnt && s->blksize &&
if (s->trnmod & SDHC_TRNS_MULTI) { SDHC_DMA_TYPE(s->hostctl1) == SDHC_CTRL_SDMA) {
sdhci_sdma_transfer_multi_blocks(s); if (s->trnmod & SDHC_TRNS_MULTI) {
} else { sdhci_sdma_transfer_multi_blocks(s);
sdhci_sdma_transfer_single_block(s); } else {
sdhci_sdma_transfer_single_block(s);
}
} }
} }
break; break;
case SDHC_BLKSIZE: case SDHC_BLKSIZE:
if (!TRANSFERRING_DATA(s->prnsts)) { if (!TRANSFERRING_DATA(s->prnsts)) {
uint16_t blksize = s->blksize;
MASKED_WRITE(s->blksize, mask, extract32(value, 0, 12)); MASKED_WRITE(s->blksize, mask, extract32(value, 0, 12));
MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16); MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16);
}
/* Limit block size to the maximum buffer size */ /* Limit block size to the maximum buffer size */
if (extract32(s->blksize, 0, 12) > s->buf_maxsz) { if (extract32(s->blksize, 0, 12) > s->buf_maxsz) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than "
"the maximum buffer 0x%x\n", __func__, s->blksize, "the maximum buffer 0x%x\n", __func__, s->blksize,
s->buf_maxsz); s->buf_maxsz);
s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz); s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz);
}
/*
* If the block size is programmed to a different value from
* the previous one, reset the data pointer of s->fifo_buffer[]
* so that s->fifo_buffer[] can be filled in using the new block
* size in the next transfer.
*/
if (blksize != s->blksize) {
s->data_count = 0;
}
} }
break; break;