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:
commit
5ca634afcf
23
hw/sd/sd.c
23
hw/sd/sd.c
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue