mirror of https://github.com/xemu-project/xemu.git
-----BEGIN PGP SIGNATURE-----
iQIcBAABAgAGBQJYKeNwAAoJEH3vgQaq/DkO2uAQAKUidQMRQjHs3T5vyb7PcXCe DVV3PO+xKIFl+eWbjDYH2OdPL8OzgyNcGnwtHkdogKklWvYMD002vQ9YmNa2cbJn cO5d8jzSRtsTTLSbtjipFIrvJ8FxedX3Jay0cvEbaEqkgZXJV1sXN5CJ/Cseyf+G IZrG047Kf4V3inV8RDvJ9U/VcSlIcst9icZOuLlONvhXM7f+R5CkvqwUn4yVOObt Wwq32r47Dd9BwzrpxM//7haDvAXYm/xcP3bImN/3LAAwYPGkswxOe1I7Q62+fbpe dd/FSfhe6nRjStKTtH7T+AQk1VJKw34su9/FSxzIZaCzHYMco5CIziCwi0s4BocR GqZ0E0oPxWvrrFhljBxt1wA4d2j354Wq2cGbmb7rQpJTEbfGH5nDHqF1FAbMmd8N F9H6tSCvh1xJaJngGZjlMsgs6TkqyQEnCjk7SSAs1XS+qyrcyOWk7ydzAAc/RIHl iIN4aLcL7ix1rcoVttw+4VOSvihas6nTvRPPwVTbHO5003QpXdr3dckQaASP3PTd wky7blVk8+O8Y242F0AAYUb04agZ+KpqsaOcCL3SIPc3yBv3JCNCNy0gH4WIBX66 yYxTgRtaNhHiUWaVQLximq1QUjz+vsTE07FI56PSabz1e/RkRp+BbrwaYLKYy+/F jBfRpP7pkPIWJhrPmYpJ =fKei -----END PGP SIGNATURE----- Merge remote-tracking branch 'jsnow/tags/ide-pull-request' into staging # gpg: Signature made Mon 14 Nov 2016 04:16:48 PM GMT # gpg: using RSA key 0x7DEF8106AAFC390E # gpg: Good signature from "John Snow (John Huston) <jsnow@redhat.com>" # 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 * jsnow/tags/ide-pull-request: ahci-test: add QMP tray test for ATAPI libqos/ahci: Add get_sense and test_ready libqos/ahci: Add ATAPI tray macros libqos/ahci: Support expected errors libqtest: add qmp_eventwait_ref block-backend: Always notify on blk_eject ahci-test: test atapi read_cd with bcl, nb_sectors = 0 ahci-test: Create smaller test ISO images atapi: classify read_cd as conditionally returning data Message-id: 1479140746-22142-1-git-send-email-jsnow@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
commit
682df581c6
|
@ -1393,13 +1393,14 @@ void blk_eject(BlockBackend *blk, bool eject_flag)
|
|||
|
||||
if (bs) {
|
||||
bdrv_eject(bs, eject_flag);
|
||||
|
||||
id = blk_get_attached_dev_id(blk);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
||||
eject_flag, &error_abort);
|
||||
g_free(id);
|
||||
|
||||
}
|
||||
|
||||
/* Whether or not we ejected on the backend,
|
||||
* the frontend experienced a tray event. */
|
||||
id = blk_get_attached_dev_id(blk);
|
||||
qapi_event_send_device_tray_moved(blk_name(blk), id,
|
||||
eject_flag, &error_abort);
|
||||
g_free(id);
|
||||
}
|
||||
|
||||
int blk_get_flags(BlockBackend *blk)
|
||||
|
|
|
@ -637,6 +637,23 @@ static unsigned int event_status_media(IDEState *s,
|
|||
return 8; /* We wrote to 4 extra bytes from the header */
|
||||
}
|
||||
|
||||
/*
|
||||
* Before transferring data or otherwise signalling acceptance of a command
|
||||
* marked CONDDATA, we must check the validity of the byte_count_limit.
|
||||
*/
|
||||
static bool validate_bcl(IDEState *s)
|
||||
{
|
||||
/* TODO: Check IDENTIFY data word 125 for defacult BCL (currently 0) */
|
||||
if (s->atapi_dma || atapi_byte_count_limit(s)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* TODO: Move abort back into core.c and introduce proper error flow between
|
||||
* ATAPI layer and IDE core layer */
|
||||
ide_abort_command(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void cmd_get_event_status_notification(IDEState *s,
|
||||
uint8_t *buf)
|
||||
{
|
||||
|
@ -1028,12 +1045,19 @@ static void cmd_read_cd(IDEState *s, uint8_t* buf)
|
|||
return;
|
||||
}
|
||||
|
||||
transfer_request = buf[9];
|
||||
switch(transfer_request & 0xf8) {
|
||||
case 0x00:
|
||||
transfer_request = buf[9] & 0xf8;
|
||||
if (transfer_request == 0x00) {
|
||||
/* nothing */
|
||||
ide_atapi_cmd_ok(s);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check validity of BCL before transferring data */
|
||||
if (!validate_bcl(s)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (transfer_request) {
|
||||
case 0x10:
|
||||
/* normal read */
|
||||
ide_atapi_cmd_read(s, lba, nb_sectors, 2048);
|
||||
|
@ -1266,6 +1290,14 @@ enum {
|
|||
* See ATA8-ACS3 "7.21.5 Byte Count Limit"
|
||||
*/
|
||||
NONDATA = 0x04,
|
||||
|
||||
/*
|
||||
* CONDDATA implies a command that transfers data only conditionally based
|
||||
* on the presence of suboptions. It should be exempt from the BCL check at
|
||||
* command validation time, but it needs to be checked at the command
|
||||
* handler level instead.
|
||||
*/
|
||||
CONDDATA = 0x08,
|
||||
};
|
||||
|
||||
static const struct AtapiCmd {
|
||||
|
@ -1289,7 +1321,7 @@ static const struct AtapiCmd {
|
|||
[ 0xad ] = { cmd_read_dvd_structure, CHECK_READY },
|
||||
[ 0xbb ] = { cmd_set_speed, NONDATA },
|
||||
[ 0xbd ] = { cmd_mechanism_status, 0 },
|
||||
[ 0xbe ] = { cmd_read_cd, CHECK_READY },
|
||||
[ 0xbe ] = { cmd_read_cd, CHECK_READY | CONDDATA },
|
||||
/* [1] handler detects and reports not ready condition itself */
|
||||
};
|
||||
|
||||
|
@ -1348,15 +1380,12 @@ void ide_atapi_cmd(IDEState *s)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Nondata commands permit the byte_count_limit to be 0.
|
||||
/* Commands that don't transfer DATA permit the byte_count_limit to be 0.
|
||||
* If this is a data-transferring PIO command and BCL is 0,
|
||||
* we abort at the /ATA/ level, not the ATAPI level.
|
||||
* See ATA8 ACS3 section 7.17.6.49 and 7.21.5 */
|
||||
if (cmd->handler && !(cmd->flags & NONDATA)) {
|
||||
/* TODO: Check IDENTIFY data word 125 for default BCL (currently 0) */
|
||||
if (!(atapi_byte_count_limit(s) || s->atapi_dma)) {
|
||||
/* TODO: Move abort back into core.c and make static inline again */
|
||||
ide_abort_command(s);
|
||||
if (cmd->handler && !(cmd->flags & (NONDATA | CONDDATA))) {
|
||||
if (!validate_bcl(s)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1473,8 +1473,13 @@ 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);
|
||||
unsigned char *rx;
|
||||
|
||||
if (!opts->size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rx = g_malloc0(opts->size);
|
||||
bufread(opts->buffer, rx, opts->size);
|
||||
g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0);
|
||||
g_free(rx);
|
||||
|
@ -1482,7 +1487,8 @@ static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ahci_test_cdrom(int nsectors, bool dma)
|
||||
static void ahci_test_cdrom(int nsectors, bool dma, uint8_t cmd,
|
||||
bool override_bcl, uint16_t bcl)
|
||||
{
|
||||
AHCIQState *ahci;
|
||||
unsigned char *tx;
|
||||
|
@ -1493,10 +1499,13 @@ static void ahci_test_cdrom(int nsectors, bool dma)
|
|||
.atapi = true,
|
||||
.atapi_dma = dma,
|
||||
.post_cb = ahci_cb_cmp_buff,
|
||||
.set_bcl = override_bcl,
|
||||
.bcl = bcl,
|
||||
};
|
||||
uint64_t iso_size = ATAPI_SECTOR_SIZE * (nsectors + 1);
|
||||
|
||||
/* Prepare ISO and fill 'tx' buffer */
|
||||
fd = prepare_iso(1024 * 1024, &tx, &iso);
|
||||
fd = prepare_iso(iso_size, &tx, &iso);
|
||||
opts.opaque = tx;
|
||||
|
||||
/* Standard startup wonkery, but use ide-cd and our special iso file */
|
||||
|
@ -1505,7 +1514,7 @@ static void ahci_test_cdrom(int nsectors, bool dma)
|
|||
"-device ide-cd,drive=drive0 ", iso);
|
||||
|
||||
/* Build & Send AHCI command */
|
||||
ahci_exec(ahci, ahci_port_select(ahci), CMD_ATAPI_READ_10, &opts);
|
||||
ahci_exec(ahci, ahci_port_select(ahci), cmd, &opts);
|
||||
|
||||
/* Cleanup */
|
||||
g_free(tx);
|
||||
|
@ -1513,24 +1522,133 @@ static void ahci_test_cdrom(int nsectors, bool dma)
|
|||
remove_iso(fd, iso);
|
||||
}
|
||||
|
||||
static void ahci_test_cdrom_read10(int nsectors, bool dma)
|
||||
{
|
||||
ahci_test_cdrom(nsectors, dma, CMD_ATAPI_READ_10, false, 0);
|
||||
}
|
||||
|
||||
static void test_cdrom_dma(void)
|
||||
{
|
||||
ahci_test_cdrom(1, true);
|
||||
ahci_test_cdrom_read10(1, true);
|
||||
}
|
||||
|
||||
static void test_cdrom_dma_multi(void)
|
||||
{
|
||||
ahci_test_cdrom(3, true);
|
||||
ahci_test_cdrom_read10(3, true);
|
||||
}
|
||||
|
||||
static void test_cdrom_pio(void)
|
||||
{
|
||||
ahci_test_cdrom(1, false);
|
||||
ahci_test_cdrom_read10(1, false);
|
||||
}
|
||||
|
||||
static void test_cdrom_pio_multi(void)
|
||||
{
|
||||
ahci_test_cdrom(3, false);
|
||||
ahci_test_cdrom_read10(3, false);
|
||||
}
|
||||
|
||||
/* Regression test: Test that a READ_CD command with a BCL of 0 but a size of 0
|
||||
* completes as a NOP instead of erroring out. */
|
||||
static void test_atapi_bcl(void)
|
||||
{
|
||||
ahci_test_cdrom(0, false, CMD_ATAPI_READ_CD, true, 0);
|
||||
}
|
||||
|
||||
|
||||
static void atapi_wait_tray(bool open)
|
||||
{
|
||||
QDict *rsp = qmp_eventwait_ref("DEVICE_TRAY_MOVED");
|
||||
QDict *data = qdict_get_qdict(rsp, "data");
|
||||
if (open) {
|
||||
g_assert(qdict_get_bool(data, "tray-open"));
|
||||
} else {
|
||||
g_assert(!qdict_get_bool(data, "tray-open"));
|
||||
}
|
||||
QDECREF(rsp);
|
||||
}
|
||||
|
||||
static void test_atapi_tray(void)
|
||||
{
|
||||
AHCIQState *ahci;
|
||||
unsigned char *tx;
|
||||
char *iso;
|
||||
int fd;
|
||||
uint8_t port, sense, asc;
|
||||
uint64_t iso_size = ATAPI_SECTOR_SIZE;
|
||||
QDict *rsp;
|
||||
|
||||
fd = prepare_iso(iso_size, &tx, &iso);
|
||||
ahci = ahci_boot_and_enable("-drive if=none,id=drive0,file=%s,format=raw "
|
||||
"-M q35 "
|
||||
"-device ide-cd,drive=drive0 ", iso);
|
||||
port = ahci_port_select(ahci);
|
||||
|
||||
ahci_atapi_eject(ahci, port);
|
||||
atapi_wait_tray(true);
|
||||
|
||||
ahci_atapi_load(ahci, port);
|
||||
atapi_wait_tray(false);
|
||||
|
||||
/* Remove media */
|
||||
qmp_async("{'execute': 'blockdev-open-tray', "
|
||||
"'arguments': {'device': 'drive0'}}");
|
||||
atapi_wait_tray(true);
|
||||
rsp = qmp_receive();
|
||||
QDECREF(rsp);
|
||||
|
||||
qmp_discard_response("{'execute': 'x-blockdev-remove-medium', "
|
||||
"'arguments': {'device': 'drive0'}}");
|
||||
|
||||
/* Test the tray without a medium */
|
||||
ahci_atapi_load(ahci, port);
|
||||
atapi_wait_tray(false);
|
||||
|
||||
ahci_atapi_eject(ahci, port);
|
||||
atapi_wait_tray(true);
|
||||
|
||||
/* Re-insert media */
|
||||
qmp_discard_response("{'execute': 'blockdev-add', "
|
||||
"'arguments': {'node-name': 'node0', "
|
||||
"'driver': 'raw', "
|
||||
"'file': { 'driver': 'file', "
|
||||
"'filename': %s }}}", iso);
|
||||
qmp_discard_response("{'execute': 'x-blockdev-insert-medium',"
|
||||
"'arguments': { 'device': 'drive0', "
|
||||
"'node-name': 'node0' }}");
|
||||
|
||||
/* Again, the event shows up first */
|
||||
qmp_async("{'execute': 'blockdev-close-tray', "
|
||||
"'arguments': {'device': 'drive0'}}");
|
||||
atapi_wait_tray(false);
|
||||
rsp = qmp_receive();
|
||||
QDECREF(rsp);
|
||||
|
||||
/* Now, to convince ATAPI we understand the media has changed... */
|
||||
ahci_atapi_test_ready(ahci, port, false, SENSE_NOT_READY);
|
||||
ahci_atapi_get_sense(ahci, port, &sense, &asc);
|
||||
g_assert_cmpuint(sense, ==, SENSE_NOT_READY);
|
||||
g_assert_cmpuint(asc, ==, ASC_MEDIUM_NOT_PRESENT);
|
||||
|
||||
ahci_atapi_test_ready(ahci, port, false, SENSE_UNIT_ATTENTION);
|
||||
ahci_atapi_get_sense(ahci, port, &sense, &asc);
|
||||
g_assert_cmpuint(sense, ==, SENSE_UNIT_ATTENTION);
|
||||
g_assert_cmpuint(asc, ==, ASC_MEDIUM_MAY_HAVE_CHANGED);
|
||||
|
||||
ahci_atapi_test_ready(ahci, port, true, SENSE_NO_SENSE);
|
||||
ahci_atapi_get_sense(ahci, port, &sense, &asc);
|
||||
g_assert_cmpuint(sense, ==, SENSE_NO_SENSE);
|
||||
|
||||
/* Final tray test. */
|
||||
ahci_atapi_eject(ahci, port);
|
||||
atapi_wait_tray(true);
|
||||
|
||||
ahci_atapi_load(ahci, port);
|
||||
atapi_wait_tray(false);
|
||||
|
||||
/* Cleanup */
|
||||
g_free(tx);
|
||||
ahci_shutdown(ahci);
|
||||
remove_iso(fd, iso);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -1822,6 +1940,9 @@ int main(int argc, char **argv)
|
|||
qtest_add_func("/ahci/cdrom/pio/single", test_cdrom_pio);
|
||||
qtest_add_func("/ahci/cdrom/pio/multi", test_cdrom_pio_multi);
|
||||
|
||||
qtest_add_func("/ahci/cdrom/pio/bcl", test_atapi_bcl);
|
||||
qtest_add_func("/ahci/cdrom/eject", test_atapi_tray);
|
||||
|
||||
ret = g_test_run();
|
||||
|
||||
/* Cleanup */
|
||||
|
|
|
@ -86,6 +86,7 @@ struct AHCICommand {
|
|||
uint8_t name;
|
||||
uint8_t port;
|
||||
uint8_t slot;
|
||||
uint8_t errors;
|
||||
uint32_t interrupts;
|
||||
uint64_t xbytes;
|
||||
uint32_t prd_size;
|
||||
|
@ -402,12 +403,14 @@ void ahci_port_clear(AHCIQState *ahci, uint8_t port)
|
|||
/**
|
||||
* Check a port for errors.
|
||||
*/
|
||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port)
|
||||
void ahci_port_check_error(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t imask, uint8_t emask)
|
||||
{
|
||||
uint32_t reg;
|
||||
|
||||
/* The upper 9 bits of the IS register all indicate errors. */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_IS);
|
||||
reg &= ~imask;
|
||||
reg >>= 23;
|
||||
g_assert_cmphex(reg, ==, 0);
|
||||
|
||||
|
@ -417,8 +420,13 @@ void ahci_port_check_error(AHCIQState *ahci, uint8_t port)
|
|||
|
||||
/* The TFD also has two error sections. */
|
||||
reg = ahci_px_rreg(ahci, port, AHCI_PX_TFD);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR);
|
||||
if (!emask) {
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_STS_ERR);
|
||||
} else {
|
||||
ASSERT_BIT_SET(reg, AHCI_PX_TFD_STS_ERR);
|
||||
}
|
||||
ASSERT_BIT_CLEAR(reg, AHCI_PX_TFD_ERR & (~emask << 8));
|
||||
ASSERT_BIT_SET(reg, AHCI_PX_TFD_ERR & (emask << 8));
|
||||
}
|
||||
|
||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
||||
|
@ -633,7 +641,8 @@ void ahci_exec(AHCIQState *ahci, uint8_t port,
|
|||
|
||||
/* Command creation */
|
||||
if (opts->atapi) {
|
||||
cmd = ahci_atapi_command_create(op);
|
||||
uint16_t bcl = opts->set_bcl ? opts->bcl : ATAPI_SECTOR_SIZE;
|
||||
cmd = ahci_atapi_command_create(op, bcl);
|
||||
if (opts->atapi_dma) {
|
||||
ahci_command_enable_atapi_dma(cmd);
|
||||
}
|
||||
|
@ -864,19 +873,82 @@ AHCICommand *ahci_command_create(uint8_t command_name)
|
|||
return cmd;
|
||||
}
|
||||
|
||||
AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd)
|
||||
AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl)
|
||||
{
|
||||
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;
|
||||
|
||||
stw_le_p(&cmd->fis.lba_lo[1], bcl);
|
||||
return cmd;
|
||||
}
|
||||
|
||||
void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port,
|
||||
bool ready, uint8_t expected_sense)
|
||||
{
|
||||
AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_TEST_UNIT_READY, 0);
|
||||
ahci_command_set_size(cmd, 0);
|
||||
if (!ready) {
|
||||
cmd->interrupts |= AHCI_PX_IS_TFES;
|
||||
cmd->errors |= expected_sense << 4;
|
||||
}
|
||||
ahci_command_commit(ahci, cmd, port);
|
||||
ahci_command_issue(ahci, cmd);
|
||||
ahci_command_verify(ahci, cmd);
|
||||
ahci_command_free(cmd);
|
||||
}
|
||||
|
||||
static int copy_buffer(AHCIQState *ahci, AHCICommand *cmd,
|
||||
const AHCIOpts *opts)
|
||||
{
|
||||
unsigned char *rx = opts->opaque;
|
||||
bufread(opts->buffer, rx, opts->size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t *sense, uint8_t *asc)
|
||||
{
|
||||
unsigned char *rx;
|
||||
AHCIOpts opts = {
|
||||
.size = 18,
|
||||
.atapi = true,
|
||||
.post_cb = copy_buffer,
|
||||
};
|
||||
rx = g_malloc(18);
|
||||
opts.opaque = rx;
|
||||
|
||||
ahci_exec(ahci, port, CMD_ATAPI_REQUEST_SENSE, &opts);
|
||||
|
||||
*sense = rx[2];
|
||||
*asc = rx[12];
|
||||
|
||||
g_free(rx);
|
||||
}
|
||||
|
||||
void ahci_atapi_eject(AHCIQState *ahci, uint8_t port)
|
||||
{
|
||||
AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0);
|
||||
ahci_command_set_size(cmd, 0);
|
||||
|
||||
cmd->atapi_cmd[4] = 0x02; /* loej = true */
|
||||
ahci_command_commit(ahci, cmd, port);
|
||||
ahci_command_issue(ahci, cmd);
|
||||
ahci_command_verify(ahci, cmd);
|
||||
ahci_command_free(cmd);
|
||||
}
|
||||
|
||||
void ahci_atapi_load(AHCIQState *ahci, uint8_t port)
|
||||
{
|
||||
AHCICommand *cmd = ahci_atapi_command_create(CMD_ATAPI_START_STOP_UNIT, 0);
|
||||
ahci_command_set_size(cmd, 0);
|
||||
|
||||
cmd->atapi_cmd[4] = 0x03; /* loej,start = true */
|
||||
ahci_command_commit(ahci, cmd, port);
|
||||
ahci_command_issue(ahci, cmd);
|
||||
ahci_command_verify(ahci, cmd);
|
||||
ahci_command_free(cmd);
|
||||
}
|
||||
|
||||
void ahci_command_free(AHCICommand *cmd)
|
||||
{
|
||||
g_free(cmd->atapi_cmd);
|
||||
|
@ -901,12 +973,22 @@ static void ahci_atapi_command_set_offset(AHCICommand *cmd, uint64_t lba)
|
|||
|
||||
switch (cbd[0]) {
|
||||
case CMD_ATAPI_READ_10:
|
||||
case CMD_ATAPI_READ_CD:
|
||||
g_assert_cmpuint(lba, <=, UINT32_MAX);
|
||||
stl_be_p(&cbd[2], lba);
|
||||
break;
|
||||
case CMD_ATAPI_REQUEST_SENSE:
|
||||
case CMD_ATAPI_TEST_UNIT_READY:
|
||||
case CMD_ATAPI_START_STOP_UNIT:
|
||||
g_assert_cmpuint(lba, ==, 0x00);
|
||||
break;
|
||||
default:
|
||||
/* SCSI doesn't have uniform packet formats,
|
||||
* so you have to add support for it manually. Sorry! */
|
||||
fprintf(stderr, "The Libqos AHCI driver does not support the "
|
||||
"set_offset operation for ATAPI command 0x%02x, "
|
||||
"please add support.\n",
|
||||
cbd[0]);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
@ -951,6 +1033,7 @@ static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
|
|||
{
|
||||
unsigned char *cbd = cmd->atapi_cmd;
|
||||
uint64_t nsectors = xbytes / 2048;
|
||||
uint32_t tmp;
|
||||
g_assert(cbd);
|
||||
|
||||
switch (cbd[0]) {
|
||||
|
@ -958,9 +1041,28 @@ static void ahci_atapi_set_size(AHCICommand *cmd, uint64_t xbytes)
|
|||
g_assert_cmpuint(nsectors, <=, UINT16_MAX);
|
||||
stw_be_p(&cbd[7], nsectors);
|
||||
break;
|
||||
case CMD_ATAPI_READ_CD:
|
||||
/* 24bit BE store */
|
||||
g_assert_cmpuint(nsectors, <, 1ULL << 24);
|
||||
tmp = nsectors;
|
||||
cbd[6] = (tmp & 0xFF0000) >> 16;
|
||||
cbd[7] = (tmp & 0xFF00) >> 8;
|
||||
cbd[8] = (tmp & 0xFF);
|
||||
break;
|
||||
case CMD_ATAPI_REQUEST_SENSE:
|
||||
g_assert_cmpuint(xbytes, <=, UINT8_MAX);
|
||||
cbd[4] = (uint8_t)xbytes;
|
||||
break;
|
||||
case CMD_ATAPI_TEST_UNIT_READY:
|
||||
case CMD_ATAPI_START_STOP_UNIT:
|
||||
g_assert_cmpuint(xbytes, ==, 0);
|
||||
break;
|
||||
default:
|
||||
/* SCSI doesn't have uniform packet formats,
|
||||
* so you have to add support for it manually. Sorry! */
|
||||
fprintf(stderr, "The Libqos AHCI driver does not support the set_size "
|
||||
"operation for ATAPI command 0x%02x, please add support.\n",
|
||||
cbd[0]);
|
||||
g_assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
@ -1105,7 +1207,7 @@ void ahci_command_verify(AHCIQState *ahci, AHCICommand *cmd)
|
|||
uint8_t slot = cmd->slot;
|
||||
uint8_t port = cmd->port;
|
||||
|
||||
ahci_port_check_error(ahci, port);
|
||||
ahci_port_check_error(ahci, port, cmd->interrupts, cmd->errors);
|
||||
ahci_port_check_interrupts(ahci, port, cmd->interrupts);
|
||||
ahci_port_check_nonbusy(ahci, port, slot);
|
||||
ahci_port_check_cmd_sanity(ahci, cmd);
|
||||
|
|
|
@ -287,7 +287,22 @@ enum {
|
|||
|
||||
/* ATAPI Commands */
|
||||
enum {
|
||||
CMD_ATAPI_READ_10 = 0x28,
|
||||
CMD_ATAPI_TEST_UNIT_READY = 0x00,
|
||||
CMD_ATAPI_REQUEST_SENSE = 0x03,
|
||||
CMD_ATAPI_START_STOP_UNIT = 0x1b,
|
||||
CMD_ATAPI_READ_10 = 0x28,
|
||||
CMD_ATAPI_READ_CD = 0xbe,
|
||||
};
|
||||
|
||||
enum {
|
||||
SENSE_NO_SENSE = 0x00,
|
||||
SENSE_NOT_READY = 0x02,
|
||||
SENSE_UNIT_ATTENTION = 0x06,
|
||||
};
|
||||
|
||||
enum {
|
||||
ASC_MEDIUM_MAY_HAVE_CHANGED = 0x28,
|
||||
ASC_MEDIUM_NOT_PRESENT = 0x3a,
|
||||
};
|
||||
|
||||
/* AHCI Command Header Flags & Masks*/
|
||||
|
@ -462,12 +477,14 @@ 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;
|
||||
size_t size; /* Size of transfer */
|
||||
unsigned prd_size; /* Size per-each PRD */
|
||||
bool set_bcl; /* Override the default BCL of ATAPI_SECTOR_SIZE */
|
||||
unsigned bcl; /* Byte Count Limit, for ATAPI PIO */
|
||||
uint64_t lba; /* Starting LBA offset */
|
||||
uint64_t buffer; /* Pointer to source or destination guest buffer */
|
||||
bool atapi; /* ATAPI command? */
|
||||
bool atapi_dma; /* Use DMA for ATAPI? */
|
||||
bool error;
|
||||
int (*pre_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
|
||||
int (*mid_cb)(AHCIQState*, AHCICommand*, const struct AHCIOpts *);
|
||||
|
@ -573,7 +590,8 @@ void ahci_set_command_header(AHCIQState *ahci, uint8_t port,
|
|||
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,
|
||||
uint32_t imask, uint8_t emask);
|
||||
void ahci_port_check_interrupts(AHCIQState *ahci, uint8_t port,
|
||||
uint32_t intr_mask);
|
||||
void ahci_port_check_nonbusy(AHCIQState *ahci, uint8_t port, uint8_t slot);
|
||||
|
@ -596,10 +614,16 @@ void ahci_io(AHCIQState *ahci, uint8_t port, uint8_t ide_cmd,
|
|||
void *buffer, size_t bufsize, uint64_t sector);
|
||||
void ahci_exec(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t op, const AHCIOpts *opts);
|
||||
void ahci_atapi_test_ready(AHCIQState *ahci, uint8_t port, bool ready,
|
||||
uint8_t expected_sense);
|
||||
void ahci_atapi_get_sense(AHCIQState *ahci, uint8_t port,
|
||||
uint8_t *sense, uint8_t *asc);
|
||||
void ahci_atapi_eject(AHCIQState *ahci, uint8_t port);
|
||||
void ahci_atapi_load(AHCIQState *ahci, uint8_t port);
|
||||
|
||||
/* Command: Fine-grained lifecycle */
|
||||
AHCICommand *ahci_command_create(uint8_t command_name);
|
||||
AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd);
|
||||
AHCICommand *ahci_atapi_command_create(uint8_t scsi_cmd, uint16_t bcl);
|
||||
void ahci_command_commit(AHCIQState *ahci, AHCICommand *cmd, uint8_t port);
|
||||
void ahci_command_issue(AHCIQState *ahci, AHCICommand *cmd);
|
||||
void ahci_command_issue_async(AHCIQState *ahci, AHCICommand *cmd);
|
||||
|
|
|
@ -533,7 +533,7 @@ void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...)
|
|||
QDECREF(response);
|
||||
}
|
||||
|
||||
void qtest_qmp_eventwait(QTestState *s, const char *event)
|
||||
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event)
|
||||
{
|
||||
QDict *response;
|
||||
|
||||
|
@ -541,13 +541,20 @@ void qtest_qmp_eventwait(QTestState *s, const char *event)
|
|||
response = qtest_qmp_receive(s);
|
||||
if ((qdict_haskey(response, "event")) &&
|
||||
(strcmp(qdict_get_str(response, "event"), event) == 0)) {
|
||||
QDECREF(response);
|
||||
break;
|
||||
return response;
|
||||
}
|
||||
QDECREF(response);
|
||||
}
|
||||
}
|
||||
|
||||
void qtest_qmp_eventwait(QTestState *s, const char *event)
|
||||
{
|
||||
QDict *response;
|
||||
|
||||
response = qtest_qmp_eventwait_ref(s, event);
|
||||
QDECREF(response);
|
||||
}
|
||||
|
||||
char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap)
|
||||
{
|
||||
char *cmd;
|
||||
|
|
|
@ -113,6 +113,16 @@ QDict *qtest_qmp_receive(QTestState *s);
|
|||
*/
|
||||
void qtest_qmp_eventwait(QTestState *s, const char *event);
|
||||
|
||||
/**
|
||||
* qtest_qmp_eventwait_ref:
|
||||
* @s: #QTestState instance to operate on.
|
||||
* @s: #event event to wait for.
|
||||
*
|
||||
* Continuosly polls for QMP responses until it receives the desired event.
|
||||
* Returns a copy of the event for further investigation.
|
||||
*/
|
||||
QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event);
|
||||
|
||||
/**
|
||||
* qtest_hmpv:
|
||||
* @s: #QTestState instance to operate on.
|
||||
|
@ -558,6 +568,18 @@ static inline void qmp_eventwait(const char *event)
|
|||
return qtest_qmp_eventwait(global_qtest, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* qmp_eventwait_ref:
|
||||
* @s: #event event to wait for.
|
||||
*
|
||||
* Continuosly polls for QMP responses until it receives the desired event.
|
||||
* Returns a copy of the event for further investigation.
|
||||
*/
|
||||
static inline QDict *qmp_eventwait_ref(const char *event)
|
||||
{
|
||||
return qtest_qmp_eventwait_ref(global_qtest, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* hmp:
|
||||
* @fmt...: HMP command to send to QEMU
|
||||
|
|
Loading…
Reference in New Issue