diff options
-rw-r--r-- | block.c | 20 | ||||
-rw-r--r-- | block/block-backend.c | 13 | ||||
-rw-r--r-- | block/nbd.c | 1 | ||||
-rw-r--r-- | block/nfs.c | 1 | ||||
-rw-r--r-- | block/qcow2.h | 2 | ||||
-rw-r--r-- | block/raw-posix.c | 56 | ||||
-rw-r--r-- | block/raw_bsd.c | 12 | ||||
-rw-r--r-- | block/ssh.c | 5 | ||||
-rw-r--r-- | hmp.c | 2 | ||||
-rw-r--r-- | hw/ide/atapi.c | 51 | ||||
-rw-r--r-- | tests/ahci-test.c | 137 | ||||
-rw-r--r-- | tests/libqos/ahci.c | 124 | ||||
-rw-r--r-- | tests/libqos/ahci.h | 42 | ||||
-rw-r--r-- | tests/libqtest.c | 13 | ||||
-rw-r--r-- | tests/libqtest.h | 22 | ||||
-rwxr-xr-x | tests/qemu-iotests/162 | 3 | ||||
-rw-r--r-- | tests/qemu-iotests/common | 2 | ||||
-rw-r--r-- | tests/qemu-iotests/common.qemu | 12 | ||||
-rw-r--r-- | tests/test-uuid.c | 1 |
19 files changed, 426 insertions, 93 deletions
@@ -2796,7 +2796,7 @@ const char *bdrv_get_format_name(BlockDriverState *bs) static int qsort_strcmp(const void *a, const void *b) { - return strcmp(a, b); + return strcmp(*(char *const *)a, *(char *const *)b); } void bdrv_iterate_format(void (*it)(void *opaque, const char *name), @@ -2822,6 +2822,24 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), } } + for (i = 0; i < (int)ARRAY_SIZE(block_driver_modules); i++) { + const char *format_name = block_driver_modules[i].format_name; + + if (format_name) { + bool found = false; + int j = count; + + while (formats && j && !found) { + found = !strcmp(formats[--j], format_name); + } + + if (!found) { + formats = g_renew(const char *, formats, count + 1); + formats[count++] = format_name; + } + } + } + qsort(formats, count, sizeof(formats[0]), qsort_strcmp); for (i = 0; i < count; i++) { diff --git a/block/block-backend.c b/block/block-backend.c index 27a7f6f523..efbf398bb5 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -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) diff --git a/block/nbd.c b/block/nbd.c index 9cff8396f9..35f24be069 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -536,6 +536,7 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) ov = qobject_output_visitor_new(&saddr_qdict); visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); visit_complete(ov, &saddr_qdict); + visit_free(ov); assert(qobject_type(saddr_qdict) == QTYPE_QDICT); qdict_put_obj(opts, "server", saddr_qdict); diff --git a/block/nfs.c b/block/nfs.c index 55c4e0b073..d08278323f 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -676,6 +676,7 @@ static int nfs_file_create(const char *url, QemuOpts *opts, Error **errp) ret = nfs_ftruncate(client->context, client->fh, total_size); nfs_client_close(client); out: + QDECREF(options); g_free(client); return ret; } diff --git a/block/qcow2.h b/block/qcow2.h index 92203a8b8c..182341483a 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -473,8 +473,6 @@ static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) return r1 > r2 ? r1 - r2 : r2 - r1; } -// FIXME Need qcow2_ prefix to global functions - /* qcow2.c functions */ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, int64_t sector_num, int nb_sectors); diff --git a/block/raw-posix.c b/block/raw-posix.c index 247e47b88f..28b47d977b 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -542,7 +542,7 @@ static int raw_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { BDRVRawState *s; - BDRVRawReopenState *raw_s; + BDRVRawReopenState *rs; int ret = 0; Error *local_err = NULL; @@ -552,15 +552,15 @@ static int raw_reopen_prepare(BDRVReopenState *state, s = state->bs->opaque; state->opaque = g_new0(BDRVRawReopenState, 1); - raw_s = state->opaque; + rs = state->opaque; if (s->type == FTYPE_CD) { - raw_s->open_flags |= O_NONBLOCK; + rs->open_flags |= O_NONBLOCK; } - raw_parse_flags(state->flags, &raw_s->open_flags); + raw_parse_flags(state->flags, &rs->open_flags); - raw_s->fd = -1; + rs->fd = -1; int fcntl_flags = O_APPEND | O_NONBLOCK; #ifdef O_NOATIME @@ -569,35 +569,35 @@ static int raw_reopen_prepare(BDRVReopenState *state, #ifdef O_ASYNC /* Not all operating systems have O_ASYNC, and those that don't - * will not let us track the state into raw_s->open_flags (typically + * will not let us track the state into rs->open_flags (typically * you achieve the same effect with an ioctl, for example I_SETSIG * on Solaris). But we do not use O_ASYNC, so that's fine. */ assert((s->open_flags & O_ASYNC) == 0); #endif - if ((raw_s->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { + if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { /* dup the original fd */ - raw_s->fd = qemu_dup(s->fd); - if (raw_s->fd >= 0) { - ret = fcntl_setfl(raw_s->fd, raw_s->open_flags); + rs->fd = qemu_dup(s->fd); + if (rs->fd >= 0) { + ret = fcntl_setfl(rs->fd, rs->open_flags); if (ret) { - qemu_close(raw_s->fd); - raw_s->fd = -1; + qemu_close(rs->fd); + rs->fd = -1; } } } /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ - if (raw_s->fd == -1) { + if (rs->fd == -1) { const char *normalized_filename = state->bs->filename; ret = raw_normalize_devicepath(&normalized_filename); if (ret < 0) { error_setg_errno(errp, -ret, "Could not normalize device path"); } else { - assert(!(raw_s->open_flags & O_CREAT)); - raw_s->fd = qemu_open(normalized_filename, raw_s->open_flags); - if (raw_s->fd == -1) { + assert(!(rs->open_flags & O_CREAT)); + rs->fd = qemu_open(normalized_filename, rs->open_flags); + if (rs->fd == -1) { error_setg_errno(errp, errno, "Could not reopen file"); ret = -1; } @@ -606,11 +606,11 @@ static int raw_reopen_prepare(BDRVReopenState *state, /* Fail already reopen_prepare() if we can't get a working O_DIRECT * alignment with the new fd. */ - if (raw_s->fd != -1) { - raw_probe_alignment(state->bs, raw_s->fd, &local_err); + if (rs->fd != -1) { + raw_probe_alignment(state->bs, rs->fd, &local_err); if (local_err) { - qemu_close(raw_s->fd); - raw_s->fd = -1; + qemu_close(rs->fd); + rs->fd = -1; error_propagate(errp, local_err); ret = -EINVAL; } @@ -621,13 +621,13 @@ static int raw_reopen_prepare(BDRVReopenState *state, static void raw_reopen_commit(BDRVReopenState *state) { - BDRVRawReopenState *raw_s = state->opaque; + BDRVRawReopenState *rs = state->opaque; BDRVRawState *s = state->bs->opaque; - s->open_flags = raw_s->open_flags; + s->open_flags = rs->open_flags; qemu_close(s->fd); - s->fd = raw_s->fd; + s->fd = rs->fd; g_free(state->opaque); state->opaque = NULL; @@ -636,16 +636,16 @@ static void raw_reopen_commit(BDRVReopenState *state) static void raw_reopen_abort(BDRVReopenState *state) { - BDRVRawReopenState *raw_s = state->opaque; + BDRVRawReopenState *rs = state->opaque; /* nothing to do if NULL, we didn't get far enough */ - if (raw_s == NULL) { + if (rs == NULL) { return; } - if (raw_s->fd >= 0) { - qemu_close(raw_s->fd); - raw_s->fd = -1; + if (rs->fd >= 0) { + qemu_close(rs->fd); + rs->fd = -1; } g_free(state->opaque); state->opaque = NULL; diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 7c9bebb507..8a5b9b0424 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -91,6 +91,14 @@ static int raw_read_options(QDict *options, BlockDriverState *bs, } s->offset = qemu_opt_get_size(opts, "offset", 0); + if (s->offset > real_size) { + error_setg(errp, "Offset (%" PRIu64 ") cannot be greater than " + "size of the containing file (%" PRId64 ")", + s->offset, real_size); + ret = -EINVAL; + goto end; + } + if (qemu_opt_find(opts, "size") != NULL) { s->size = qemu_opt_get_size(opts, "size", 0); s->has_size = true; @@ -100,7 +108,7 @@ static int raw_read_options(QDict *options, BlockDriverState *bs, } /* Check size and offset */ - if (real_size < s->offset || (real_size - s->offset) < s->size) { + if ((real_size - s->offset) < s->size) { error_setg(errp, "The sum of offset (%" PRIu64 ") and size " "(%" PRIu64 ") has to be smaller or equal to the " " actual size of the containing file (%" PRId64 ")", @@ -111,7 +119,7 @@ static int raw_read_options(QDict *options, BlockDriverState *bs, /* Make sure size is multiple of BDRV_SECTOR_SIZE to prevent rounding * up and leaking out of the specified area. */ - if (!QEMU_IS_ALIGNED(s->size, BDRV_SECTOR_SIZE)) { + if (s->has_size && !QEMU_IS_ALIGNED(s->size, BDRV_SECTOR_SIZE)) { error_setg(errp, "Specified size is not multiple of %llu", BDRV_SECTOR_SIZE); ret = -EINVAL; diff --git a/block/ssh.c b/block/ssh.c index ca071c569c..15ed2818c5 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -582,8 +582,7 @@ static bool ssh_process_legacy_socket_options(QDict *output_opts, return true; } -static InetSocketAddress *ssh_config(BDRVSSHState *s, QDict *options, - Error **errp) +static InetSocketAddress *ssh_config(QDict *options, Error **errp) { InetSocketAddress *inet = NULL; QDict *addr = NULL; @@ -661,7 +660,7 @@ static int connect_to_ssh(BDRVSSHState *s, QDict *options, } /* Pop the config into our state object, Exit if invalid */ - s->inet = ssh_config(s, options, errp); + s->inet = ssh_config(options, errp); if (!s->inet) { ret = -EINVAL; goto err; @@ -1571,7 +1571,7 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) const char *base = qdict_get_try_str(qdict, "base"); int64_t speed = qdict_get_try_int(qdict, "speed", 0); - qmp_block_stream(false, NULL, device, base != NULL, base, false, NULL, + qmp_block_stream(true, device, device, base != NULL, base, false, NULL, false, NULL, qdict_haskey(qdict, "speed"), speed, true, BLOCKDEV_ON_ERROR_REPORT, &error); diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 6189675036..fc1d19c6d4 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -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; } } diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 70bcafa9e4..ef17629345 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -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 */ diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 5180d65279..1ca7f456b5 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -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); diff --git a/tests/libqos/ahci.h b/tests/libqos/ahci.h index caaafe3fdf..5f9627bb0f 100644 --- a/tests/libqos/ahci.h +++ b/tests/libqos/ahci.h @@ -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); diff --git a/tests/libqtest.c b/tests/libqtest.c index d4e6bff121..6f6975248f 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -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; diff --git a/tests/libqtest.h b/tests/libqtest.h index 0224f06d65..90f182e1d8 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -114,6 +114,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. * @fmt...: HMP command to send to QEMU @@ -559,6 +569,18 @@ static inline void qmp_eventwait(const char *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 * diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index f8eecb325b..cad2bd70ab 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -35,6 +35,9 @@ status=1 # failure is the default! _supported_fmt generic _supported_os Linux +test_ssh=$($QEMU_IMG --help | grep '^Supported formats:.* ssh\( \|$\)') +[ "$test_ssh" = "" ] && _notrun "ssh support required" + echo echo '=== NBD ===' # NBD expects all of its arguments to be strings diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index d60ea2ce3c..b6274bee0a 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -51,7 +51,7 @@ export IMGOPTS="" export CACHEMODE="writeback" export QEMU_IO_OPTIONS="" export CACHEMODE_IS_DEFAULT=true -export QEMU_OPTIONS="-nodefaults" +export QEMU_OPTIONS="-nodefaults -machine accel=qtest" export VALGRIND_QEMU= export IMGKEYSECRET= export IMGOPTSSYNTAX=false diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu index 2548a8700b..e657361790 100644 --- a/tests/qemu-iotests/common.qemu +++ b/tests/qemu-iotests/common.qemu @@ -155,15 +155,13 @@ function _launch_qemu() if [ -z "$keep_stderr" ]; then QEMU_NEED_PID='y'\ - ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ - >"${fifo_out}" \ - 2>&1 \ - <"${fifo_in}" & + ${QEMU} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \ + 2>&1 \ + <"${fifo_in}" & elif [ "$keep_stderr" = "y" ]; then QEMU_NEED_PID='y'\ - ${QEMU} -nographic -serial none ${comm} -machine accel=qtest "${@}" \ - >"${fifo_out}" \ - <"${fifo_in}" & + ${QEMU} -nographic -serial none ${comm} "${@}" >"${fifo_out}" \ + <"${fifo_in}" & else exit 1 fi diff --git a/tests/test-uuid.c b/tests/test-uuid.c index 77dcdc4b55..d3a2791fd4 100644 --- a/tests/test-uuid.c +++ b/tests/test-uuid.c @@ -161,6 +161,7 @@ static void test_uuid_unparse_strdup(void) } out = qemu_uuid_unparse_strdup(&uuid_test_data[i].uuid); g_assert_cmpstr(uuid_test_data[i].uuidstr, ==, out); + g_free(out); } } |