On 6 August 2012 04:25, Peter A. G. Crosthwaite <peter.crosthwa...@petalogix.com> wrote: > From: Igor Mitsyanko <i.mitsya...@samsung.com> > > Device model for standard SD Host Controller Interface (SDHCI) compliant with > version 2.00 of SD association specification.
> +typedef struct ADMADescr { > + target_phys_addr_t addr; > + uint16_t length; > + uint8_t attr; > + uint8_t incr; > +} ADMADescr; > + > +static void get_adma_description(SDHCIState *s, ADMADescr *dscr) > +{ > + uint32_t adma1 = 0; > + uint64_t adma2 = 0; > + target_phys_addr_t entry_addr = (target_phys_addr_t)s->admasysaddr; > + > + switch (SDHC_DMA_TYPE(s->hostctl)) { > + case SDHC_CTRL_ADMA2_32: > + cpu_physical_memory_read(entry_addr, (uint8_t *)&adma2, > sizeof(adma2)); > + dscr->addr = (target_phys_addr_t)((adma2 >> 32) & 0xfffffffc); > + dscr->length = (uint16_t)((adma2 >> 16) & 0xFFFF); > + dscr->attr = (uint8_t)(adma2 & 0x3F); > + dscr->incr = 8; > + break; > + case SDHC_CTRL_ADMA1_32: > + cpu_physical_memory_read(entry_addr, (uint8_t *)&adma1, > sizeof(adma1)); > + dscr->addr = (target_phys_addr_t)(adma1 & 0xFFFFF000); > + dscr->attr = (uint8_t)(adma1 & 0x3F); > + dscr->incr = 4; > + if ((dscr->attr & SDHC_ADMA_ATTR_ACT_MASK) == > SDHC_ADMA_ATTR_SET_LEN) { > + dscr->length = (uint16_t)((adma1 >> 12) & 0xFFFF); > + } else { > + dscr->length = 4096; > + } > + break; > + case SDHC_CTRL_ADMA2_64: > + cpu_physical_memory_read(entry_addr, (uint8_t *)(&dscr->attr), 1); > + cpu_physical_memory_read(entry_addr + 2, (uint8_t *)(&dscr->length), > 2); > + cpu_physical_memory_read(entry_addr + 4, (uint8_t *)(&dscr->addr), > 8); > + dscr->attr &= 0xfffffff8; > + dscr->incr = 12; > + break; > + } > +} > + > +/* Advanced DMA data transfer */ > +static void sdhci_start_adma(SDHCIState *s) > +{ > + unsigned int n, begin, length; > + const uint16_t block_size = s->blksize & 0x0fff; > + ADMADescr dscr; > + s->admaerr &= ~SDHC_ADMAERR_LENGTH_MISMATCH; > + > + while (1) { > + get_adma_description(s, &dscr); > + DPRINT_L2("ADMA loop: addr=" TARGET_FMT_plx ", len=%d, attr=%x\n", > + dscr.addr, dscr.length, dscr.attr); > + > + if ((dscr.attr & SDHC_ADMA_ATTR_VALID) == 0) { > + /* Indicate that error occurred in ST_FDS state */ > + s->admaerr &= ~SDHC_ADMAERR_STATE_MASK; > + s->admaerr |= SDHC_ADMAERR_STATE_ST_FDS; > + > + /* Generate ADMA error interrupt */ > + if (s->errintstsen & SDHC_EISEN_ADMAERR) { > + s->errintsts |= SDHC_EIS_ADMAERR; > + s->norintsts |= SDHC_NIS_ERR; > + } > + > + sdhci_update_irq(s); > + break; > + } > + > + length = dscr.length ? dscr.length : 65536; > + > + 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) { > + for (n = 0; n < block_size; n++) { > + s->fifo_buffer[n] = sd_read_data(s->card); > + } > + } > + begin = s->data_count; > + if ((length + begin) < block_size) { > + s->data_count = length + begin; > + length = 0; > + } else { > + s->data_count = block_size; > + length -= block_size - begin; > + } > + cpu_physical_memory_write(dscr.addr, > &s->fifo_buffer[begin], > + s->data_count - begin); > + dscr.addr += s->data_count - begin; > + if (s->data_count == block_size) { > + s->data_count = 0; > + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { > + s->blkcnt--; > + if (s->blkcnt == 0) { > + break; > + } > + } > + } > + } > + } else { > + while (length) { > + begin = s->data_count; > + if ((length + begin) < block_size) { > + s->data_count = length + begin; > + length = 0; > + } else { > + s->data_count = block_size; > + length -= block_size - begin; > + } > + cpu_physical_memory_read(dscr.addr, > + &s->fifo_buffer[begin], s->data_count); > + dscr.addr += s->data_count - begin; > + if (s->data_count == block_size) { > + for (n = 0; n < block_size; n++) { > + sd_write_data(s->card, s->fifo_buffer[n]); > + } > + s->data_count = 0; > + if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { > + s->blkcnt--; > + if (s->blkcnt == 0) { > + break; > + } > + } > + } > + } > + } > + s->admasysaddr += dscr.incr; > + break; > + case SDHC_ADMA_ATTR_ACT_LINK: /* link to next descriptor table */ > + s->admasysaddr = dscr.addr; > + DPRINT_L1("ADMA link: admasysaddr=0x%lx\n", s->admasysaddr); > + break; > + default: > + s->admasysaddr += dscr.incr; > + break; > + } > + > + /* ADMA transfer terminates if blkcnt == 0 or by END attribute */ > + if (((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && > + (s->blkcnt == 0)) || (dscr.attr & SDHC_ADMA_ATTR_END)) { > + DPRINT_L2("ADMA transfer completed\n"); > + if (length || ((dscr.attr & SDHC_ADMA_ATTR_END) && > + (s->trnmod & SDHC_TRNS_BLK_CNT_EN) && > + s->blkcnt != 0) || ((s->trnmod & SDHC_TRNS_BLK_CNT_EN) && > + s->blkcnt == 0 && (dscr.attr & SDHC_ADMA_ATTR_END) == 0)) { > + ERRPRINT("SD/MMC host ADMA length mismatch\n"); > + s->admaerr |= SDHC_ADMAERR_LENGTH_MISMATCH | > + SDHC_ADMAERR_STATE_ST_TFR; > + if (s->errintstsen & SDHC_EISEN_ADMAERR) { > + ERRPRINT("Set ADMA error flag\n"); > + s->errintsts |= SDHC_EIS_ADMAERR; > + s->norintsts |= SDHC_NIS_ERR; > + } > + > + sdhci_update_irq(s); > + } > + SDHCI_GET_CLASS(s)->end_data_transfer(s); > + break; > + } > + > + if (dscr.attr & SDHC_ADMA_ATTR_INT) { > + DPRINT_L1("ADMA interrupt: admasysaddr=0x%lx\n", s->admasysaddr); > + if (s->norintstsen & SDHC_NISEN_DMA) { > + s->norintsts |= SDHC_NIS_DMA; > + } > + > + sdhci_update_irq(s); > + break; > + } > + } > +} So I think the guest can make this loop never terminate if it sets up a loop of ACT_LINK descriptors, right? I don't know how we should handle this but I'm pretty sure "make qemu sit there forever not responding to anything and not resettable" isn't it. -- PMM