summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bhyve
diff options
context:
space:
mode:
authorjhb <jhb@FreeBSD.org>2014-06-10 19:00:14 +0000
committerjhb <jhb@FreeBSD.org>2014-06-10 19:00:14 +0000
commit986f765d45db8448cc0061b91d6ecb6ecf10b0e7 (patch)
tree950708dfa975f1d853259f54113599b2fa838ce9 /usr.sbin/bhyve
parent3320f64ddd537b24933f71d4c774e68755ead862 (diff)
downloadFreeBSD-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/bhyve')
-rw-r--r--usr.sbin/bhyve/pci_ahci.c62
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);
OpenPOWER on IntegriCloud