diff options
author | jhb <jhb@FreeBSD.org> | 2014-06-10 19:00:14 +0000 |
---|---|---|
committer | jhb <jhb@FreeBSD.org> | 2014-06-10 19:00:14 +0000 |
commit | 986f765d45db8448cc0061b91d6ecb6ecf10b0e7 (patch) | |
tree | 950708dfa975f1d853259f54113599b2fa838ce9 /usr.sbin | |
parent | 3320f64ddd537b24933f71d4c774e68755ead862 (diff) | |
download | FreeBSD-src-986f765d45db8448cc0061b91d6ecb6ecf10b0e7.zip FreeBSD-src-986f765d45db8448cc0061b91d6ecb6ecf10b0e7.tar.gz |
MFC 261000,261785,263238,263322,264302:
Various AHCI fixes:
- Fix issue with stale fields from a recycled request pulled off the
freelist.
- Provide an indication a "PIO Setup Device to Host FIS" occurred while
executing the IDENTIFY DEVICE and IDENTIFY PACKET DEVICE commands.
- Provide an indication a "D2H Register FIS" occurred during a SET FEATURES
command.
- Though there currently isn't a way to insert new media into an ATAPI
drive, at least pretend to support Asynchronous Notification (AN) to
avoid a guest needlessly polling for it.
- Don't reissue in-flight commands.
- Constrain the amount of data returned to what is actually available
not the size of the buffer.
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/bhyve/pci_ahci.c | 62 |
1 files changed, 54 insertions, 8 deletions
diff --git a/usr.sbin/bhyve/pci_ahci.c b/usr.sbin/bhyve/pci_ahci.c index 0109975..fb32bad 100644 --- a/usr.sbin/bhyve/pci_ahci.c +++ b/usr.sbin/bhyve/pci_ahci.c @@ -95,6 +95,13 @@ enum sata_fis_type { #define MODEPAGE_CD_CAPABILITIES 0x2A /* + * ATA commands + */ +#define ATA_SF_ENAB_SATA_SF 0x10 +#define ATA_SATA_SF_AN 0x05 +#define ATA_SF_DIS_SATA_SF 0x90 + +/* * Debug printf */ #ifdef AHCI_DEBUG @@ -127,6 +134,7 @@ struct ahci_port { uint8_t xfermode; uint8_t sense_key; uint8_t asc; + uint32_t pending; uint32_t clb; uint32_t clbu; @@ -254,6 +262,16 @@ ahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis) } static void +ahci_write_fis_piosetup(struct ahci_port *p) +{ + uint8_t fis[20]; + + memset(fis, 0, sizeof(fis)); + fis[0] = FIS_TYPE_PIOSETUP; + ahci_write_fis(p, FIS_TYPE_PIOSETUP, fis); +} + +static void ahci_write_fis_sdb(struct ahci_port *p, int slot, uint32_t tfd) { uint8_t fis[8]; @@ -454,6 +472,10 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done, if (iovcnt > BLOCKIF_IOV_MAX) { aior->prdtl = iovcnt - BLOCKIF_IOV_MAX; iovcnt = BLOCKIF_IOV_MAX; + /* + * Mark this command in-flight. + */ + p->pending |= 1 << slot; } else aior->prdtl = 0; breq->br_iovcnt = iovcnt; @@ -477,7 +499,7 @@ ahci_handle_dma(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done, err = blockif_write(p->bctx, breq); assert(err == 0); - if (!aior->prdtl && ncq) + if (ncq) p->ci &= ~(1 << slot); } @@ -497,6 +519,8 @@ ahci_handle_flush(struct ahci_port *p, int slot, uint8_t *cfis) aior->cfis = cfis; aior->slot = slot; aior->len = 0; + aior->done = 0; + aior->prdtl = 0; breq = &aior->io_req; err = blockif_flush(p->bctx, breq); @@ -519,12 +543,14 @@ write_prdt(struct ahci_port *p, int slot, uint8_t *cfis, for (i = 0; i < hdr->prdtl && len; i++) { uint8_t *ptr; uint32_t dbcsz; + int sublen; dbcsz = (prdt->dbc & DBCMASK) + 1; ptr = paddr_guest2host(ahci_ctx(p->pr_sc), prdt->dba, dbcsz); - memcpy(ptr, from, dbcsz); - len -= dbcsz; - from += dbcsz; + sublen = len < dbcsz ? len : dbcsz; + memcpy(ptr, from, sublen); + len -= sublen; + from += sublen; prdt++; } hdr->prdbc = size - len; @@ -585,6 +611,7 @@ handle_identify(struct ahci_port *p, int slot, uint8_t *cfis) buf[101] = (sectors >> 16); buf[102] = (sectors >> 32); buf[103] = (sectors >> 48); + ahci_write_fis_piosetup(p); write_prdt(p, slot, cfis, (void *)buf, sizeof(buf)); p->tfd = ATA_S_DSC | ATA_S_READY; p->is |= AHCI_P_IX_DP; @@ -627,6 +654,7 @@ handle_atapi_identify(struct ahci_port *p, int slot, uint8_t *cfis) buf[85] = (1 << 4); buf[87] = (1 << 14); buf[88] = (1 << 14 | 0x7f); + ahci_write_fis_piosetup(p); write_prdt(p, slot, cfis, (void *)buf, sizeof(buf)); p->tfd = ATA_S_DSC | ATA_S_READY; p->is |= AHCI_P_IX_DHR; @@ -1155,6 +1183,17 @@ ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis) case ATA_SETFEATURES: { switch (cfis[3]) { + case ATA_SF_ENAB_SATA_SF: + switch (cfis[12]) { + case ATA_SATA_SF_AN: + p->tfd = ATA_S_DSC | ATA_S_READY; + break; + default: + p->tfd = ATA_S_ERROR | ATA_S_READY; + p->tfd |= (ATA_ERROR_ABORT << 8); + break; + } + break; case ATA_SF_ENAB_WCACHE: case ATA_SF_DIS_WCACHE: case ATA_SF_ENAB_RCACHE: @@ -1180,9 +1219,7 @@ ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis) p->tfd |= (ATA_ERROR_ABORT << 8); break; } - p->is |= AHCI_P_IX_DP; - p->ci &= ~(1 << slot); - ahci_generate_intr(p->pr_sc); + ahci_write_fis_d2h(p, slot, cfis, p->tfd); break; } case ATA_SET_MULTI: @@ -1297,8 +1334,12 @@ ahci_handle_port(struct ahci_port *p) if (!(p->cmd & AHCI_P_CMD_ST)) return; + /* + * Search for any new commands to issue ignoring those that + * are already in-flight. + */ for (i = 0; (i < 32) && p->ci; i++) { - if (p->ci & (1 << i)) + if ((p->ci & (1 << i)) && !(p->pending & (1 << i))) ahci_handle_slot(p, i); } } @@ -1359,6 +1400,11 @@ ata_ioreq_cb(struct blockif_req *br, int err) p->serr |= (1 << slot); } + /* + * This command is now complete. + */ + p->pending &= ~(1 << slot); + if (ncq) { p->sact &= ~(1 << slot); ahci_write_fis_sdb(p, slot, tfd); |