libqos/ahci: add ahci_io

ahci_io is a wrapper around ahci_guest_io that takes a pointer to host
memory instead, and will create a guest memory buffer and copy the data
to/from as needed and as appropriate for a read/write command, such that
after a read, the guest data will be in a host buffer, and for a write,
the data will be transmitted to guest memory prior to the block operation.

Now that we have all the syntactic sugar functions in place for AHCI,
we can convert the identify test to be very, very short.

Signed-off-by: John Snow <jsnow@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-id: 1423158090-25580-17-git-send-email-jsnow@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
John Snow 2015-02-05 12:41:27 -05:00 committed by Stefan Hajnoczi
parent 113221956c
commit ae02962017
3 changed files with 52 additions and 38 deletions

View File

@ -657,56 +657,43 @@ static void ahci_test_port_spec(AHCIQState *ahci, uint8_t port)
*/ */
static void ahci_test_identify(AHCIQState *ahci) static void ahci_test_identify(AHCIQState *ahci)
{ {
uint32_t data_ptr;
uint16_t buff[256]; uint16_t buff[256];
unsigned i; unsigned px;
int rc; int rc;
AHCICommand *cmd; const size_t buffsize = 512;
g_assert(ahci != NULL); g_assert(ahci != NULL);
/* We need to: /**
* (1) Create a data buffer for the IDENTIFY response to be sent to, * This serves as a bit of a tutorial on AHCI device programming:
* (2) Create a Command Table Buffer *
* (1) Create a data buffer for the IDENTIFY response to be sent to
* (2) Create a Command Table buffer, where we will store the
* command and PRDT (Physical Region Descriptor Table)
* (3) Construct an FIS host-to-device command structure, and write it to * (3) Construct an FIS host-to-device command structure, and write it to
* the top of the command table buffer. * the top of the Command Table buffer.
* (4) Create a Physical Region Descriptor that points to the data buffer, * (4) Create one or more Physical Region Descriptors (PRDs) that describe
* and write it to the bottom (offset 0x80) of the command table. * a location in memory where data may be stored/retrieved.
* (5) Obtain a Command List slot, and update this header to point to * (5) Write these PRDTs to the bottom (offset 0x80) of the Command Table.
* the Command Table we built above. * (6) Each AHCI port has up to 32 command slots. Each slot contains a
* (6) Now, PxCLB points to the command list, command 0 points to * header that points to a Command Table buffer. Pick an unused slot
* our table, and our table contains an FIS instruction and a * and update it to point to the Command Table we have built.
* PRD that points to our rx buffer. * (7) Now: Command #n points to our Command Table, and our Command Table
* (7) We inform the HBA via PxCI that there is a command ready in slot #0. * contains the FIS (that describes our command) and the PRDTL, which
* describes our buffer.
* (8) We inform the HBA via PxCI (Command Issue) that the command in slot
* #n is ready for processing.
*/ */
/* Pick the first implemented and running port */ /* Pick the first implemented and running port */
i = ahci_port_select(ahci); px = ahci_port_select(ahci);
g_test_message("Selected port %u for test", i); g_test_message("Selected port %u for test", px);
/* Clear out the FIS Receive area and any pending interrupts. */ /* Clear out the FIS Receive area and any pending interrupts. */
ahci_port_clear(ahci, i); ahci_port_clear(ahci, px);
/* Create a data buffer where we will dump the IDENTIFY data to. */ /* "Read" 512 bytes using CMD_IDENTIFY into the host buffer. */
data_ptr = ahci_alloc(ahci, 512); ahci_io(ahci, px, CMD_IDENTIFY, &buff, buffsize);
g_assert(data_ptr);
/* Construct the Command Table (FIS and PRDT) and Command Header */
cmd = ahci_command_create(CMD_IDENTIFY);
ahci_command_set_buffer(cmd, data_ptr);
/* Write the command header and PRDT to guest memory */
ahci_command_commit(ahci, cmd, i);
/* Everything is in place, but we haven't given the go-ahead yet,
* so we should find that there are no pending interrupts yet. */
g_assert_cmphex(ahci_px_rreg(ahci, i, AHCI_PX_IS), ==, 0);
/* Issue command and sanity check response. */
ahci_command_issue(ahci, cmd);
ahci_command_verify(ahci, cmd);
/* Last, but not least: Investigate the IDENTIFY response data. */
memread(data_ptr, &buff, 512);
/* Check serial number/version in the buffer */ /* Check serial number/version in the buffer */
/* NB: IDENTIFY strings are packed in 16bit little endian chunks. /* NB: IDENTIFY strings are packed in 16bit little endian chunks.

View File

@ -592,6 +592,31 @@ static AHCICommandProp *ahci_command_find(uint8_t command_name)
return NULL; return NULL;
} }
/* Given a HOST buffer, create a buffer address and perform an IO operation. */
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
void *buffer, size_t bufsize)
{
uint64_t ptr;
AHCICommandProp *props;
props = ahci_command_find(ide_cmd);
g_assert(props);
ptr = ahci_alloc(ahci, bufsize);
g_assert(ptr);
if (props->write) {
memwrite(ptr, buffer, bufsize);
}
ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize);
if (props->read) {
memread(ptr, buffer, bufsize);
}
ahci_free(ahci, ptr);
}
/** /**
* Initializes a basic command header in memory. * Initializes a basic command header in memory.
* We assume that this is for an ATA command using RegH2DFIS. * We assume that this is for an ATA command using RegH2DFIS.

View File

@ -523,6 +523,8 @@ unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd); unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd);
void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd, void ahci_guest_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
uint64_t gbuffer, size_t size); uint64_t gbuffer, size_t size);
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
void *buffer, size_t bufsize);
/* Command Lifecycle */ /* Command Lifecycle */
AHCICommand *ahci_command_create(uint8_t command_name); AHCICommand *ahci_command_create(uint8_t command_name);