mirror of https://github.com/xemu-project/xemu.git
IDE Pull request
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAmT5RpYACgkQfe+BBqr8 OQ7GuA//S/gyyqsnltz4W9D0liaan1a2YsSx7Q2gcKdotdmFwgEHWWuVKorCteQt 1AtkFiA1bawF9ZSRQIpQzMNDOkSJHOs/0HXhdbNRs6JZ6C+c/aLnNSpxIfFpkP3I Wcrmi98F8zHlRc+KGqvZFHW+woqWJxTvglG4OmpMhMWCZRuqADeaxWaywgSXxlK+ MtmpsslPeTxHdwa6ijXCJd2ghP59z391Ulo4kZ7YOMou/YLEd/AnezBDtepDGnbb TnyDcvGf+Dp5nJ4Rcp22frZdcxb44+wt2QlQFDp+h6r7KzIEwGIK2LL37sN8VHwU B8GbYkjoPnau2cOaLgmpC1reWkdwaiXfaI+1B/35/jg6hwYHFe6F03+JstMWXHXt ++Wy4MKDx5wRt7cmOu6htS776UC15NMcZB0AzxQuE5mL+eSNp1n5Nw5UW2iD/USL LD2dlMO05acdqn2iXoMTX/K1cUo1wRkEns7PISk+F2ve0PTS1RJUvuiNXs+aDrt9 +AfE/e025YMQY8CWLiaihfNH7/QY8vS874SrcDr5rtfhitu16nqq5JpjnyzkqgbR PE+5JWT3QGBOcDMQeNUDfxFlcCVDm3ffIKo/7/PDCfeKQsJkG/nVGF7OmlAVmoUD GsvIlKBegIQvpp8LRabzfeTfbj7NGKFwaShQ6wcqxOakjy+iKx8= =ZRVt -----END PGP SIGNATURE----- Merge tag 'ide-pull-request' of https://gitlab.com/jsnow/qemu into staging IDE Pull request # -----BEGIN PGP SIGNATURE----- # # iQIzBAABCAAdFiEE+ber27ys35W+dsvQfe+BBqr8OQ4FAmT5RpYACgkQfe+BBqr8 # OQ7GuA//S/gyyqsnltz4W9D0liaan1a2YsSx7Q2gcKdotdmFwgEHWWuVKorCteQt # 1AtkFiA1bawF9ZSRQIpQzMNDOkSJHOs/0HXhdbNRs6JZ6C+c/aLnNSpxIfFpkP3I # Wcrmi98F8zHlRc+KGqvZFHW+woqWJxTvglG4OmpMhMWCZRuqADeaxWaywgSXxlK+ # MtmpsslPeTxHdwa6ijXCJd2ghP59z391Ulo4kZ7YOMou/YLEd/AnezBDtepDGnbb # TnyDcvGf+Dp5nJ4Rcp22frZdcxb44+wt2QlQFDp+h6r7KzIEwGIK2LL37sN8VHwU # B8GbYkjoPnau2cOaLgmpC1reWkdwaiXfaI+1B/35/jg6hwYHFe6F03+JstMWXHXt # ++Wy4MKDx5wRt7cmOu6htS776UC15NMcZB0AzxQuE5mL+eSNp1n5Nw5UW2iD/USL # LD2dlMO05acdqn2iXoMTX/K1cUo1wRkEns7PISk+F2ve0PTS1RJUvuiNXs+aDrt9 # +AfE/e025YMQY8CWLiaihfNH7/QY8vS874SrcDr5rtfhitu16nqq5JpjnyzkqgbR # PE+5JWT3QGBOcDMQeNUDfxFlcCVDm3ffIKo/7/PDCfeKQsJkG/nVGF7OmlAVmoUD # GsvIlKBegIQvpp8LRabzfeTfbj7NGKFwaShQ6wcqxOakjy+iKx8= # =ZRVt # -----END PGP SIGNATURE----- # gpg: Signature made Wed 06 Sep 2023 23:42:14 EDT # gpg: using RSA key F9B7ABDBBCACDF95BE76CBD07DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" [unknown] # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: FAEB 9711 A12C F475 812F 18F2 88A9 064D 1835 61EB # Subkey fingerprint: F9B7 ABDB BCAC DF95 BE76 CBD0 7DEF 8106 AAFC 390E * tag 'ide-pull-request' of https://gitlab.com/jsnow/qemu: hw/ide/ahci: fix broken SError handling hw/ide/ahci: fix ahci_write_fis_sdb() hw/ide/ahci: PxCI should not get cleared when ERR_STAT is set hw/ide/ahci: PxSACT and PxCI is cleared when PxCMD.ST is cleared hw/ide/ahci: simplify and document PxCI handling hw/ide/ahci: write D2H FIS when processing NCQ command hw/ide/core: set ERR_STAT in unsupported command completion Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
13d9f6dca0
108
hw/ide/ahci.c
108
hw/ide/ahci.c
|
@ -41,9 +41,10 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
static void check_cmd(AHCIState *s, int port);
|
static void check_cmd(AHCIState *s, int port);
|
||||||
static int handle_cmd(AHCIState *s, int port, uint8_t slot);
|
static void handle_cmd(AHCIState *s, int port, uint8_t slot);
|
||||||
static void ahci_reset_port(AHCIState *s, int port);
|
static void ahci_reset_port(AHCIState *s, int port);
|
||||||
static bool ahci_write_fis_d2h(AHCIDevice *ad);
|
static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i);
|
||||||
|
static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot);
|
||||||
static void ahci_init_d2h(AHCIDevice *ad);
|
static void ahci_init_d2h(AHCIDevice *ad);
|
||||||
static int ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit);
|
static int ahci_dma_prepare_buf(const IDEDMA *dma, int32_t limit);
|
||||||
static bool ahci_map_clb_address(AHCIDevice *ad);
|
static bool ahci_map_clb_address(AHCIDevice *ad);
|
||||||
|
@ -328,6 +329,11 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val)
|
||||||
ahci_check_irq(s);
|
ahci_check_irq(s);
|
||||||
break;
|
break;
|
||||||
case AHCI_PORT_REG_CMD:
|
case AHCI_PORT_REG_CMD:
|
||||||
|
if ((pr->cmd & PORT_CMD_START) && !(val & PORT_CMD_START)) {
|
||||||
|
pr->scr_act = 0;
|
||||||
|
pr->cmd_issue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Block any Read-only fields from being set;
|
/* Block any Read-only fields from being set;
|
||||||
* including LIST_ON and FIS_ON.
|
* including LIST_ON and FIS_ON.
|
||||||
* The spec requires to set ICC bits to zero after the ICC change
|
* The spec requires to set ICC bits to zero after the ICC change
|
||||||
|
@ -591,9 +597,8 @@ static void check_cmd(AHCIState *s, int port)
|
||||||
|
|
||||||
if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
|
if ((pr->cmd & PORT_CMD_START) && pr->cmd_issue) {
|
||||||
for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
|
for (slot = 0; (slot < 32) && pr->cmd_issue; slot++) {
|
||||||
if ((pr->cmd_issue & (1U << slot)) &&
|
if (pr->cmd_issue & (1U << slot)) {
|
||||||
!handle_cmd(s, port, slot)) {
|
handle_cmd(s, port, slot);
|
||||||
pr->cmd_issue &= ~(1U << slot);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -618,7 +623,7 @@ static void ahci_init_d2h(AHCIDevice *ad)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ahci_write_fis_d2h(ad)) {
|
if (ahci_write_fis_d2h(ad, true)) {
|
||||||
ad->init_d2h_sent = true;
|
ad->init_d2h_sent = true;
|
||||||
/* We're emulating receiving the first Reg H2D Fis from the device;
|
/* We're emulating receiving the first Reg H2D Fis from the device;
|
||||||
* Update the SIG register, but otherwise proceed as normal. */
|
* Update the SIG register, but otherwise proceed as normal. */
|
||||||
|
@ -801,8 +806,14 @@ static void ahci_write_fis_sdb(AHCIState *s, NCQTransferState *ncq_tfs)
|
||||||
pr->scr_act &= ~ad->finished;
|
pr->scr_act &= ~ad->finished;
|
||||||
ad->finished = 0;
|
ad->finished = 0;
|
||||||
|
|
||||||
/* Trigger IRQ if interrupt bit is set (which currently, it always is) */
|
/*
|
||||||
if (sdb_fis->flags & 0x40) {
|
* TFES IRQ is always raised if ERR_STAT is set, regardless of I bit.
|
||||||
|
* If ERR_STAT is not set, trigger SDBS IRQ if interrupt bit is set
|
||||||
|
* (which currently, it always is).
|
||||||
|
*/
|
||||||
|
if (sdb_fis->status & ERR_STAT) {
|
||||||
|
ahci_trigger_irq(s, ad, AHCI_PORT_IRQ_BIT_TFES);
|
||||||
|
} else if (sdb_fis->flags & 0x40) {
|
||||||
ahci_trigger_irq(s, ad, AHCI_PORT_IRQ_BIT_SDBS);
|
ahci_trigger_irq(s, ad, AHCI_PORT_IRQ_BIT_SDBS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -850,7 +861,7 @@ static void ahci_write_fis_pio(AHCIDevice *ad, uint16_t len, bool pio_fis_i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool ahci_write_fis_d2h(AHCIDevice *ad)
|
static bool ahci_write_fis_d2h(AHCIDevice *ad, bool d2h_fis_i)
|
||||||
{
|
{
|
||||||
AHCIPortRegs *pr = &ad->port_regs;
|
AHCIPortRegs *pr = &ad->port_regs;
|
||||||
uint8_t *d2h_fis;
|
uint8_t *d2h_fis;
|
||||||
|
@ -864,7 +875,7 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad)
|
||||||
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
|
d2h_fis = &ad->res_fis[RES_FIS_RFIS];
|
||||||
|
|
||||||
d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
|
d2h_fis[0] = SATA_FIS_TYPE_REGISTER_D2H;
|
||||||
d2h_fis[1] = (1 << 6); /* interrupt bit */
|
d2h_fis[1] = d2h_fis_i ? (1 << 6) : 0; /* interrupt bit */
|
||||||
d2h_fis[2] = s->status;
|
d2h_fis[2] = s->status;
|
||||||
d2h_fis[3] = s->error;
|
d2h_fis[3] = s->error;
|
||||||
|
|
||||||
|
@ -890,7 +901,10 @@ static bool ahci_write_fis_d2h(AHCIDevice *ad)
|
||||||
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_TFES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (d2h_fis_i) {
|
||||||
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS);
|
ahci_trigger_irq(ad->hba, ad, AHCI_PORT_IRQ_BIT_DHRS);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -998,7 +1012,6 @@ static void ncq_err(NCQTransferState *ncq_tfs)
|
||||||
|
|
||||||
ide_state->error = ABRT_ERR;
|
ide_state->error = ABRT_ERR;
|
||||||
ide_state->status = READY_STAT | ERR_STAT;
|
ide_state->status = READY_STAT | ERR_STAT;
|
||||||
ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
|
|
||||||
qemu_sglist_destroy(&ncq_tfs->sglist);
|
qemu_sglist_destroy(&ncq_tfs->sglist);
|
||||||
ncq_tfs->used = 0;
|
ncq_tfs->used = 0;
|
||||||
}
|
}
|
||||||
|
@ -1008,7 +1021,7 @@ static void ncq_finish(NCQTransferState *ncq_tfs)
|
||||||
/* If we didn't error out, set our finished bit. Errored commands
|
/* If we didn't error out, set our finished bit. Errored commands
|
||||||
* do not get a bit set for the SDB FIS ACT register, nor do they
|
* do not get a bit set for the SDB FIS ACT register, nor do they
|
||||||
* clear the outstanding bit in scr_act (PxSACT). */
|
* clear the outstanding bit in scr_act (PxSACT). */
|
||||||
if (!(ncq_tfs->drive->port_regs.scr_err & (1 << ncq_tfs->tag))) {
|
if (ncq_tfs->used) {
|
||||||
ncq_tfs->drive->finished |= (1 << ncq_tfs->tag);
|
ncq_tfs->drive->finished |= (1 << ncq_tfs->tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,6 +1133,24 @@ static void process_ncq_command(AHCIState *s, int port, const uint8_t *cmd_fis,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A NCQ command clears the bit in PxCI after the command has been QUEUED
|
||||||
|
* successfully (ERROR not set, BUSY and DRQ cleared).
|
||||||
|
*
|
||||||
|
* For NCQ commands, PxCI will always be cleared here.
|
||||||
|
*
|
||||||
|
* (Once the NCQ command is COMPLETED, the device will send a SDB FIS with
|
||||||
|
* the interrupt bit set, which will clear PxSACT and raise an interrupt.)
|
||||||
|
*/
|
||||||
|
ahci_clear_cmd_issue(ad, slot);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In reality, for NCQ commands, PxCI is cleared after receiving a D2H FIS
|
||||||
|
* without the interrupt bit set, but since ahci_write_fis_d2h() can raise
|
||||||
|
* an IRQ on error, we need to call them in reverse order.
|
||||||
|
*/
|
||||||
|
ahci_write_fis_d2h(ad, false);
|
||||||
|
|
||||||
ncq_tfs->used = 1;
|
ncq_tfs->used = 1;
|
||||||
ncq_tfs->drive = ad;
|
ncq_tfs->drive = ad;
|
||||||
ncq_tfs->slot = slot;
|
ncq_tfs->slot = slot;
|
||||||
|
@ -1192,6 +1223,7 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
|
||||||
{
|
{
|
||||||
IDEState *ide_state = &s->dev[port].port.ifs[0];
|
IDEState *ide_state = &s->dev[port].port.ifs[0];
|
||||||
AHCICmdHdr *cmd = get_cmd_header(s, port, slot);
|
AHCICmdHdr *cmd = get_cmd_header(s, port, slot);
|
||||||
|
AHCIDevice *ad = &s->dev[port];
|
||||||
uint16_t opts = le16_to_cpu(cmd->opts);
|
uint16_t opts = le16_to_cpu(cmd->opts);
|
||||||
|
|
||||||
if (cmd_fis[1] & 0x0F) {
|
if (cmd_fis[1] & 0x0F) {
|
||||||
|
@ -1268,11 +1300,19 @@ static void handle_reg_h2d_fis(AHCIState *s, int port,
|
||||||
/* Reset transferred byte counter */
|
/* Reset transferred byte counter */
|
||||||
cmd->status = 0;
|
cmd->status = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A non-NCQ command clears the bit in PxCI after the command has COMPLETED
|
||||||
|
* successfully (ERROR not set, BUSY and DRQ cleared).
|
||||||
|
*
|
||||||
|
* For non-NCQ commands, PxCI will always be cleared by ahci_cmd_done().
|
||||||
|
*/
|
||||||
|
ad->busy_slot = slot;
|
||||||
|
|
||||||
/* We're ready to process the command in FIS byte 2. */
|
/* We're ready to process the command in FIS byte 2. */
|
||||||
ide_bus_exec_cmd(&s->dev[port].port, cmd_fis[2]);
|
ide_bus_exec_cmd(&s->dev[port].port, cmd_fis[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_cmd(AHCIState *s, int port, uint8_t slot)
|
static void handle_cmd(AHCIState *s, int port, uint8_t slot)
|
||||||
{
|
{
|
||||||
IDEState *ide_state;
|
IDEState *ide_state;
|
||||||
uint64_t tbl_addr;
|
uint64_t tbl_addr;
|
||||||
|
@ -1283,12 +1323,12 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
|
||||||
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
|
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
|
||||||
/* Engine currently busy, try again later */
|
/* Engine currently busy, try again later */
|
||||||
trace_handle_cmd_busy(s, port);
|
trace_handle_cmd_busy(s, port);
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!s->dev[port].lst) {
|
if (!s->dev[port].lst) {
|
||||||
trace_handle_cmd_nolist(s, port);
|
trace_handle_cmd_nolist(s, port);
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
cmd = get_cmd_header(s, port, slot);
|
cmd = get_cmd_header(s, port, slot);
|
||||||
/* remember current slot handle for later */
|
/* remember current slot handle for later */
|
||||||
|
@ -1298,7 +1338,7 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
|
||||||
ide_state = &s->dev[port].port.ifs[0];
|
ide_state = &s->dev[port].port.ifs[0];
|
||||||
if (!ide_state->blk) {
|
if (!ide_state->blk) {
|
||||||
trace_handle_cmd_badport(s, port);
|
trace_handle_cmd_badport(s, port);
|
||||||
return -1;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tbl_addr = le64_to_cpu(cmd->tbl_addr);
|
tbl_addr = le64_to_cpu(cmd->tbl_addr);
|
||||||
|
@ -1307,7 +1347,7 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
|
||||||
DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED);
|
DMA_DIRECTION_TO_DEVICE, MEMTXATTRS_UNSPECIFIED);
|
||||||
if (!cmd_fis) {
|
if (!cmd_fis) {
|
||||||
trace_handle_cmd_badfis(s, port);
|
trace_handle_cmd_badfis(s, port);
|
||||||
return -1;
|
return;
|
||||||
} else if (cmd_len != 0x80) {
|
} else if (cmd_len != 0x80) {
|
||||||
ahci_trigger_irq(s, &s->dev[port], AHCI_PORT_IRQ_BIT_HBFS);
|
ahci_trigger_irq(s, &s->dev[port], AHCI_PORT_IRQ_BIT_HBFS);
|
||||||
trace_handle_cmd_badmap(s, port, cmd_len);
|
trace_handle_cmd_badmap(s, port, cmd_len);
|
||||||
|
@ -1331,15 +1371,6 @@ static int handle_cmd(AHCIState *s, int port, uint8_t slot)
|
||||||
out:
|
out:
|
||||||
dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_TO_DEVICE,
|
dma_memory_unmap(s->as, cmd_fis, cmd_len, DMA_DIRECTION_TO_DEVICE,
|
||||||
cmd_len);
|
cmd_len);
|
||||||
|
|
||||||
if (s->dev[port].port.ifs[0].status & (BUSY_STAT|DRQ_STAT)) {
|
|
||||||
/* async command, complete later */
|
|
||||||
s->dev[port].busy_slot = slot;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* done handling the command */
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transfer PIO data between RAM and device */
|
/* Transfer PIO data between RAM and device */
|
||||||
|
@ -1493,22 +1524,39 @@ static int ahci_dma_rw_buf(const IDEDMA *dma, bool is_write)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ahci_clear_cmd_issue(AHCIDevice *ad, uint8_t slot)
|
||||||
|
{
|
||||||
|
IDEState *ide_state = &ad->port.ifs[0];
|
||||||
|
|
||||||
|
if (!(ide_state->status & ERR_STAT) &&
|
||||||
|
!(ide_state->status & (BUSY_STAT | DRQ_STAT))) {
|
||||||
|
ad->port_regs.cmd_issue &= ~(1 << slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Non-NCQ command is done - This function is never called for NCQ commands. */
|
||||||
static void ahci_cmd_done(const IDEDMA *dma)
|
static void ahci_cmd_done(const IDEDMA *dma)
|
||||||
{
|
{
|
||||||
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
AHCIDevice *ad = DO_UPCAST(AHCIDevice, dma, dma);
|
||||||
|
IDEState *ide_state = &ad->port.ifs[0];
|
||||||
|
|
||||||
trace_ahci_cmd_done(ad->hba, ad->port_no);
|
trace_ahci_cmd_done(ad->hba, ad->port_no);
|
||||||
|
|
||||||
/* no longer busy */
|
/* no longer busy */
|
||||||
if (ad->busy_slot != -1) {
|
if (ad->busy_slot != -1) {
|
||||||
ad->port_regs.cmd_issue &= ~(1 << ad->busy_slot);
|
ahci_clear_cmd_issue(ad, ad->busy_slot);
|
||||||
ad->busy_slot = -1;
|
ad->busy_slot = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update d2h status */
|
/*
|
||||||
ahci_write_fis_d2h(ad);
|
* In reality, for non-NCQ commands, PxCI is cleared after receiving a D2H
|
||||||
|
* FIS with the interrupt bit set, but since ahci_write_fis_d2h() will raise
|
||||||
|
* an IRQ, we need to call them in reverse order.
|
||||||
|
*/
|
||||||
|
ahci_write_fis_d2h(ad, true);
|
||||||
|
|
||||||
if (ad->port_regs.cmd_issue && !ad->check_bh) {
|
if (!(ide_state->status & ERR_STAT) &&
|
||||||
|
ad->port_regs.cmd_issue && !ad->check_bh) {
|
||||||
ad->check_bh = qemu_bh_new_guarded(ahci_check_cmd_bh, ad,
|
ad->check_bh = qemu_bh_new_guarded(ahci_check_cmd_bh, ad,
|
||||||
&ad->mem_reentrancy_guard);
|
&ad->mem_reentrancy_guard);
|
||||||
qemu_bh_schedule(ad->check_bh);
|
qemu_bh_schedule(ad->check_bh);
|
||||||
|
|
|
@ -533,9 +533,9 @@ BlockAIOCB *ide_issue_trim(
|
||||||
|
|
||||||
void ide_abort_command(IDEState *s)
|
void ide_abort_command(IDEState *s)
|
||||||
{
|
{
|
||||||
ide_transfer_stop(s);
|
|
||||||
s->status = READY_STAT | ERR_STAT;
|
s->status = READY_STAT | ERR_STAT;
|
||||||
s->error = ABRT_ERR;
|
s->error = ABRT_ERR;
|
||||||
|
ide_transfer_stop(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ide_set_retry(IDEState *s)
|
static void ide_set_retry(IDEState *s)
|
||||||
|
|
|
@ -404,57 +404,110 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port)
|
||||||
/**
|
/**
|
||||||
* Check a port for errors.
|
* Check a port for errors.
|
||||||
*/
|
*/
|
||||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
|
void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd)
|
||||||
uint32_t imask, uint8_t emask)
|
|
||||||
{
|
{
|
||||||
|
uint8_t port = cmd->port;
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
|
||||||
|
/* If expecting TF error, ensure that TFES is set. */
|
||||||
|
if (cmd->errors) {
|
||||||
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||||
|
ASSERT_BIT_SET(reg, AHCI_PX_IS_TFES);
|
||||||
|
} else {
|
||||||
/* The upper 9 bits of the IS register all indicate errors. */
|
/* The upper 9 bits of the IS register all indicate errors. */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||||
reg &= ~imask;
|
reg &= ~cmd->interrupts;
|
||||||
reg >>= 23;
|
reg >>= 23;
|
||||||
g_assert_cmphex(reg, ==, 0);
|
g_assert_cmphex(reg, ==, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* The Sata Error Register should be empty. */
|
/* The Sata Error Register should be empty, even when expecting TF error. */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR);
|
||||||
g_assert_cmphex(reg, ==, 0);
|
g_assert_cmphex(reg, ==, 0);
|
||||||
|
|
||||||
|
/* If expecting TF error, and TFES was set, perform error recovery
|
||||||
|
* (see AHCI 1.3 section 6.2.2.1) such that we can send new commands. */
|
||||||
|
if (cmd->errors) {
|
||||||
|
/* This will clear PxCI. */
|
||||||
|
ahci_px_clr(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST);
|
||||||
|
|
||||||
|
/* The port has 500ms to disengage. */
|
||||||
|
usleep(500000);
|
||||||
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_CMD);
|
||||||
|
ASSERT_BIT_CLEAR(reg, AHCI_PX_CMD_CR);
|
||||||
|
|
||||||
|
/* Clear PxIS. */
|
||||||
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||||
|
ahci_px_wreg(ahci, port, AHCI_PX_IS, reg);
|
||||||
|
|
||||||
|
/* Check if we need to perform a COMRESET.
|
||||||
|
* Not implemented right now, as there is no reason why our QEMU model
|
||||||
|
* should need a COMRESET when expecting TF error. */
|
||||||
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||||
|
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_BSY | AHCI_PX_TFD_STS_DRQ);
|
||||||
|
|
||||||
|
/* Enable issuing new commands. */
|
||||||
|
ahci_px_set(ahci, port, AHCI_PX_CMD, AHCI_PX_CMD_ST);
|
||||||
|
}
|
||||||
|
|
||||||
/* The TFD also has two error sections. */
|
/* The TFD also has two error sections. */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||||
if (!emask) {
|
if (!cmd->errors) {
|
||||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
|
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
|
ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
|
||||||
}
|
}
|
||||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8));
|
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~cmd->errors << 8));
|
||||||
ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8));
|
ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (cmd->errors << 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
void ahci_port_check_interrupts(AHCIQState *ahci, AHCICommand *cmd)
|
||||||
uint32_t intr_mask)
|
|
||||||
{
|
{
|
||||||
|
uint8_t port = cmd->port;
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
|
||||||
|
/* If we expect errors, error handling in ahci_port_check_error() will
|
||||||
|
* already have cleared PxIS, so in that case this function cannot verify
|
||||||
|
* and clear expected interrupts. */
|
||||||
|
if (cmd->errors) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for expected interrupts */
|
/* Check for expected interrupts */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||||
ASSERT_BIT_SET(reg, intr_mask);
|
ASSERT_BIT_SET(reg, cmd->interrupts);
|
||||||
|
|
||||||
/* Clear expected interrupts and assert all interrupts now cleared. */
|
/* Clear expected interrupts and assert all interrupts now cleared. */
|
||||||
ahci_px_wreg(ahci, port, AHCI_PX_IS, intr_mask);
|
ahci_px_wreg(ahci, port, AHCI_PX_IS, cmd->interrupts);
|
||||||
g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
|
g_assert_cmphex(ahci_px_rreg(ahci, port, AHCI_PX_IS), ==, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot)
|
void ahci_port_check_nonbusy(AHCIQState *ahci, AHCICommand *cmd)
|
||||||
{
|
{
|
||||||
|
uint8_t slot = cmd->slot;
|
||||||
|
uint8_t port = cmd->port;
|
||||||
uint32_t reg;
|
uint32_t reg;
|
||||||
|
|
||||||
/* Assert that the command slot is no longer busy (NCQ) */
|
/* For NCQ command with error PxSACT bit should still be set.
|
||||||
|
* For NCQ command without error, PxSACT bit should be cleared.
|
||||||
|
* For non-NCQ command, PxSACT bit should always be cleared. */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_SACT);
|
||||||
|
if (cmd->props->ncq && cmd->errors) {
|
||||||
|
ASSERT_BIT_SET(reg, (1 << slot));
|
||||||
|
} else {
|
||||||
ASSERT_BIT_CLEAR(reg, (1 << slot));
|
ASSERT_BIT_CLEAR(reg, (1 << slot));
|
||||||
|
}
|
||||||
|
|
||||||
/* Non-NCQ */
|
/* For non-NCQ command with error, PxCI bit should still be set.
|
||||||
|
* For non-NCQ command without error, PxCI bit should be cleared.
|
||||||
|
* For NCQ command without error, PxCI bit should be cleared.
|
||||||
|
* For NCQ command with error, PxCI bit may or may not be cleared. */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_CI);
|
||||||
|
if (!cmd->props->ncq && cmd->errors) {
|
||||||
|
ASSERT_BIT_SET(reg, (1 << slot));
|
||||||
|
} else if (!cmd->errors) {
|
||||||
ASSERT_BIT_CLEAR(reg, (1 << slot));
|
ASSERT_BIT_CLEAR(reg, (1 << slot));
|
||||||
|
}
|
||||||
|
|
||||||
/* And assert that we are generally not busy. */
|
/* And assert that we are generally not busy. */
|
||||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||||
|
@ -1207,9 +1260,10 @@ void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd)
|
||||||
|
|
||||||
#define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK)))
|
#define RSET(REG, MASK) (BITSET(ahci_px_rreg(ahci, cmd->port, (REG)), (MASK)))
|
||||||
|
|
||||||
while (RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) ||
|
while (!RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_ERR) &&
|
||||||
|
(RSET(AHCI_PX_TFD, AHCI_PX_TFD_STS_BSY) ||
|
||||||
RSET(AHCI_PX_CI, 1 << cmd->slot) ||
|
RSET(AHCI_PX_CI, 1 << cmd->slot) ||
|
||||||
(cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot))) {
|
(cmd->props->ncq && RSET(AHCI_PX_SACT, 1 << cmd->slot)))) {
|
||||||
usleep(50);
|
usleep(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1226,9 +1280,9 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
|
||||||
uint8_t slot = cmd->slot;
|
uint8_t slot = cmd->slot;
|
||||||
uint8_t port = cmd->port;
|
uint8_t port = cmd->port;
|
||||||
|
|
||||||
ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors);
|
ahci_port_check_nonbusy(ahci, cmd);
|
||||||
ahci_port_check_interrupts(ahci, port, cmd->interrupts);
|
ahci_port_check_error(ahci, cmd);
|
||||||
ahci_port_check_nonbusy(ahci, port, slot);
|
ahci_port_check_interrupts(ahci, cmd);
|
||||||
ahci_port_check_cmd_sanity(ahci, cmd);
|
ahci_port_check_cmd_sanity(ahci, cmd);
|
||||||
if (cmd->interrupts & AHCI_PX_IS_DHRS) {
|
if (cmd->interrupts & AHCI_PX_IS_DHRS) {
|
||||||
ahci_port_check_d2h_sanity(ahci, port, slot);
|
ahci_port_check_d2h_sanity(ahci, port, slot);
|
||||||
|
|
|
@ -590,11 +590,9 @@ void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
||||||
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||||
|
|
||||||
/* AHCI sanity check routines */
|
/* AHCI sanity check routines */
|
||||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
|
void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
uint32_t imask, uint8_t emask);
|
void ahci_port_check_interrupts(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
void ahci_port_check_nonbusy(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
uint32_t intr_mask);
|
|
||||||
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
|
||||||
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||||
void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
|
|
Loading…
Reference in New Issue