diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-10-22 11:13:24 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-10-22 11:13:25 +0100 |
commit | eec4682e9977ea4e57d7238fba2782e6f2f3b0d0 (patch) | |
tree | 3e8dc5a34291c075167d071af671b0e15ea1a835 /hw | |
parent | 02aa56c4bc409d5822d39e734fc13a2b26cdd171 (diff) | |
parent | 84816fb63e5c57159b469a66052d1b2bc862ef77 (diff) |
Merge remote-tracking branch 'remotes/philmd-gitlab/tags/sd-next-20201021' into staging
SD/MMC patches
Fix two heap-overflow reported by Alexander Bulekov while fuzzing:
- https://bugs.launchpad.net/qemu/+bug/1892960
- https://bugs.launchpad.net/qemu/+bug/1895310
CI jobs results:
. https://cirrus-ci.com/build/6399328187056128
. https://gitlab.com/philmd/qemu/-/pipelines/205701966
. https://travis-ci.org/github/philmd/qemu/builds/737708930
# gpg: Signature made Wed 21 Oct 2020 18:33:08 BST
# 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-gitlab/tags/sd-next-20201021:
hw/sd/sdcard: Assert if accessing an illegal group
hw/sd/sdcard: Do not attempt to erase out of range addresses
hw/sd/sdcard: Reset both start/end addresses on error
hw/sd/sdcard: Do not use legal address '0' for INVALID_ADDRESS
hw/sd/sdcard: Introduce the INVALID_ADDRESS definition
hw/sd/sdcard: Add trace event for ERASE command (CMD38)
hw/sd/sdhci: Yield if interrupt delivered during multiple transfer
hw/sd/sdhci: Let sdhci_update_irq() return if IRQ was delivered
hw/sd/sdhci: Resume pending DMA transfers on MMIO accesses
hw/sd/sdhci: Stop multiple transfers when block count is cleared
hw/sd/sdhci: Fix DMA Transfer Block Size field
hw/sd/sdhci: Document the datasheet used
hw/sd/sdhci: Fix qemu_log_mask() format string
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw')
-rw-r--r-- | hw/sd/sd.c | 30 | ||||
-rw-r--r-- | hw/sd/sdhci.c | 41 | ||||
-rw-r--r-- | hw/sd/trace-events | 2 |
3 files changed, 58 insertions, 15 deletions
diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0012882222..c3febed243 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -53,6 +53,8 @@ #define SDSC_MAX_CAPACITY (2 * GiB) +#define INVALID_ADDRESS UINT32_MAX + typedef enum { sd_r0 = 0, /* no response */ sd_r1, /* normal response command */ @@ -575,8 +577,8 @@ static void sd_reset(DeviceState *dev) sd->wpgrps_size = sect; sd->wp_groups = bitmap_new(sd->wpgrps_size); memset(sd->function_group, 0, sizeof(sd->function_group)); - sd->erase_start = 0; - sd->erase_end = 0; + sd->erase_start = INVALID_ADDRESS; + sd->erase_end = INVALID_ADDRESS; sd->size = size; sd->blk_len = 0x200; sd->pwd_len = 0; @@ -664,8 +666,8 @@ static int sd_vmstate_pre_load(void *opaque) static const VMStateDescription sd_vmstate = { .name = "sd-card", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .pre_load = sd_vmstate_pre_load, .fields = (VMStateField[]) { VMSTATE_UINT32(mode, SDState), @@ -749,9 +751,12 @@ static void sd_erase(SDState *sd) uint64_t erase_start = sd->erase_start; uint64_t erase_end = sd->erase_end; - trace_sdcard_erase(); - if (!sd->erase_start || !sd->erase_end) { + trace_sdcard_erase(sd->erase_start, sd->erase_end); + if (sd->erase_start == INVALID_ADDRESS + || sd->erase_end == INVALID_ADDRESS) { sd->card_status |= ERASE_SEQ_ERROR; + sd->erase_start = INVALID_ADDRESS; + sd->erase_end = INVALID_ADDRESS; return; } @@ -761,13 +766,21 @@ static void sd_erase(SDState *sd) erase_end *= 512; } + if (sd->erase_start > sd->size || sd->erase_end > sd->size) { + sd->card_status |= OUT_OF_RANGE; + sd->erase_start = INVALID_ADDRESS; + sd->erase_end = INVALID_ADDRESS; + return; + } + erase_start = sd_addr_to_wpnum(erase_start); erase_end = sd_addr_to_wpnum(erase_end); - sd->erase_start = 0; - sd->erase_end = 0; + sd->erase_start = INVALID_ADDRESS; + sd->erase_end = INVALID_ADDRESS; sd->csd[14] |= 0x40; for (i = erase_start; i <= erase_end; i++) { + assert(i < sd->wpgrps_size); if (test_bit(i, sd->wp_groups)) { sd->card_status |= WP_ERASE_SKIP; } @@ -782,6 +795,7 @@ static uint32_t sd_wpbits(SDState *sd, uint64_t addr) wpnum = sd_addr_to_wpnum(addr); for (i = 0; i < 32; i++, wpnum++, addr += WPGROUP_SIZE) { + assert(wpnum < sd->wpgrps_size); if (addr < sd->size && test_bit(wpnum, sd->wp_groups)) { ret |= (1 << i); } diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 6900213083..2f8b74a84f 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1,6 +1,8 @@ /* * SD Association Host Standard Specification v2.0 controller emulation * + * Datasheet: PartA2_SD_Host_Controller_Simplified_Specification_Ver2.00.pdf + * * Copyright (c) 2011 Samsung Electronics Co., Ltd. * Mitsyanko Igor <i.mitsyanko@samsung.com> * Peter A.G. Crosthwaite <peter.crosthwaite@petalogix.com> @@ -216,9 +218,14 @@ static uint8_t sdhci_slotint(SDHCIState *s) ((s->norintsts & SDHC_NIS_REMOVE) && (s->wakcon & SDHC_WKUP_ON_RMV)); } -static inline void sdhci_update_irq(SDHCIState *s) +/* Return true if IRQ was pending and delivered */ +static bool sdhci_update_irq(SDHCIState *s) { - qemu_set_irq(s->irq, sdhci_slotint(s)); + bool pending = sdhci_slotint(s); + + qemu_set_irq(s->irq, pending); + + return pending; } static void sdhci_raise_insertion_irq(void *opaque) @@ -729,6 +736,12 @@ static void sdhci_do_adma(SDHCIState *s) ADMADescr dscr = {}; int i; + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN && !s->blkcnt) { + /* Stop Multiple Transfer */ + sdhci_end_transfer(s); + return; + } + for (i = 0; i < SDHC_ADMA_DESCS_PER_DELAY; ++i) { s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH; @@ -754,7 +767,6 @@ static void sdhci_do_adma(SDHCIState *s) switch (dscr.attr & SDHC_ADMA_ATTR_ACT_MASK) { case SDHC_ADMA_ATTR_ACT_TRAN: /* data transfer */ - if (s->trnmod & SDHC_TRNS_READ) { while (length) { if (s->data_count == 0) { @@ -825,7 +837,10 @@ static void sdhci_do_adma(SDHCIState *s) s->norintsts |= SDHC_NIS_DMA; } - sdhci_update_irq(s); + if (sdhci_update_irq(s) && !(dscr.attr & SDHC_ADMA_ATTR_END)) { + /* IRQ delivered, reschedule current transfer */ + break; + } } /* ADMA transfer terminates if blkcnt == 0 or by END attribute */ @@ -941,11 +956,21 @@ sdhci_buff_access_is_sequential(SDHCIState *s, unsigned byte_num) return true; } +static void sdhci_resume_pending_transfer(SDHCIState *s) +{ + timer_del(s->transfer_timer); + sdhci_data_transfer(s); +} + static uint64_t sdhci_read(void *opaque, hwaddr offset, unsigned size) { SDHCIState *s = (SDHCIState *)opaque; uint32_t ret = 0; + if (timer_pending(s->transfer_timer)) { + sdhci_resume_pending_transfer(s); + } + switch (offset & ~0x3) { case SDHC_SYSAD: ret = s->sdmasysad; @@ -1089,6 +1114,10 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) uint32_t value = val; value <<= shift; + if (timer_pending(s->transfer_timer)) { + sdhci_resume_pending_transfer(s); + } + switch (offset & ~0x3) { case SDHC_SYSAD: s->sdmasysad = (s->sdmasysad & mask) | value; @@ -1105,14 +1134,14 @@ sdhci_write(void *opaque, hwaddr offset, uint64_t val, unsigned size) break; case SDHC_BLKSIZE: if (!TRANSFERRING_DATA(s->prnsts)) { - MASKED_WRITE(s->blksize, mask, value); + MASKED_WRITE(s->blksize, mask, extract32(value, 0, 12)); MASKED_WRITE(s->blkcnt, mask >> 16, value >> 16); } /* Limit block size to the maximum buffer size */ if (extract32(s->blksize, 0, 12) > s->buf_maxsz) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Size 0x%x is larger than " - "the maximum buffer 0x%x", __func__, s->blksize, + "the maximum buffer 0x%x\n", __func__, s->blksize, s->buf_maxsz); s->blksize = deposit32(s->blksize, 0, 12, s->buf_maxsz); diff --git a/hw/sd/trace-events b/hw/sd/trace-events index a87d7355fb..96c7ea5e52 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -46,7 +46,7 @@ sdcard_reset(void) "" sdcard_set_blocklen(uint16_t length) "0x%03x" sdcard_inserted(bool readonly) "read_only: %u" sdcard_ejected(void) "" -sdcard_erase(void) "" +sdcard_erase(uint32_t first, uint32_t last) "addr first 0x%" PRIx32" last 0x%" PRIx32 sdcard_lock(void) "" sdcard_unlock(void) "" sdcard_read_block(uint64_t addr, uint32_t len) "addr 0x%" PRIx64 " size 0x%x" |