qemu-e2k/hw/ide
Niklas Cassel e2a5d9b3d9 hw/ide/ahci: simplify and document PxCI handling
The AHCI spec states that:
For NCQ, PxCI is cleared on command queued successfully.

For non-NCQ, PxCI is cleared on command completed successfully.
(A non-NCQ command that completes with error does not clear PxCI.)

The current QEMU implementation either clears PxCI in check_cmd(),
or in ahci_cmd_done().

check_cmd() will clear PxCI for a command if handle_cmd() returns 0.
handle_cmd() will return -1 if BUSY or DRQ is set.

The QEMU implementation for NCQ commands will currently not set BUSY
or DRQ, so they will always have PxCI cleared by handle_cmd().
ahci_cmd_done() will never even get called for NCQ commands.

Non-NCQ commands are executed by ide_bus_exec_cmd().
Non-NCQ commands in QEMU are implemented either in a sync or in an async
way.

For non-NCQ commands implemented in a sync way, the command handler will
return true, and when ide_bus_exec_cmd() sees that a command handler
returns true, it will call ide_cmd_done() (which will call
ahci_cmd_done()). For a command implemented in a sync way,
ahci_cmd_done() will do nothing (since busy_slot is not set). Instead,
after ide_bus_exec_cmd() has finished, check_cmd() will clear PxCI for
these commands.

For non-NCQ commands implemented in an async way (using either aiocb or
pio_aiocb), the command handler will return false, ide_bus_exec_cmd()
will not call ide_cmd_done(), instead it is expected that the async
callback function will call ide_cmd_done() once the async command is
done. handle_cmd() will set busy_slot, if and only if BUSY or DRQ is
set, and this is checked _after_ ide_bus_exec_cmd() has returned.
handle_cmd() will return -1, so check_cmd() will not clear PxCI.
When the async callback calls ide_cmd_done() (which will call
ahci_cmd_done()), it will see that busy_slot is set, and
ahci_cmd_done() will clear PxCI.

This seems racy, since busy_slot is set _after_ ide_bus_exec_cmd() has
returned. The callback might come before busy_slot gets set. And it is
quite confusing that ahci_cmd_done() will be called for all non-NCQ
commands when the command is done, but will only clear PxCI in certain
cases, even though it will always write a D2H FIS and raise an IRQ.

Even worse, in the case where ahci_cmd_done() does not clear PxCI, it
still raises an IRQ. Host software might thus read an old PxCI value,
since PxCI is cleared (by check_cmd()) after the IRQ has been raised.

Try to simplify this by always setting busy_slot for non-NCQ commands,
such that ahci_cmd_done() will always be responsible for clearing PxCI
for non-NCQ commands.

For NCQ commands, clear PxCI when we receive the D2H FIS, but before
raising the IRQ, see AHCI 1.3.1, section 5.3.8, states RegFIS:Entry and
RegFIS:ClearCI.

Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
Message-id: 20230609140844.202795-5-nks@flawful.org
Signed-off-by: John Snow <jsnow@redhat.com>
2023-09-06 22:48:04 -04:00
..
ahci_internal.h hw/ide: spelling fixes 2023-08-31 19:47:43 +02:00
ahci-allwinner.c
ahci.c hw/ide/ahci: simplify and document PxCI handling 2023-09-06 22:48:04 -04:00
atapi.c
cmd646.c hw/ide: spelling fixes 2023-08-31 19:47:43 +02:00
core.c hw/ide/core: set ERR_STAT in unsupported command completion 2023-09-06 22:48:04 -04:00
ich.c
ioport.c hw/ide/ioport: Remove unnecessary includes 2023-02-27 22:29:02 +01:00
isa.c hw/ide: Rename ide_init2() -> ide_bus_init_output_irq() 2023-02-27 22:29:02 +01:00
Kconfig
macio.c hw/ide: Rename idebus_active_if() -> ide_bus_active_if() 2023-02-27 22:29:02 +01:00
meson.build meson: Replace softmmu_ss -> system_ss 2023-06-20 10:01:30 +02:00
microdrive.c hw/ide: replace TABs with space 2023-03-24 11:45:33 +01:00
mmio.c hw/ide: Rename ide_init2() -> ide_bus_init_output_irq() 2023-02-27 22:29:02 +01:00
pci.c hw/ide/pci: Replace some magic numbers by constants 2023-07-11 00:11:25 +02:00
piix.c hw/ide/piix: properly initialize the BMIBA register 2023-07-14 11:10:57 +02:00
qdev.c
sii3112.c hw/ide: Extract bmdma_status_writeb() 2023-07-11 00:11:25 +02:00
trace-events hw/ide: Rename ide_exec_cmd() -> ide_bus_exec_cmd() 2023-02-27 22:29:02 +01:00
trace.h
via.c hw/ide: Extract bmdma_status_writeb() 2023-07-11 00:11:25 +02:00