Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_ahci.c
Show First 20 Lines • Show All 234 Lines • ▼ Show 20 Lines | ahci_generate_intr(struct pci_ahci_softc *sc, uint32_t mask) | ||||
uint32_t mmask; | uint32_t mmask; | ||||
/* Update global IS from PxIS/PxIE. */ | /* Update global IS from PxIS/PxIE. */ | ||||
for (i = 0; i < sc->ports; i++) { | for (i = 0; i < sc->ports; i++) { | ||||
p = &sc->port[i]; | p = &sc->port[i]; | ||||
if (p->is & p->ie) | if (p->is & p->ie) | ||||
sc->is |= (1 << i); | sc->is |= (1 << i); | ||||
} | } | ||||
DPRINTF("%s(%08x) %08x\n", __func__, mask, sc->is); | DPRINTF("%s(%08x) %08x\n\r", __func__, mask, sc->is); | ||||
/* If there is nothing enabled -- clear legacy interrupt and exit. */ | /* If there is nothing enabled -- clear legacy interrupt and exit. */ | ||||
if (sc->is == 0 || (sc->ghc & AHCI_GHC_IE) == 0) { | if (sc->is == 0 || (sc->ghc & AHCI_GHC_IE) == 0) { | ||||
if (sc->lintr) { | if (sc->lintr) { | ||||
pci_lintr_deassert(pi); | pci_lintr_deassert(pi); | ||||
sc->lintr = 0; | sc->lintr = 0; | ||||
} | } | ||||
return; | return; | ||||
Show All 25 Lines | |||||
*/ | */ | ||||
static void | static void | ||||
ahci_port_intr(struct ahci_port *p) | ahci_port_intr(struct ahci_port *p) | ||||
{ | { | ||||
struct pci_ahci_softc *sc = p->pr_sc; | struct pci_ahci_softc *sc = p->pr_sc; | ||||
struct pci_devinst *pi = sc->asc_pi; | struct pci_devinst *pi = sc->asc_pi; | ||||
int nmsg; | int nmsg; | ||||
DPRINTF("%s(%d) %08x/%08x %08x\n", __func__, | DPRINTF("%s(%d) %08x/%08x %08x\n\r", __func__, | ||||
p->port, p->is, p->ie, sc->is); | p->port, p->is, p->ie, sc->is); | ||||
/* If there is nothing enabled -- we are done. */ | /* If there is nothing enabled -- we are done. */ | ||||
if ((p->is & p->ie) == 0) | if ((p->is & p->ie) == 0) | ||||
return; | return; | ||||
/* In case of non-shared MSI always generate interrupt. */ | /* In case of non-shared MSI always generate interrupt. */ | ||||
nmsg = pci_msi_maxmsgnum(pi); | nmsg = pci_msi_maxmsgnum(pi); | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | case FIS_TYPE_SETDEVBITS: | ||||
irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0; | irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_SDB : 0; | ||||
break; | break; | ||||
case FIS_TYPE_PIOSETUP: | case FIS_TYPE_PIOSETUP: | ||||
offset = 0x20; | offset = 0x20; | ||||
len = 20; | len = 20; | ||||
irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0; | irq = (fis[1] & (1 << 6)) ? AHCI_P_IX_PS : 0; | ||||
break; | break; | ||||
default: | default: | ||||
WPRINTF("unsupported fis type %d\n", ft); | WPRINTF("unsupported fis type %d\n\r", ft); | ||||
return; | return; | ||||
} | } | ||||
if (fis[2] & ATA_S_ERROR) { | if (fis[2] & ATA_S_ERROR) { | ||||
p->waitforclear = 1; | p->waitforclear = 1; | ||||
irq |= AHCI_P_IX_TFE; | irq |= AHCI_P_IX_TFE; | ||||
} | } | ||||
memcpy(p->rfis + offset, fis, len); | memcpy(p->rfis + offset, fis, len); | ||||
if (irq) { | if (irq) { | ||||
▲ Show 20 Lines • Show All 1,243 Lines • ▼ Show 20 Lines | handle_packet_cmd(struct ahci_port *p, int slot, uint8_t *cfis) | ||||
acmd = cfis + 0x40; | acmd = cfis + 0x40; | ||||
#ifdef AHCI_DEBUG | #ifdef AHCI_DEBUG | ||||
{ | { | ||||
int i; | int i; | ||||
DPRINTF("ACMD:"); | DPRINTF("ACMD:"); | ||||
for (i = 0; i < 16; i++) | for (i = 0; i < 16; i++) | ||||
DPRINTF("%02x ", acmd[i]); | DPRINTF("%02x ", acmd[i]); | ||||
DPRINTF("\n"); | DPRINTF("\n\r"); | ||||
} | } | ||||
#endif | #endif | ||||
switch (acmd[0]) { | switch (acmd[0]) { | ||||
case TEST_UNIT_READY: | case TEST_UNIT_READY: | ||||
cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; | cfis[4] = (cfis[4] & ~7) | ATA_I_CMD | ATA_I_IN; | ||||
ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); | ahci_write_fis_d2h(p, slot, cfis, ATA_S_READY | ATA_S_DSC); | ||||
break; | break; | ||||
▲ Show 20 Lines • Show All 170 Lines • ▼ Show 20 Lines | ahci_handle_cmd(struct ahci_port *p, int slot, uint8_t *cfis) | ||||
case ATA_PACKET_CMD: | case ATA_PACKET_CMD: | ||||
if (!p->atapi) { | if (!p->atapi) { | ||||
ahci_write_fis_d2h(p, slot, cfis, | ahci_write_fis_d2h(p, slot, cfis, | ||||
(ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR); | (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR); | ||||
} else | } else | ||||
handle_packet_cmd(p, slot, cfis); | handle_packet_cmd(p, slot, cfis); | ||||
break; | break; | ||||
default: | default: | ||||
WPRINTF("Unsupported cmd:%02x\n", cfis[2]); | WPRINTF("Unsupported cmd:%02x\n\r", cfis[2]); | ||||
ahci_write_fis_d2h(p, slot, cfis, | ahci_write_fis_d2h(p, slot, cfis, | ||||
(ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR); | (ATA_E_ABORT << 8) | ATA_S_READY | ATA_S_ERROR); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
ahci_handle_slot(struct ahci_port *p, int slot) | ahci_handle_slot(struct ahci_port *p, int slot) | ||||
Show All 13 Lines | |||||
#ifdef AHCI_DEBUG | #ifdef AHCI_DEBUG | ||||
cfl = (hdr->flags & 0x1f) * 4; | cfl = (hdr->flags & 0x1f) * 4; | ||||
#endif | #endif | ||||
cfis = paddr_guest2host(ahci_ctx(sc), hdr->ctba, | cfis = paddr_guest2host(ahci_ctx(sc), hdr->ctba, | ||||
0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry)); | 0x80 + hdr->prdtl * sizeof(struct ahci_prdt_entry)); | ||||
#ifdef AHCI_DEBUG | #ifdef AHCI_DEBUG | ||||
prdt = (struct ahci_prdt_entry *)(cfis + 0x80); | prdt = (struct ahci_prdt_entry *)(cfis + 0x80); | ||||
DPRINTF("\ncfis:"); | DPRINTF("\n\rcfis:"); | ||||
for (i = 0; i < cfl; i++) { | for (i = 0; i < cfl; i++) { | ||||
if (i % 10 == 0) | if (i % 10 == 0) | ||||
DPRINTF("\n"); | DPRINTF("\n\r"); | ||||
DPRINTF("%02x ", cfis[i]); | DPRINTF("%02x ", cfis[i]); | ||||
} | } | ||||
DPRINTF("\n"); | DPRINTF("\n\r"); | ||||
for (i = 0; i < hdr->prdtl; i++) { | for (i = 0; i < hdr->prdtl; i++) { | ||||
DPRINTF("%d@%08"PRIx64"\n", prdt->dbc & 0x3fffff, prdt->dba); | DPRINTF("%d@%08"PRIx64"\n\r", prdt->dbc & 0x3fffff, prdt->dba); | ||||
prdt++; | prdt++; | ||||
} | } | ||||
#endif | #endif | ||||
if (cfis[0] != FIS_TYPE_REGH2D) { | if (cfis[0] != FIS_TYPE_REGH2D) { | ||||
WPRINTF("Not a H2D FIS:%02x\n", cfis[0]); | WPRINTF("Not a H2D FIS:%02x\n\r", cfis[0]); | ||||
return; | return; | ||||
} | } | ||||
if (cfis[1] & 0x80) { | if (cfis[1] & 0x80) { | ||||
ahci_handle_cmd(p, slot, cfis); | ahci_handle_cmd(p, slot, cfis); | ||||
} else { | } else { | ||||
if (cfis[15] & (1 << 2)) | if (cfis[15] & (1 << 2)) | ||||
p->reset = 1; | p->reset = 1; | ||||
Show All 39 Lines | ata_ioreq_cb(struct blockif_req *br, int err) | ||||
struct ahci_cmd_hdr *hdr; | struct ahci_cmd_hdr *hdr; | ||||
struct ahci_ioreq *aior; | struct ahci_ioreq *aior; | ||||
struct ahci_port *p; | struct ahci_port *p; | ||||
struct pci_ahci_softc *sc; | struct pci_ahci_softc *sc; | ||||
uint32_t tfd; | uint32_t tfd; | ||||
uint8_t *cfis; | uint8_t *cfis; | ||||
int slot, ncq, dsm; | int slot, ncq, dsm; | ||||
DPRINTF("%s %d\n", __func__, err); | DPRINTF("%s %d\n\r", __func__, err); | ||||
ncq = dsm = 0; | ncq = dsm = 0; | ||||
aior = br->br_param; | aior = br->br_param; | ||||
p = aior->io_pr; | p = aior->io_pr; | ||||
cfis = aior->cfis; | cfis = aior->cfis; | ||||
slot = aior->slot; | slot = aior->slot; | ||||
sc = p->pr_sc; | sc = p->pr_sc; | ||||
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); | hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + slot * AHCI_CL_SIZE); | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | ata_ioreq_cb(struct blockif_req *br, int err) | ||||
* This command is now complete. | * This command is now complete. | ||||
*/ | */ | ||||
p->pending &= ~(1 << slot); | p->pending &= ~(1 << slot); | ||||
ahci_check_stopped(p); | ahci_check_stopped(p); | ||||
ahci_handle_port(p); | ahci_handle_port(p); | ||||
out: | out: | ||||
pthread_mutex_unlock(&sc->mtx); | pthread_mutex_unlock(&sc->mtx); | ||||
DPRINTF("%s exit\n", __func__); | DPRINTF("%s exit\n\r", __func__); | ||||
} | } | ||||
static void | static void | ||||
atapi_ioreq_cb(struct blockif_req *br, int err) | atapi_ioreq_cb(struct blockif_req *br, int err) | ||||
{ | { | ||||
struct ahci_cmd_hdr *hdr; | struct ahci_cmd_hdr *hdr; | ||||
struct ahci_ioreq *aior; | struct ahci_ioreq *aior; | ||||
struct ahci_port *p; | struct ahci_port *p; | ||||
struct pci_ahci_softc *sc; | struct pci_ahci_softc *sc; | ||||
uint8_t *cfis; | uint8_t *cfis; | ||||
uint32_t tfd; | uint32_t tfd; | ||||
int slot; | int slot; | ||||
DPRINTF("%s %d\n", __func__, err); | DPRINTF("%s %d\n\r", __func__, err); | ||||
aior = br->br_param; | aior = br->br_param; | ||||
p = aior->io_pr; | p = aior->io_pr; | ||||
cfis = aior->cfis; | cfis = aior->cfis; | ||||
slot = aior->slot; | slot = aior->slot; | ||||
sc = p->pr_sc; | sc = p->pr_sc; | ||||
hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + aior->slot * AHCI_CL_SIZE); | hdr = (struct ahci_cmd_hdr *)(p->cmd_lst + aior->slot * AHCI_CL_SIZE); | ||||
Show All 31 Lines | atapi_ioreq_cb(struct blockif_req *br, int err) | ||||
* This command is now complete. | * This command is now complete. | ||||
*/ | */ | ||||
p->pending &= ~(1 << slot); | p->pending &= ~(1 << slot); | ||||
ahci_check_stopped(p); | ahci_check_stopped(p); | ||||
ahci_handle_port(p); | ahci_handle_port(p); | ||||
out: | out: | ||||
pthread_mutex_unlock(&sc->mtx); | pthread_mutex_unlock(&sc->mtx); | ||||
DPRINTF("%s exit\n", __func__); | DPRINTF("%s exit\n\r", __func__); | ||||
} | } | ||||
static void | static void | ||||
pci_ahci_ioreq_init(struct ahci_port *pr) | pci_ahci_ioreq_init(struct ahci_port *pr) | ||||
{ | { | ||||
struct ahci_ioreq *vr; | struct ahci_ioreq *vr; | ||||
int i; | int i; | ||||
Show All 20 Lines | |||||
static void | static void | ||||
pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) | pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) | ||||
{ | { | ||||
int port = (offset - AHCI_OFFSET) / AHCI_STEP; | int port = (offset - AHCI_OFFSET) / AHCI_STEP; | ||||
offset = (offset - AHCI_OFFSET) % AHCI_STEP; | offset = (offset - AHCI_OFFSET) % AHCI_STEP; | ||||
struct ahci_port *p = &sc->port[port]; | struct ahci_port *p = &sc->port[port]; | ||||
DPRINTF("pci_ahci_port %d: write offset 0x%"PRIx64" value 0x%"PRIx64"\n", | DPRINTF("pci_ahci_port %d: write offset 0x%"PRIx64" value 0x%"PRIx64"\n\r", | ||||
port, offset, value); | port, offset, value); | ||||
switch (offset) { | switch (offset) { | ||||
case AHCI_P_CLB: | case AHCI_P_CLB: | ||||
p->clb = value; | p->clb = value; | ||||
break; | break; | ||||
case AHCI_P_CLBU: | case AHCI_P_CLBU: | ||||
p->clbu = value; | p->clbu = value; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | case AHCI_P_CMD: | ||||
} | } | ||||
ahci_handle_port(p); | ahci_handle_port(p); | ||||
break; | break; | ||||
} | } | ||||
case AHCI_P_TFD: | case AHCI_P_TFD: | ||||
case AHCI_P_SIG: | case AHCI_P_SIG: | ||||
case AHCI_P_SSTS: | case AHCI_P_SSTS: | ||||
WPRINTF("pci_ahci_port: read only registers 0x%"PRIx64"\n", offset); | WPRINTF("pci_ahci_port: read only registers 0x%"PRIx64"\n\r", offset); | ||||
break; | break; | ||||
case AHCI_P_SCTL: | case AHCI_P_SCTL: | ||||
p->sctl = value; | p->sctl = value; | ||||
if (!(p->cmd & AHCI_P_CMD_ST)) { | if (!(p->cmd & AHCI_P_CMD_ST)) { | ||||
if (value & ATA_SC_DET_RESET) | if (value & ATA_SC_DET_RESET) | ||||
ahci_port_reset(p); | ahci_port_reset(p); | ||||
} | } | ||||
break; | break; | ||||
Show All 12 Lines | pci_ahci_port_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
pci_ahci_host_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) | pci_ahci_host_write(struct pci_ahci_softc *sc, uint64_t offset, uint64_t value) | ||||
{ | { | ||||
DPRINTF("pci_ahci_host: write offset 0x%"PRIx64" value 0x%"PRIx64"\n", | DPRINTF("pci_ahci_host: write offset 0x%"PRIx64" value 0x%"PRIx64"\n\r", | ||||
offset, value); | offset, value); | ||||
switch (offset) { | switch (offset) { | ||||
case AHCI_CAP: | case AHCI_CAP: | ||||
case AHCI_PI: | case AHCI_PI: | ||||
case AHCI_VS: | case AHCI_VS: | ||||
case AHCI_CAP2: | case AHCI_CAP2: | ||||
DPRINTF("pci_ahci_host: read only registers 0x%"PRIx64"\n", offset); | DPRINTF("pci_ahci_host: read only registers 0x%"PRIx64"\n\r", offset); | ||||
break; | break; | ||||
case AHCI_GHC: | case AHCI_GHC: | ||||
if (value & AHCI_GHC_HR) { | if (value & AHCI_GHC_HR) { | ||||
ahci_reset(sc); | ahci_reset(sc); | ||||
break; | break; | ||||
} | } | ||||
if (value & AHCI_GHC_IE) | if (value & AHCI_GHC_IE) | ||||
sc->ghc |= AHCI_GHC_IE; | sc->ghc |= AHCI_GHC_IE; | ||||
Show All 21 Lines | pci_ahci_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
pthread_mutex_lock(&sc->mtx); | pthread_mutex_lock(&sc->mtx); | ||||
if (offset < AHCI_OFFSET) | if (offset < AHCI_OFFSET) | ||||
pci_ahci_host_write(sc, offset, value); | pci_ahci_host_write(sc, offset, value); | ||||
else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP) | else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP) | ||||
pci_ahci_port_write(sc, offset, value); | pci_ahci_port_write(sc, offset, value); | ||||
else | else | ||||
WPRINTF("pci_ahci: unknown i/o write offset 0x%"PRIx64"\n", offset); | WPRINTF("pci_ahci: unknown i/o write offset 0x%"PRIx64"\n\r", offset); | ||||
pthread_mutex_unlock(&sc->mtx); | pthread_mutex_unlock(&sc->mtx); | ||||
} | } | ||||
static uint64_t | static uint64_t | ||||
pci_ahci_host_read(struct pci_ahci_softc *sc, uint64_t offset) | pci_ahci_host_read(struct pci_ahci_softc *sc, uint64_t offset) | ||||
{ | { | ||||
uint32_t value; | uint32_t value; | ||||
Show All 14 Lines | case AHCI_CAP2: | ||||
p += (offset - AHCI_CAP) / sizeof(uint32_t); | p += (offset - AHCI_CAP) / sizeof(uint32_t); | ||||
value = *p; | value = *p; | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
value = 0; | value = 0; | ||||
break; | break; | ||||
} | } | ||||
DPRINTF("pci_ahci_host: read offset 0x%"PRIx64" value 0x%x\n", | DPRINTF("pci_ahci_host: read offset 0x%"PRIx64" value 0x%x\n\r", | ||||
offset, value); | offset, value); | ||||
return (value); | return (value); | ||||
} | } | ||||
static uint64_t | static uint64_t | ||||
pci_ahci_port_read(struct pci_ahci_softc *sc, uint64_t offset) | pci_ahci_port_read(struct pci_ahci_softc *sc, uint64_t offset) | ||||
{ | { | ||||
Show All 24 Lines | case AHCI_P_FBS: | ||||
value = *p; | value = *p; | ||||
break; | break; | ||||
} | } | ||||
default: | default: | ||||
value = 0; | value = 0; | ||||
break; | break; | ||||
} | } | ||||
DPRINTF("pci_ahci_port %d: read offset 0x%"PRIx64" value 0x%x\n", | DPRINTF("pci_ahci_port %d: read offset 0x%"PRIx64" value 0x%x\n\r", | ||||
port, offset, value); | port, offset, value); | ||||
return value; | return value; | ||||
} | } | ||||
static uint64_t | static uint64_t | ||||
pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
uint64_t regoff, int size) | uint64_t regoff, int size) | ||||
Show All 10 Lines | pci_ahci_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, int baridx, | ||||
offset = regoff & ~0x3; /* round down to a multiple of 4 bytes */ | offset = regoff & ~0x3; /* round down to a multiple of 4 bytes */ | ||||
if (offset < AHCI_OFFSET) | if (offset < AHCI_OFFSET) | ||||
value = pci_ahci_host_read(sc, offset); | value = pci_ahci_host_read(sc, offset); | ||||
else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP) | else if (offset < AHCI_OFFSET + sc->ports * AHCI_STEP) | ||||
value = pci_ahci_port_read(sc, offset); | value = pci_ahci_port_read(sc, offset); | ||||
else { | else { | ||||
value = 0; | value = 0; | ||||
WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n", | WPRINTF("pci_ahci: unknown i/o read offset 0x%"PRIx64"\n\r", | ||||
regoff); | regoff); | ||||
} | } | ||||
value >>= 8 * (regoff & 0x3); | value >>= 8 * (regoff & 0x3); | ||||
pthread_mutex_unlock(&sc->mtx); | pthread_mutex_unlock(&sc->mtx); | ||||
return (value); | return (value); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 169 Lines • Show Last 20 Lines |