-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1 iQIcBAABAgAGBQJWk/+LAAoJEH3vgQaq/DkOkxwP/ivsjXs69noYV+xpAiah5OwR MrfudT/4xFg2cSAbFDvAXbkk+6ibcxpd46zYX6X3aS56469vU0kOh5xueQtZeMOl 0XTZNN2LifbD+aa7DcnmHPqppe1cF0wJCzhZzc0cSv+LxASqyi9iQKt2ewscXdXO fkOoJxLA6620jYRrMet7linKWxAoM/Gp0gz0/jspOluzNrW1XbE2lrf8JPkUsKQB 8rgEJ9dUtye7duW790twGPTql/vrmRu/ZtVLjVvjwyhqPrIQnE+QhQ9qCQg9QBiW wNctK27X9Hck4XEH90TS5/M6van8h+OT9ouG2BAr7Uo8iacoSHmxV2w/IT+BglXL 0XFbAp377hEUKe3DzneKB7aLMOmPGnyKlr4/ZWJeYGyyaBadafsDvn0ALHM6ymUe qRtDhcQ7rjzkFU/AcNhdpT4wIO9hhUXSrZqb6KNYvipdtk1ul6RSOoiMS7Vz5nMI Kjeg7rbHBzKHmf1/rQGfT7YlMgQSo4kzSII6NDc6/HsrAYx4Zm7AL35nJN0LcC9h l11LEtyEp80X1hI9h25OHch50CfojWXNE29Rq9EuG7poa4mTGQDAIhmGiF5EZLF8 wkI0euoL0UwaAWbgNhaEECjMq6FSkH9I3xHmf2QuYaaa7oxnAYCcc0bh/eJQ7ARf hpkGcjG7/l1uT1MZ8dZb =5saf -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/jnsnow/tags/ide-pull-request' into staging # gpg: Signature made Mon 11 Jan 2016 19:16:27 GMT using RSA key ID AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" * remotes/jnsnow/tags/ide-pull-request: libqos/ahci: organize header qtest/ahci: ATAPI data tests libqos/ahci: add ahci_exec libqos/ahci: allow nondata commands for ahci_io variants libqos: allow zero-size allocations libqos/ahci: Switch to mutable properties libqos/ahci: ATAPI identify libqos/ahci: ATAPI support ahci-test: fix memory leak ide: ahci: reset ncq object to unused on error macio: fix overflow in lba to offset conversion for ATAPI devices Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
cf57c2f18b
|
@ -910,6 +910,7 @@ 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);
|
ncq_tfs->drive->port_regs.scr_err |= (1 << ncq_tfs->tag);
|
||||||
|
ncq_tfs->used = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ncq_finish(NCQTransferState *ncq_tfs)
|
static void ncq_finish(NCQTransferState *ncq_tfs)
|
||||||
|
|
|
@ -280,7 +280,7 @@ static void pmac_ide_atapi_transfer_cb(void *opaque, int ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate current offset */
|
/* Calculate current offset */
|
||||||
offset = (int64_t)(s->lba << 11) + s->io_buffer_index;
|
offset = ((int64_t)s->lba << 11) + s->io_buffer_index;
|
||||||
|
|
||||||
pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io);
|
pmac_dma_read(s->blk, offset, io->len, pmac_ide_atapi_transfer_cb, io);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -215,6 +215,7 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...)
|
||||||
va_list ap;
|
va_list ap;
|
||||||
uint16_t buff[256];
|
uint16_t buff[256];
|
||||||
uint8_t port;
|
uint8_t port;
|
||||||
|
uint8_t hello;
|
||||||
|
|
||||||
if (cli) {
|
if (cli) {
|
||||||
va_start(ap, cli);
|
va_start(ap, cli);
|
||||||
|
@ -229,7 +230,12 @@ static AHCIQState *ahci_boot_and_enable(const char *cli, ...)
|
||||||
/* Initialize test device */
|
/* Initialize test device */
|
||||||
port = ahci_port_select(ahci);
|
port = ahci_port_select(ahci);
|
||||||
ahci_port_clear(ahci, port);
|
ahci_port_clear(ahci, port);
|
||||||
ahci_io(ahci, port, CMD_IDENTIFY, &buff, sizeof(buff), 0);
|
if (is_atapi(ahci, port)) {
|
||||||
|
hello = CMD_PACKET_ID;
|
||||||
|
} else {
|
||||||
|
hello = CMD_IDENTIFY;
|
||||||
|
}
|
||||||
|
ahci_io(ahci, port, hello, &buff, sizeof(buff), 0);
|
||||||
|
|
||||||
return ahci;
|
return ahci;
|
||||||
}
|
}
|
||||||
|
@ -884,18 +890,12 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize,
|
||||||
static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd)
|
static uint8_t ahci_test_nondata(AHCIQState *ahci, uint8_t ide_cmd)
|
||||||
{
|
{
|
||||||
uint8_t port;
|
uint8_t port;
|
||||||
AHCICommand *cmd;
|
|
||||||
|
|
||||||
/* Sanitize */
|
/* Sanitize */
|
||||||
port = ahci_port_select(ahci);
|
port = ahci_port_select(ahci);
|
||||||
ahci_port_clear(ahci, port);
|
ahci_port_clear(ahci, port);
|
||||||
|
|
||||||
/* Issue Command */
|
ahci_io(ahci, port, ide_cmd, NULL, 0, 0);
|
||||||
cmd = ahci_command_create(ide_cmd);
|
|
||||||
ahci_command_commit(ahci, cmd, port);
|
|
||||||
ahci_command_issue(ahci, cmd);
|
|
||||||
ahci_command_verify(ahci, cmd);
|
|
||||||
ahci_command_free(cmd);
|
|
||||||
|
|
||||||
return port;
|
return port;
|
||||||
}
|
}
|
||||||
|
@ -1045,14 +1045,14 @@ static void test_dma_fragmented(void)
|
||||||
ahci_command_commit(ahci, cmd, px);
|
ahci_command_commit(ahci, cmd, px);
|
||||||
ahci_command_issue(ahci, cmd);
|
ahci_command_issue(ahci, cmd);
|
||||||
ahci_command_verify(ahci, cmd);
|
ahci_command_verify(ahci, cmd);
|
||||||
g_free(cmd);
|
ahci_command_free(cmd);
|
||||||
|
|
||||||
cmd = ahci_command_create(CMD_READ_DMA);
|
cmd = ahci_command_create(CMD_READ_DMA);
|
||||||
ahci_command_adjust(cmd, 0, ptr, bufsize, 32);
|
ahci_command_adjust(cmd, 0, ptr, bufsize, 32);
|
||||||
ahci_command_commit(ahci, cmd, px);
|
ahci_command_commit(ahci, cmd, px);
|
||||||
ahci_command_issue(ahci, cmd);
|
ahci_command_issue(ahci, cmd);
|
||||||
ahci_command_verify(ahci, cmd);
|
ahci_command_verify(ahci, cmd);
|
||||||
g_free(cmd);
|
ahci_command_free(cmd);
|
||||||
|
|
||||||
/* Read back the guest's receive buffer into local memory */
|
/* Read back the guest's receive buffer into local memory */
|
||||||
bufread(ptr, rx, bufsize);
|
bufread(ptr, rx, bufsize);
|
||||||
|
@ -1080,7 +1080,6 @@ static void test_flush_retry(void)
|
||||||
AHCIQState *ahci;
|
AHCIQState *ahci;
|
||||||
AHCICommand *cmd;
|
AHCICommand *cmd;
|
||||||
uint8_t port;
|
uint8_t port;
|
||||||
const char *s;
|
|
||||||
|
|
||||||
prepare_blkdebug_script(debug_path, "flush_to_disk");
|
prepare_blkdebug_script(debug_path, "flush_to_disk");
|
||||||
ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
|
ahci = ahci_boot_and_enable("-drive file=blkdebug:%s:%s,if=none,id=drive0,"
|
||||||
|
@ -1094,19 +1093,10 @@ static void test_flush_retry(void)
|
||||||
/* Issue Flush Command and wait for error */
|
/* Issue Flush Command and wait for error */
|
||||||
port = ahci_port_select(ahci);
|
port = ahci_port_select(ahci);
|
||||||
ahci_port_clear(ahci, port);
|
ahci_port_clear(ahci, port);
|
||||||
cmd = ahci_command_create(CMD_FLUSH_CACHE);
|
|
||||||
ahci_command_commit(ahci, cmd, port);
|
|
||||||
ahci_command_issue_async(ahci, cmd);
|
|
||||||
qmp_eventwait("STOP");
|
|
||||||
|
|
||||||
/* Complete the command */
|
cmd = ahci_guest_io_halt(ahci, port, CMD_FLUSH_CACHE, 0, 0, 0);
|
||||||
s = "{'execute':'cont' }";
|
ahci_guest_io_resume(ahci, cmd);
|
||||||
qmp_async(s);
|
|
||||||
qmp_eventwait("RESUME");
|
|
||||||
ahci_command_wait(ahci, cmd);
|
|
||||||
ahci_command_verify(ahci, cmd);
|
|
||||||
|
|
||||||
ahci_command_free(cmd);
|
|
||||||
ahci_shutdown(ahci);
|
ahci_shutdown(ahci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1423,6 +1413,98 @@ static void test_ncq_simple(void)
|
||||||
ahci_shutdown(ahci);
|
ahci_shutdown(ahci);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int prepare_iso(size_t size, unsigned char **buf, char **name)
|
||||||
|
{
|
||||||
|
char cdrom_path[] = "/tmp/qtest.iso.XXXXXX";
|
||||||
|
unsigned char *patt;
|
||||||
|
ssize_t ret;
|
||||||
|
int fd = mkstemp(cdrom_path);
|
||||||
|
|
||||||
|
g_assert(buf);
|
||||||
|
g_assert(name);
|
||||||
|
patt = g_malloc(size);
|
||||||
|
|
||||||
|
/* Generate a pattern and build a CDROM image to read from */
|
||||||
|
generate_pattern(patt, size, ATAPI_SECTOR_SIZE);
|
||||||
|
ret = write(fd, patt, size);
|
||||||
|
g_assert(ret == size);
|
||||||
|
|
||||||
|
*name = g_strdup(cdrom_path);
|
||||||
|
*buf = patt;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void remove_iso(int fd, char *name)
|
||||||
|
{
|
||||||
|
unlink(name);
|
||||||
|
g_free(name);
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd,
|
||||||
|
const AHCIOpts *opts)
|
||||||
|
{
|
||||||
|
unsigned char *tx = opts->opaque;
|
||||||
|
unsigned char *rx = g_malloc0(opts->size);
|
||||||
|
|
||||||
|
bufread(opts->buffer, rx, opts->size);
|
||||||
|
g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0);
|
||||||
|
g_free(rx);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ahci_test_cdrom(int nsectors, bool dma)
|
||||||
|
{
|
||||||
|
AHCIQState *ahci;
|
||||||
|
unsigned char *tx;
|
||||||
|
char *iso;
|
||||||
|
int fd;
|
||||||
|
AHCIOpts opts = {
|
||||||
|
.size = (ATAPI_SECTOR_SIZE * nsectors),
|
||||||
|
.atapi = true,
|
||||||
|
.atapi_dma = dma,
|
||||||
|
.post_cb = ahci_cb_cmp_buff,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Prepare ISO and fill 'tx' buffer */
|
||||||
|
fd = prepare_iso(1024 * 1024, &tx, &iso);
|
||||||
|
opts.opaque = tx;
|
||||||
|
|
||||||
|
/* Standard startup wonkery, but use ide-cd and our special iso file */
|
||||||
|
ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw "
|
||||||
|
"-M q35 "
|
||||||
|
"-device ide-cd,drive=drive0 ", iso);
|
||||||
|
|
||||||
|
/* Build & Send AHCI command */
|
||||||
|
ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_10, &opts);
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
g_free(tx);
|
||||||
|
ahci_shutdown(ahci);
|
||||||
|
remove_iso(fd, iso);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cdrom_dma(void)
|
||||||
|
{
|
||||||
|
ahci_test_cdrom(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cdrom_dma_multi(void)
|
||||||
|
{
|
||||||
|
ahci_test_cdrom(3, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cdrom_pio(void)
|
||||||
|
{
|
||||||
|
ahci_test_cdrom(1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_cdrom_pio_multi(void)
|
||||||
|
{
|
||||||
|
ahci_test_cdrom(3, false);
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/* AHCI I/O Test Matrix Definitions */
|
/* AHCI I/O Test Matrix Definitions */
|
||||||
|
|
||||||
|
@ -1707,6 +1789,11 @@ int main(int argc, char **argv)
|
||||||
qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq);
|
qtest_add_func("/ahci/io/ncq/retry", test_halted_ncq);
|
||||||
qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq);
|
qtest_add_func("/ahci/migrate/ncq/halted", test_migrate_halted_ncq);
|
||||||
|
|
||||||
|
qtest_add_func("/ahci/cdrom/dma/single", test_cdrom_dma);
|
||||||
|
qtest_add_func("/ahci/cdrom/dma/multi", test_cdrom_dma_multi);
|
||||||
|
qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio);
|
||||||
|
qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi);
|
||||||
|
|
||||||
ret = g_test_run();
|
ret = g_test_run();
|
||||||
|
|
||||||
/* Cleanup */
|
/* Cleanup */
|
||||||
|
|
|
@ -74,7 +74,11 @@ AHCICommandProp ahci_command_properties[] = {
|
||||||
.lba48 = true, .write = true, .ncq = true },
|
.lba48 = true, .write = true, .ncq = true },
|
||||||
{ .cmd = CMD_READ_MAX, .lba28 = true },
|
{ .cmd = CMD_READ_MAX, .lba28 = true },
|
||||||
{ .cmd = CMD_READ_MAX_EXT, .lba48 = true },
|
{ .cmd = CMD_READ_MAX_EXT, .lba48 = true },
|
||||||
{ .cmd = CMD_FLUSH_CACHE, .data = false }
|
{ .cmd = CMD_FLUSH_CACHE, .data = false },
|
||||||
|
{ .cmd = CMD_PACKET, .data = true, .size = 16,
|
||||||
|
.atapi = true, .pio = true },
|
||||||
|
{ .cmd = CMD_PACKET_ID, .data = true, .pio = true,
|
||||||
|
.size = 512, .read = true }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AHCICommand {
|
struct AHCICommand {
|
||||||
|
@ -90,7 +94,7 @@ struct AHCICommand {
|
||||||
/* Data to be transferred to the guest */
|
/* Data to be transferred to the guest */
|
||||||
AHCICommandHeader header;
|
AHCICommandHeader header;
|
||||||
RegH2DFIS fis;
|
RegH2DFIS fis;
|
||||||
void *atapi_cmd;
|
unsigned char *atapi_cmd;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,6 +114,11 @@ void ahci_free(AHCIQState *ahci, uint64_t addr)
|
||||||
qfree(ahci->parent, addr);
|
qfree(ahci->parent, addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_atapi(AHCIQState *ahci, uint8_t port)
|
||||||
|
{
|
||||||
|
return ahci_px_rreg(ahci, port, AHCI_PX_SIG) == AHCI_SIGNATURE_CDROM;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locate, verify, and return a handle to the AHCI device.
|
* Locate, verify, and return a handle to the AHCI device.
|
||||||
*/
|
*/
|
||||||
|
@ -592,6 +601,82 @@ inline unsigned size_to_prdtl(unsigned bytes, unsigned bytes_per_prd)
|
||||||
return (bytes + bytes_per_prd - 1) / bytes_per_prd;
|
return (bytes + bytes_per_prd - 1) / bytes_per_prd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const AHCIOpts default_opts = { .size = 0 };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ahci_exec: execute a given command on a specific
|
||||||
|
* AHCI port.
|
||||||
|
*
|
||||||
|
* @ahci: The device to send the command to
|
||||||
|
* @port: The port number of the SATA device we wish
|
||||||
|
* to have execute this command
|
||||||
|
* @op: The S/ATA command to execute, or if opts.atapi
|
||||||
|
* is true, the SCSI command code.
|
||||||
|
* @opts: Optional arguments to modify execution behavior.
|
||||||
|
*/
|
||||||
|
void ahci_exec(AHCIQState *ahci, uint8_t port,
|
||||||
|
uint8_t op, const AHCIOpts *opts_in)
|
||||||
|
{
|
||||||
|
AHCICommand *cmd;
|
||||||
|
int rc;
|
||||||
|
AHCIOpts *opts;
|
||||||
|
|
||||||
|
opts = g_memdup((opts_in == NULL ? &default_opts : opts_in),
|
||||||
|
sizeof(AHCIOpts));
|
||||||
|
|
||||||
|
/* No guest buffer provided, create one. */
|
||||||
|
if (opts->size && !opts->buffer) {
|
||||||
|
opts->buffer = ahci_alloc(ahci, opts->size);
|
||||||
|
g_assert(opts->buffer);
|
||||||
|
qmemset(opts->buffer, 0x00, opts->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Command creation */
|
||||||
|
if (opts->atapi) {
|
||||||
|
cmd = ahci_atapi_command_create(op);
|
||||||
|
if (opts->atapi_dma) {
|
||||||
|
ahci_command_enable_atapi_dma(cmd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmd = ahci_command_create(op);
|
||||||
|
}
|
||||||
|
ahci_command_adjust(cmd, opts->lba, opts->buffer,
|
||||||
|
opts->size, opts->prd_size);
|
||||||
|
|
||||||
|
if (opts->pre_cb) {
|
||||||
|
rc = opts->pre_cb(ahci, cmd, opts);
|
||||||
|
g_assert_cmpint(rc, ==, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write command to memory and issue it */
|
||||||
|
ahci_command_commit(ahci, cmd, port);
|
||||||
|
ahci_command_issue_async(ahci, cmd);
|
||||||
|
if (opts->error) {
|
||||||
|
qmp_eventwait("STOP");
|
||||||
|
}
|
||||||
|
if (opts->mid_cb) {
|
||||||
|
rc = opts->mid_cb(ahci, cmd, opts);
|
||||||
|
g_assert_cmpint(rc, ==, 0);
|
||||||
|
}
|
||||||
|
if (opts->error) {
|
||||||
|
qmp_async("{'execute':'cont' }");
|
||||||
|
qmp_eventwait("RESUME");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for command to complete and verify sanity */
|
||||||
|
ahci_command_wait(ahci, cmd);
|
||||||
|
ahci_command_verify(ahci, cmd);
|
||||||
|
if (opts->post_cb) {
|
||||||
|
rc = opts->post_cb(ahci, cmd, opts);
|
||||||
|
g_assert_cmpint(rc, ==, 0);
|
||||||
|
}
|
||||||
|
ahci_command_free(cmd);
|
||||||
|
if (opts->buffer != opts_in->buffer) {
|
||||||
|
ahci_free(ahci, opts->buffer);
|
||||||
|
}
|
||||||
|
g_free(opts);
|
||||||
|
}
|
||||||
|
|
||||||
/* Issue a command, expecting it to fail and STOP the VM */
|
/* Issue a command, expecting it to fail and STOP the VM */
|
||||||
AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port,
|
AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port,
|
||||||
uint8_t ide_cmd, uint64_t buffer,
|
uint8_t ide_cmd, uint64_t buffer,
|
||||||
|
@ -659,16 +744,16 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||||
props = ahci_command_find(ide_cmd);
|
props = ahci_command_find(ide_cmd);
|
||||||
g_assert(props);
|
g_assert(props);
|
||||||
ptr = ahci_alloc(ahci, bufsize);
|
ptr = ahci_alloc(ahci, bufsize);
|
||||||
g_assert(ptr);
|
g_assert(!bufsize || ptr);
|
||||||
qmemset(ptr, 0x00, bufsize);
|
qmemset(ptr, 0x00, bufsize);
|
||||||
|
|
||||||
if (props->write) {
|
if (bufsize && props->write) {
|
||||||
bufwrite(ptr, buffer, bufsize);
|
bufwrite(ptr, buffer, bufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector);
|
ahci_guest_io(ahci, port, ide_cmd, ptr, bufsize, sector);
|
||||||
|
|
||||||
if (props->read) {
|
if (bufsize && props->read) {
|
||||||
bufread(ptr, buffer, bufsize);
|
bufread(ptr, buffer, bufsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -731,6 +816,18 @@ static void command_table_init(AHCICommand *cmd)
|
||||||
memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux));
|
memset(fis->aux, 0x00, ARRAY_SIZE(fis->aux));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ahci_command_enable_atapi_dma(AHCICommand *cmd)
|
||||||
|
{
|
||||||
|
RegH2DFIS *fis = &(cmd->fis);
|
||||||
|
g_assert(cmd->props->atapi);
|
||||||
|
fis->feature_low |= 0x01;
|
||||||
|
cmd->interrupts &= ~AHCI_PX_IS_PSS;
|
||||||
|
cmd->props->dma = true;
|
||||||
|
cmd->props->pio = false;
|
||||||
|
/* BUG: We expect the DMA Setup interrupt for DMA commands */
|
||||||
|
/* cmd->interrupts |= AHCI_PX_IS_DSS; */
|
||||||
|
}
|
||||||
|
|
||||||
AHCICommand *ahci_command_create(uint8_t command_name)
|
AHCICommand *ahci_command_create(uint8_t command_name)
|
||||||
{
|
{
|
||||||
AHCICommandProp *props = ahci_command_find(command_name);
|
AHCICommandProp *props = ahci_command_find(command_name);
|
||||||
|
@ -745,7 +842,7 @@ AHCICommand *ahci_command_create(uint8_t command_name)
|
||||||
g_assert(!props->ncq || props->lba48);
|
g_assert(!props->ncq || props->lba48);
|
||||||
|
|
||||||
/* Defaults and book-keeping */
|
/* Defaults and book-keeping */
|
||||||
cmd->props = props;
|
cmd->props = g_memdup(props, sizeof(AHCICommandProp));
|
||||||
cmd->name = command_name;
|
cmd->name = command_name;
|
||||||
cmd->xbytes = props->size;
|
cmd->xbytes = props->size;
|
||||||
cmd->prd_size = 4096;
|
cmd->prd_size = 4096;
|
||||||
|
@ -767,8 +864,23 @@ AHCICommand *ahci_command_create(uint8_t command_name)
|
||||||
return cmd;
|
return cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd)
|
||||||
|
{
|
||||||
|
AHCICommand *cmd = ahci_command_create(CMD_PACKET);
|
||||||
|
cmd->atapi_cmd = g_malloc0(16);
|
||||||
|
cmd->atapi_cmd[0] = scsi_cmd;
|
||||||
|
/* ATAPI needs a PIO transfer chunk size set inside of the LBA registers.
|
||||||
|
* The block/sector size is a natural default. */
|
||||||
|
cmd->fis.lba_lo[1] = ATAPI_SECTOR_SIZE >> 8 & 0xFF;
|
||||||
|
cmd->fis.lba_lo[2] = ATAPI_SECTOR_SIZE & 0xFF;
|
||||||
|
|
||||||
|
return cmd;
|
||||||
|
}
|
||||||
|
|
||||||
void ahci_command_free(AHCICommand *cmd)
|
void ahci_command_free(AHCICommand *cmd)
|
||||||
{
|
{
|
||||||
|
g_free(cmd->atapi_cmd);
|
||||||
|
g_free(cmd->props);
|
||||||
g_free(cmd);
|
g_free(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -782,10 +894,34 @@ void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags)
|
||||||
cmd->header.flags &= ~cmdh_flags;
|
cmd->header.flags &= ~cmdh_flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba)
|
||||||
|
{
|
||||||
|
unsigned char *cbd = cmd->atapi_cmd;
|
||||||
|
g_assert(cbd);
|
||||||
|
|
||||||
|
switch (cbd[0]) {
|
||||||
|
case CMD_ATAPI_READ_10:
|
||||||
|
g_assert_cmpuint(lba, <=, UINT32_MAX);
|
||||||
|
stl_be_p(&cbd[2], lba);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* SCSI doesn't have uniform packet formats,
|
||||||
|
* so you have to add support for it manually. Sorry! */
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
|
void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect)
|
||||||
{
|
{
|
||||||
RegH2DFIS *fis = &(cmd->fis);
|
RegH2DFIS *fis = &(cmd->fis);
|
||||||
if (cmd->props->lba28) {
|
|
||||||
|
if (cmd->props->atapi) {
|
||||||
|
ahci_atapi_command_set_offset(cmd, lba_sect);
|
||||||
|
return;
|
||||||
|
} else if (!cmd->props->data && !lba_sect) {
|
||||||
|
/* Not meaningful, ignore. */
|
||||||
|
return;
|
||||||
|
} else if (cmd->props->lba28) {
|
||||||
g_assert_cmphex(lba_sect, <=, 0xFFFFFFF);
|
g_assert_cmphex(lba_sect, <=, 0xFFFFFFF);
|
||||||
} else if (cmd->props->lba48 || cmd->props->ncq) {
|
} else if (cmd->props->lba48 || cmd->props->ncq) {
|
||||||
g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF);
|
g_assert_cmphex(lba_sect, <=, 0xFFFFFFFFFFFF);
|
||||||
|
@ -811,6 +947,24 @@ void ahci_command_set_buffer(AHCICommand *cmd, uint64_t buffer)
|
||||||
cmd->buffer = buffer;
|
cmd->buffer = buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
|
||||||
|
{
|
||||||
|
unsigned char *cbd = cmd->atapi_cmd;
|
||||||
|
uint64_t nsectors = xbytes / 2048;
|
||||||
|
g_assert(cbd);
|
||||||
|
|
||||||
|
switch (cbd[0]) {
|
||||||
|
case CMD_ATAPI_READ_10:
|
||||||
|
g_assert_cmpuint(nsectors, <=, UINT16_MAX);
|
||||||
|
stw_be_p(&cbd[7], nsectors);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* SCSI doesn't have uniform packet formats,
|
||||||
|
* so you have to add support for it manually. Sorry! */
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
|
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
|
||||||
unsigned prd_size)
|
unsigned prd_size)
|
||||||
{
|
{
|
||||||
|
@ -829,6 +983,8 @@ void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
|
||||||
NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
|
NCQFIS *nfis = (NCQFIS *)&(cmd->fis);
|
||||||
nfis->sector_low = sect_count & 0xFF;
|
nfis->sector_low = sect_count & 0xFF;
|
||||||
nfis->sector_hi = (sect_count >> 8) & 0xFF;
|
nfis->sector_hi = (sect_count >> 8) & 0xFF;
|
||||||
|
} else if (cmd->props->atapi) {
|
||||||
|
ahci_atapi_set_size(cmd, xbytes);
|
||||||
} else {
|
} else {
|
||||||
cmd->fis.count = sect_count;
|
cmd->fis.count = sect_count;
|
||||||
}
|
}
|
||||||
|
@ -877,9 +1033,14 @@ void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port)
|
||||||
g_assert((table_ptr & 0x7F) == 0x00);
|
g_assert((table_ptr & 0x7F) == 0x00);
|
||||||
cmd->header.ctba = table_ptr;
|
cmd->header.ctba = table_ptr;
|
||||||
|
|
||||||
/* Commit the command header and command FIS */
|
/* Commit the command header (part of the Command List Buffer) */
|
||||||
ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header));
|
ahci_set_command_header(ahci, port, cmd->slot, &(cmd->header));
|
||||||
|
/* Now, write the command table (FIS, ACMD, and PRDT) -- FIS first, */
|
||||||
ahci_write_fis(ahci, cmd);
|
ahci_write_fis(ahci, cmd);
|
||||||
|
/* Then ATAPI CMD, if needed */
|
||||||
|
if (cmd->props->atapi) {
|
||||||
|
memwrite(table_ptr + 0x40, cmd->atapi_cmd, 16);
|
||||||
|
}
|
||||||
|
|
||||||
/* Construct and write the PRDs to the command table */
|
/* Construct and write the PRDs to the command table */
|
||||||
g_assert_cmphex(prdtl, ==, cmd->header.prdtl);
|
g_assert_cmphex(prdtl, ==, cmd->header.prdtl);
|
||||||
|
|
|
@ -244,6 +244,10 @@
|
||||||
#define AHCI_VERSION_1_3 (0x00010300)
|
#define AHCI_VERSION_1_3 (0x00010300)
|
||||||
|
|
||||||
#define AHCI_SECTOR_SIZE (512)
|
#define AHCI_SECTOR_SIZE (512)
|
||||||
|
#define ATAPI_SECTOR_SIZE (2048)
|
||||||
|
|
||||||
|
#define AHCI_SIGNATURE_CDROM (0xeb140101)
|
||||||
|
#define AHCI_SIGNATURE_DISK (0x00000101)
|
||||||
|
|
||||||
/* FIS types */
|
/* FIS types */
|
||||||
enum {
|
enum {
|
||||||
|
@ -277,11 +281,18 @@ enum {
|
||||||
CMD_READ_MAX_EXT = 0x27,
|
CMD_READ_MAX_EXT = 0x27,
|
||||||
CMD_FLUSH_CACHE = 0xE7,
|
CMD_FLUSH_CACHE = 0xE7,
|
||||||
CMD_IDENTIFY = 0xEC,
|
CMD_IDENTIFY = 0xEC,
|
||||||
|
CMD_PACKET = 0xA0,
|
||||||
|
CMD_PACKET_ID = 0xA1,
|
||||||
/* NCQ */
|
/* NCQ */
|
||||||
READ_FPDMA_QUEUED = 0x60,
|
READ_FPDMA_QUEUED = 0x60,
|
||||||
WRITE_FPDMA_QUEUED = 0x61,
|
WRITE_FPDMA_QUEUED = 0x61,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ATAPI Commands */
|
||||||
|
enum {
|
||||||
|
CMD_ATAPI_READ_10 = 0x28,
|
||||||
|
};
|
||||||
|
|
||||||
/* AHCI Command Header Flags & Masks*/
|
/* AHCI Command Header Flags & Masks*/
|
||||||
#define CMDH_CFL (0x1F)
|
#define CMDH_CFL (0x1F)
|
||||||
#define CMDH_ATAPI (0x20)
|
#define CMDH_ATAPI (0x20)
|
||||||
|
@ -451,6 +462,21 @@ typedef struct PRD {
|
||||||
/* Opaque, defined within ahci.c */
|
/* Opaque, defined within ahci.c */
|
||||||
typedef struct AHCICommand AHCICommand;
|
typedef struct AHCICommand AHCICommand;
|
||||||
|
|
||||||
|
/* Options to ahci_exec */
|
||||||
|
typedef struct AHCIOpts {
|
||||||
|
size_t size;
|
||||||
|
unsigned prd_size;
|
||||||
|
uint64_t lba;
|
||||||
|
uint64_t buffer;
|
||||||
|
bool atapi;
|
||||||
|
bool atapi_dma;
|
||||||
|
bool error;
|
||||||
|
int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
|
||||||
|
int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
|
||||||
|
int (*post_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
|
||||||
|
void *opaque;
|
||||||
|
} AHCIOpts;
|
||||||
|
|
||||||
/*** Macro Utilities ***/
|
/*** Macro Utilities ***/
|
||||||
#define BITANY(data, mask) (((data) & (mask)) != 0)
|
#define BITANY(data, mask) (((data) & (mask)) != 0)
|
||||||
#define BITSET(data, mask) (((data) & (mask)) == (mask))
|
#define BITSET(data, mask) (((data) & (mask)) == (mask))
|
||||||
|
@ -527,14 +553,28 @@ static inline void ahci_px_clr(AHCIQState *ahci, uint8_t port,
|
||||||
/*** Prototypes ***/
|
/*** Prototypes ***/
|
||||||
uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes);
|
uint64_t ahci_alloc(AHCIQState *ahci, size_t bytes);
|
||||||
void ahci_free(AHCIQState *ahci, uint64_t addr);
|
void ahci_free(AHCIQState *ahci, uint64_t addr);
|
||||||
|
void ahci_clean_mem(AHCIQState *ahci);
|
||||||
|
|
||||||
|
/* Device management */
|
||||||
QPCIDevice *get_ahci_device(uint32_t *fingerprint);
|
QPCIDevice *get_ahci_device(uint32_t *fingerprint);
|
||||||
void free_ahci_device(QPCIDevice *dev);
|
void free_ahci_device(QPCIDevice *dev);
|
||||||
void ahci_clean_mem(AHCIQState *ahci);
|
|
||||||
void ahci_pci_enable(AHCIQState *ahci);
|
void ahci_pci_enable(AHCIQState *ahci);
|
||||||
void start_ahci_device(AHCIQState *ahci);
|
void start_ahci_device(AHCIQState *ahci);
|
||||||
void ahci_hba_enable(AHCIQState *ahci);
|
void ahci_hba_enable(AHCIQState *ahci);
|
||||||
|
|
||||||
|
/* Port Management */
|
||||||
unsigned ahci_port_select(AHCIQState *ahci);
|
unsigned ahci_port_select(AHCIQState *ahci);
|
||||||
void ahci_port_clear(AHCIQState *ahci, uint8_t port);
|
void ahci_port_clear(AHCIQState *ahci, uint8_t port);
|
||||||
|
|
||||||
|
/* Command header / table management */
|
||||||
|
unsigned ahci_pick_cmd(AHCIQState *ahci, uint8_t port);
|
||||||
|
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
|
||||||
|
uint8_t slot, AHCICommandHeader *cmd);
|
||||||
|
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
||||||
|
uint8_t slot, AHCICommandHeader *cmd);
|
||||||
|
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||||
|
|
||||||
|
/* AHCI sanity check routines */
|
||||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port);
|
void ahci_port_check_error(AHCIQState *ahci, uint8_t port);
|
||||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
||||||
uint32_t intr_mask);
|
uint32_t intr_mask);
|
||||||
|
@ -543,14 +583,12 @@ void ahci_port_check_d2h_sanity(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||||
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
|
void ahci_port_check_pio_sanity(AHCIQState *ahci, uint8_t port,
|
||||||
uint8_t slot, size_t buffsize);
|
uint8_t slot, size_t buffsize);
|
||||||
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_port_check_cmd_sanity(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_get_command_header(AHCIQState *ahci, uint8_t port,
|
|
||||||
uint8_t slot, AHCICommandHeader *cmd);
|
/* Misc */
|
||||||
void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
bool is_atapi(AHCIQState *ahci, uint8_t port);
|
||||||
uint8_t slot, AHCICommandHeader *cmd);
|
|
||||||
void ahci_destroy_command(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
|
||||||
void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd);
|
|
||||||
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);
|
||||||
|
|
||||||
|
/* Command: Macro level execution */
|
||||||
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 sector);
|
uint64_t gbuffer, size_t size, uint64_t sector);
|
||||||
AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||||
|
@ -558,9 +596,12 @@ AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||||
void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
||||||
void *buffer, size_t bufsize, uint64_t sector);
|
void *buffer, size_t bufsize, uint64_t sector);
|
||||||
|
void ahci_exec(AHCIQState *ahci, uint8_t port,
|
||||||
|
uint8_t op, const AHCIOpts *opts);
|
||||||
|
|
||||||
/* Command Lifecycle */
|
/* Command: Fine-grained lifecycle */
|
||||||
AHCICommand *ahci_command_create(uint8_t command_name);
|
AHCICommand *ahci_command_create(uint8_t command_name);
|
||||||
|
AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd);
|
||||||
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
|
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
|
||||||
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
|
@ -568,7 +609,7 @@ void ahci_command_wait(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd);
|
void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
void ahci_command_free(AHCICommand *cmd);
|
void ahci_command_free(AHCICommand *cmd);
|
||||||
|
|
||||||
/* Command adjustments */
|
/* Command: adjustments */
|
||||||
void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags);
|
void ahci_command_set_flags(AHCICommand *cmd, uint16_t cmdh_flags);
|
||||||
void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags);
|
void ahci_command_clr_flags(AHCICommand *cmd, uint16_t cmdh_flags);
|
||||||
void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect);
|
void ahci_command_set_offset(AHCICommand *cmd, uint64_t lba_sect);
|
||||||
|
@ -577,10 +618,13 @@ void ahci_command_set_size(AHCICommand *cmd, uint64_t xbytes);
|
||||||
void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size);
|
void ahci_command_set_prd_size(AHCICommand *cmd, unsigned prd_size);
|
||||||
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
|
void ahci_command_set_sizes(AHCICommand *cmd, uint64_t xbytes,
|
||||||
unsigned prd_size);
|
unsigned prd_size);
|
||||||
|
void ahci_command_set_acmd(AHCICommand *cmd, void *acmd);
|
||||||
|
void ahci_command_enable_atapi_dma(AHCICommand *cmd);
|
||||||
void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer,
|
void ahci_command_adjust(AHCICommand *cmd, uint64_t lba_sect, uint64_t gbuffer,
|
||||||
uint64_t xbytes, unsigned prd_size);
|
uint64_t xbytes, unsigned prd_size);
|
||||||
|
|
||||||
/* Command Misc */
|
/* Command: Misc */
|
||||||
uint8_t ahci_command_slot(AHCICommand *cmd);
|
uint8_t ahci_command_slot(AHCICommand *cmd);
|
||||||
|
void ahci_write_fis(AHCIQState *ahci, AHCICommand *cmd);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -270,6 +270,10 @@ uint64_t guest_alloc(QGuestAllocator *allocator, size_t size)
|
||||||
uint64_t rsize = size;
|
uint64_t rsize = size;
|
||||||
uint64_t naddr;
|
uint64_t naddr;
|
||||||
|
|
||||||
|
if (!size) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
rsize += (allocator->page_size - 1);
|
rsize += (allocator->page_size - 1);
|
||||||
rsize &= -allocator->page_size;
|
rsize &= -allocator->page_size;
|
||||||
g_assert_cmpint((allocator->start + rsize), <=, allocator->end);
|
g_assert_cmpint((allocator->start + rsize), <=, allocator->end);
|
||||||
|
|
Loading…
Reference in a new issue