diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/sd/sd.c | 86 |
1 files changed, 67 insertions, 19 deletions
diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5137168d66..fad9cf1ee7 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -32,6 +32,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/cutils.h" #include "hw/irq.h" #include "hw/registerfields.h" #include "sysemu/block-backend.h" @@ -920,6 +921,11 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) sd->multi_blk_cnt = 0; } + if (sd_cmd_class[req.cmd] == 6 && FIELD_EX32(sd->ocr, OCR, CARD_CAPACITY)) { + /* Only Standard Capacity cards support class 6 commands */ + return sd_illegal; + } + switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ case 0: /* CMD0: GO_IDLE_STATE */ @@ -1165,12 +1171,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 17: /* CMD17: READ_SINGLE_BLOCK */ switch (sd->state) { case sd_transfer_state: + + if (addr + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + return sd_r1; + } + sd->state = sd_sendingdata_state; sd->data_start = addr; sd->data_offset = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; return sd_r1; default: @@ -1181,12 +1190,15 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) case 18: /* CMD18: READ_MULTIPLE_BLOCK */ switch (sd->state) { case sd_transfer_state: + + if (addr + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + return sd_r1; + } + sd->state = sd_sendingdata_state; sd->data_start = addr; sd->data_offset = 0; - - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; return sd_r1; default: @@ -1226,17 +1238,23 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Writing in SPI mode not implemented. */ if (sd->spi) break; + + if (addr + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + return sd_r1; + } + sd->state = sd_receivingdata_state; sd->data_start = addr; sd->data_offset = 0; sd->blk_written = 0; - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - if (sd_wp_addr(sd, sd->data_start)) + if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; - if (sd->csd[14] & 0x30) + } + if (sd->csd[14] & 0x30) { sd->card_status |= WP_VIOLATION; + } return sd_r1; default: @@ -1250,17 +1268,23 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) /* Writing in SPI mode not implemented. */ if (sd->spi) break; + + if (addr + sd->blk_len > sd->size) { + sd->card_status |= ADDRESS_ERROR; + return sd_r1; + } + sd->state = sd_receivingdata_state; sd->data_start = addr; sd->data_offset = 0; sd->blk_written = 0; - if (sd->data_start + sd->blk_len > sd->size) - sd->card_status |= ADDRESS_ERROR; - if (sd_wp_addr(sd, sd->data_start)) + if (sd_wp_addr(sd, sd->data_start)) { sd->card_status |= WP_VIOLATION; - if (sd->csd[14] & 0x30) + } + if (sd->csd[14] & 0x30) { sd->card_status |= WP_VIOLATION; + } return sd_r1; default: @@ -2100,12 +2124,36 @@ static void sd_realize(DeviceState *dev, Error **errp) return; } - if (sd->blk && blk_is_read_only(sd->blk)) { - error_setg(errp, "Cannot use read-only drive as SD card"); - return; - } - if (sd->blk) { + int64_t blk_size; + + if (blk_is_read_only(sd->blk)) { + error_setg(errp, "Cannot use read-only drive as SD card"); + return; + } + + blk_size = blk_getlength(sd->blk); + if (blk_size > 0 && !is_power_of_2(blk_size)) { + int64_t blk_size_aligned = pow2ceil(blk_size); + char *blk_size_str; + + blk_size_str = size_to_str(blk_size); + error_setg(errp, "Invalid SD card size: %s", blk_size_str); + g_free(blk_size_str); + + blk_size_str = size_to_str(blk_size_aligned); + error_append_hint(errp, + "SD card size has to be a power of 2, e.g. %s.\n" + "You can resize disk images with" + " 'qemu-img resize <imagefile> <new-size>'\n" + "(note that this will lose data if you make the" + " image smaller than it currently is).\n", + blk_size_str); + g_free(blk_size_str); + + return; + } + ret = blk_set_perm(sd->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, errp); if (ret < 0) { |