@@ -425,6 +425,31 @@ void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd)
reg = ahci_px_rreg(ahci, port, AHCI_PX_SERR);
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. */
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
if (!cmd->errors) {
@@ -436,17 +461,24 @@ void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd)
ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (cmd->errors << 8));
}
-void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
- uint32_t intr_mask)
+void ahci_port_check_interrupts(AHCIQState *ahci, AHCICommand *cmd)
{
+ uint8_t port = cmd->port;
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 */
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. */
- 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);
}
@@ -1248,9 +1280,9 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
uint8_t slot = cmd->slot;
uint8_t port = cmd->port;
- ahci_port_check_error(ahci, cmd);
- ahci_port_check_interrupts(ahci, port, cmd->interrupts);
ahci_port_check_nonbusy(ahci, cmd);
+ ahci_port_check_error(ahci, cmd);
+ ahci_port_check_interrupts(ahci, cmd);
ahci_port_check_cmd_sanity(ahci, cmd);
if (cmd->interrupts & AHCI_PX_IS_DHRS) {
ahci_port_check_d2h_sanity(ahci, port, slot);
@@ -591,8 +591,7 @@ void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
/* AHCI sanity check routines */
void ahci_port_check_error(AHCIQState *ahci, AHCICommand *cmd);
-void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
- uint32_t intr_mask);
+void ahci_port_check_interrupts(AHCIQState *ahci, AHCICommand *cmd);
void ahci_port_check_nonbusy(AHCIQState *ahci, AHCICommand *cmd);
void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
void ahci_port_check_pio_sanity(AHCIQState *ahci, AHCICommand *cmd);