summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bhyve
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bhyve')
-rw-r--r--usr.sbin/bhyve/pci_ahci.c101
-rw-r--r--usr.sbin/bhyve/pci_emul.c38
2 files changed, 101 insertions, 38 deletions
diff --git a/usr.sbin/bhyve/pci_ahci.c b/usr.sbin/bhyve/pci_ahci.c
index 12540a1..31e02f8 100644
--- a/usr.sbin/bhyve/pci_ahci.c
+++ b/usr.sbin/bhyve/pci_ahci.c
@@ -135,11 +135,13 @@ struct ahci_port {
char ident[20 + 1];
int atapi;
int reset;
+ int waitforclear;
int mult_sectors;
uint8_t xfermode;
uint8_t err_cfis[20];
uint8_t sense_key;
uint8_t asc;
+ u_int ccs;
uint32_t pending;
uint32_t clb;
@@ -204,6 +206,8 @@ struct pci_ahci_softc {
};
#define ahci_ctx(sc) ((sc)->asc_pi->pi_vmctx)
+static void ahci_handle_port(struct ahci_port *p);
+
static inline void lba_to_msf(uint8_t *buf, int lba)
{
lba += 150;
@@ -269,22 +273,26 @@ ahci_write_fis(struct ahci_port *p, enum sata_fis_type ft, uint8_t *fis)
case FIS_TYPE_REGD2H:
offset = 0x40;
len = 20;
- irq = AHCI_P_IX_DHR;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_DHR : 0;
break;
case FIS_TYPE_SETDEVBITS:
offset = 0x58;
len = 8;
- irq = AHCI_P_IX_SDB;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0;
break;
case FIS_TYPE_PIOSETUP:
offset = 0x20;
len = 20;
- irq = 0;
+ irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0;
break;
default:
WPRINTF("unsupported fis type %d\n", ft);
return;
}
+ if (fis[2] & ATA_S_ERROR) {
+ p->waitforclear = 1;
+ irq |= AHCI_P_IX_TFE;
+ }
memcpy(p->rfis + offset, fis, len);
if (irq) {
p->is |= irq;
@@ -309,22 +317,23 @@ ahci_write_fis_sdb(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
uint8_t error;
error = (tfd >> 8) & 0xff;
+ tfd &= 0x77;
memset(fis, 0, sizeof(fis));
fis[0] = FIS_TYPE_SETDEVBITS;
fis[1] = (1 << 6);
- fis[2] = tfd & 0x77;
+ fis[2] = tfd;
fis[3] = error;
if (fis[2] & ATA_S_ERROR) {
- p->is |= AHCI_P_IX_TFE;
p->err_cfis[0] = slot;
- p->err_cfis[2] = tfd & 0x77;
+ p->err_cfis[2] = tfd;
p->err_cfis[3] = error;
memcpy(&p->err_cfis[4], cfis + 4, 16);
} else {
*(uint32_t *)(fis + 4) = (1 << slot);
p->sact &= ~(1 << slot);
}
- p->tfd = tfd;
+ p->tfd &= ~0x77;
+ p->tfd |= tfd;
ahci_write_fis(p, FIS_TYPE_SETDEVBITS, fis);
}
@@ -351,7 +360,6 @@ ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
fis[12] = cfis[12];
fis[13] = cfis[13];
if (fis[2] & ATA_S_ERROR) {
- p->is |= AHCI_P_IX_TFE;
p->err_cfis[0] = 0x80;
p->err_cfis[2] = tfd & 0xff;
p->err_cfis[3] = error;
@@ -363,6 +371,21 @@ ahci_write_fis_d2h(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t tfd)
}
static void
+ahci_write_fis_d2h_ncq(struct ahci_port *p, int slot)
+{
+ uint8_t fis[20];
+
+ p->tfd = ATA_S_READY | ATA_S_DSC;
+ memset(fis, 0, sizeof(fis));
+ fis[0] = FIS_TYPE_REGD2H;
+ fis[1] = 0; /* No interrupt */
+ fis[2] = p->tfd; /* Status */
+ fis[3] = 0; /* No error */
+ p->ci &= ~(1 << slot);
+ ahci_write_fis(p, FIS_TYPE_REGD2H, fis);
+}
+
+static void
ahci_write_reset_fis_d2h(struct ahci_port *p)
{
uint8_t fis[20];
@@ -389,9 +412,11 @@ ahci_check_stopped(struct ahci_port *p)
*/
if (!(p->cmd & AHCI_P_CMD_ST)) {
if (p->pending == 0) {
+ p->ccs = 0;
p->cmd &= ~(AHCI_P_CMD_CR | AHCI_P_CMD_CCS_MASK);
p->ci = 0;
p->sact = 0;
+ p->waitforclear = 0;
}
}
}
@@ -418,7 +443,8 @@ ahci_port_stop(struct ahci_port *p)
slot = aior->slot;
cfis = aior->cfis;
if (cfis[2] == ATA_WRITE_FPDMA_QUEUED ||
- cfis[2] == ATA_READ_FPDMA_QUEUED)
+ cfis[2] == ATA_READ_FPDMA_QUEUED ||
+ cfis[2] == ATA_SEND_FPDMA_QUEUED)
ncq = 1;
if (ncq)
@@ -489,6 +515,9 @@ ahci_reset(struct pci_ahci_softc *sc)
for (i = 0; i < sc->ports; i++) {
sc->port[i].ie = 0;
sc->port[i].is = 0;
+ sc->port[i].cmd = (AHCI_P_CMD_SUD | AHCI_P_CMD_POD);
+ if (sc->port[i].bctx)
+ sc->port[i].cmd |= AHCI_P_CMD_CPS;
sc->port[i].sctl = 0;
ahci_port_reset(&sc->port[i]);
}
@@ -585,12 +614,13 @@ ahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
struct ahci_cmd_hdr *hdr;
uint64_t lba;
uint32_t len;
- int err, ncq, readop;
+ int err, first, ncq, readop;
prdt = (struct ahci_prdt_entry *)(cfis + 0x80);
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE);
ncq = 0;
readop = 1;
+ first = (done == 0);
if (cfis[2] == ATA_WRITE || cfis[2] == ATA_WRITE48 ||
cfis[2] == ATA_WRITE_MUL || cfis[2] == ATA_WRITE_MUL48 ||
@@ -651,14 +681,14 @@ ahci_handle_rw(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done)
/* Stuff request onto busy list. */
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
+ if (ncq && first)
+ ahci_write_fis_d2h_ncq(p, slot);
+
if (readop)
err = blockif_read(p->bctx, breq);
else
err = blockif_write(p->bctx, breq);
assert(err == 0);
-
- if (ncq)
- p->ci &= ~(1 << slot);
}
static void
@@ -731,15 +761,18 @@ ahci_handle_dsm_trim(struct ahci_port *p, int slot, uint8_t *cfis, uint32_t done
uint8_t *entry;
uint64_t elba;
uint32_t len, elen;
- int err;
+ int err, first, ncq;
uint8_t buf[512];
+ first = (done == 0);
if (cfis[2] == ATA_DATA_SET_MANAGEMENT) {
len = (uint16_t)cfis[13] << 8 | cfis[12];
len *= 512;
+ ncq = 0;
} else { /* ATA_SEND_FPDMA_QUEUED */
len = (uint16_t)cfis[11] << 8 | cfis[3];
len *= 512;
+ ncq = 1;
}
read_prdt(p, slot, cfis, buf, sizeof(buf));
@@ -758,6 +791,8 @@ next:
ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC);
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ if (!first)
+ ahci_handle_port(p);
return;
}
goto next;
@@ -789,6 +824,9 @@ next:
*/
TAILQ_INSERT_HEAD(&p->iobhd, aior, io_blist);
+ if (ncq && first)
+ ahci_write_fis_d2h_ncq(p, slot);
+
err = blockif_delete(p->bctx, breq);
assert(err == 0);
}
@@ -1544,6 +1582,7 @@ static void
ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis)
{
+ p->tfd |= ATA_S_BUSY;
switch (cfis[2]) {
case ATA_ATA_IDENTIFY:
handle_identify(p, slot, cfis);
@@ -1725,20 +1764,23 @@ ahci_handle_slot(struct ahci_port *p, int slot)
static void
ahci_handle_port(struct ahci_port *p)
{
- int i;
if (!(p->cmd & AHCI_P_CMD_ST))
return;
/*
* Search for any new commands to issue ignoring those that
- * are already in-flight.
+ * are already in-flight. Stop if device is busy or in error.
*/
- for (i = 0; (i < 32) && p->ci; i++) {
- if ((p->ci & (1 << i)) && !(p->pending & (1 << i))) {
+ for (; (p->ci & ~p->pending) != 0; p->ccs = ((p->ccs + 1) & 31)) {
+ if ((p->tfd & (ATA_S_BUSY | ATA_S_DRQ)) != 0)
+ break;
+ if (p->waitforclear)
+ break;
+ if ((p->ci & ~p->pending & (1 << p->ccs)) != 0) {
p->cmd &= ~AHCI_P_CMD_CCS_MASK;
- p->cmd |= i << AHCI_P_CMD_CCS_SHIFT;
- ahci_handle_slot(p, i);
+ p->cmd |= p->ccs << AHCI_P_CMD_CCS_SHIFT;
+ ahci_handle_slot(p, p->ccs);
}
}
}
@@ -1815,6 +1857,7 @@ ata_ioreq_cb(struct blockif_req *br, int err)
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ ahci_handle_port(p);
out:
pthread_mutex_unlock(&sc->mtx);
DPRINTF("%s exit\n", __func__);
@@ -1876,6 +1919,7 @@ atapi_ioreq_cb(struct blockif_req *br, int err)
p->pending &= ~(1 << slot);
ahci_check_stopped(p);
+ ahci_handle_port(p);
out:
pthread_mutex_unlock(&sc->mtx);
DPRINTF("%s exit\n", __func__);
@@ -1940,8 +1984,15 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
break;
case AHCI_P_CMD:
{
- p->cmd = value;
-
+ p->cmd &= ~(AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
+ AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
+ AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
+ AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK);
+ p->cmd |= (AHCI_P_CMD_ST | AHCI_P_CMD_SUD | AHCI_P_CMD_POD |
+ AHCI_P_CMD_CLO | AHCI_P_CMD_FRE | AHCI_P_CMD_APSTE |
+ AHCI_P_CMD_ATAPI | AHCI_P_CMD_DLAE | AHCI_P_CMD_ALPE |
+ AHCI_P_CMD_ASP | AHCI_P_CMD_ICC_MASK) & value;
+
if (!(value & AHCI_P_CMD_ST)) {
ahci_port_stop(p);
} else {
@@ -1965,10 +2016,14 @@ pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value)
}
if (value & AHCI_P_CMD_CLO) {
- p->tfd = 0;
+ p->tfd &= ~(ATA_S_BUSY | ATA_S_DRQ);
p->cmd &= ~AHCI_P_CMD_CLO;
}
+ if (value & AHCI_P_CMD_ICC_MASK) {
+ p->cmd &= ~AHCI_P_CMD_ICC_MASK;
+ }
+
ahci_handle_port(p);
break;
}
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index a08ac4c..03ff0c0 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -1938,7 +1938,7 @@ INOUT_PORT(pci_cfgdata, CONF1_DATA_PORT+3, IOPORT_F_INOUT, pci_emul_cfgdata);
#define DMEMSZ 4096
struct pci_emul_dsoftc {
uint8_t ioregs[DIOSZ];
- uint8_t memregs[DMEMSZ];
+ uint8_t memregs[2][DMEMSZ];
};
#define PCI_EMUL_MSI_MSGS 4
@@ -1967,6 +1967,9 @@ pci_emul_dinit(struct vmctx *ctx, struct pci_devinst *pi, char *opts)
error = pci_emul_alloc_bar(pi, 1, PCIBAR_MEM32, DMEMSZ);
assert(error == 0);
+ error = pci_emul_alloc_bar(pi, 2, PCIBAR_MEM32, DMEMSZ);
+ assert(error == 0);
+
return (0);
}
@@ -2006,21 +2009,23 @@ pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
}
}
- if (baridx == 1) {
+ if (baridx == 1 || baridx == 2) {
if (offset + size > DMEMSZ) {
printf("diow: memw too large, offset %ld size %d\n",
offset, size);
return;
}
+ i = baridx - 1; /* 'memregs' index */
+
if (size == 1) {
- sc->memregs[offset] = value;
+ sc->memregs[i][offset] = value;
} else if (size == 2) {
- *(uint16_t *)&sc->memregs[offset] = value;
+ *(uint16_t *)&sc->memregs[i][offset] = value;
} else if (size == 4) {
- *(uint32_t *)&sc->memregs[offset] = value;
+ *(uint32_t *)&sc->memregs[i][offset] = value;
} else if (size == 8) {
- *(uint64_t *)&sc->memregs[offset] = value;
+ *(uint64_t *)&sc->memregs[i][offset] = value;
} else {
printf("diow: memw unknown size %d\n", size);
}
@@ -2030,7 +2035,7 @@ pci_emul_diow(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
*/
}
- if (baridx > 1) {
+ if (baridx > 2) {
printf("diow: unknown bar idx %d\n", baridx);
}
}
@@ -2041,6 +2046,7 @@ pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
{
struct pci_emul_dsoftc *sc = pi->pi_arg;
uint32_t value;
+ int i;
if (baridx == 0) {
if (offset + size > DIOSZ) {
@@ -2059,29 +2065,31 @@ pci_emul_dior(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx,
printf("dior: ior unknown size %d\n", size);
}
}
-
- if (baridx == 1) {
+
+ if (baridx == 1 || baridx == 2) {
if (offset + size > DMEMSZ) {
printf("dior: memr too large, offset %ld size %d\n",
offset, size);
return (0);
}
-
+
+ i = baridx - 1; /* 'memregs' index */
+
if (size == 1) {
- value = sc->memregs[offset];
+ value = sc->memregs[i][offset];
} else if (size == 2) {
- value = *(uint16_t *) &sc->memregs[offset];
+ value = *(uint16_t *) &sc->memregs[i][offset];
} else if (size == 4) {
- value = *(uint32_t *) &sc->memregs[offset];
+ value = *(uint32_t *) &sc->memregs[i][offset];
} else if (size == 8) {
- value = *(uint64_t *) &sc->memregs[offset];
+ value = *(uint64_t *) &sc->memregs[i][offset];
} else {
printf("dior: ior unknown size %d\n", size);
}
}
- if (baridx > 1) {
+ if (baridx > 2) {
printf("dior: unknown bar idx %d\n", baridx);
return (0);
}
OpenPOWER on IntegriCloud