diff --git a/VERSION b/VERSION index bd8bf882d0..f8a696c8dc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.7.0 +1.7.2 diff --git a/arch_init.c b/arch_init.c index e0acbc5661..80389376e9 100644 --- a/arch_init.c +++ b/arch_init.c @@ -857,64 +857,60 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) { ram_addr_t addr; int flags, ret = 0; - int error; static uint64_t seq_iter; seq_iter++; - if (version_id < 4 || version_id > 4) { + if (version_id != 4) { return -EINVAL; } - do { + while (!ret) { addr = qemu_get_be64(f); flags = addr & ~TARGET_PAGE_MASK; addr &= TARGET_PAGE_MASK; if (flags & RAM_SAVE_FLAG_MEM_SIZE) { - if (version_id == 4) { - /* Synchronize RAM block list */ - char id[256]; - ram_addr_t length; - ram_addr_t total_ram_bytes = addr; + /* Synchronize RAM block list */ + char id[256]; + ram_addr_t length; + ram_addr_t total_ram_bytes = addr; - while (total_ram_bytes) { - RAMBlock *block; - uint8_t len; + while (total_ram_bytes) { + RAMBlock *block; + uint8_t len; - len = qemu_get_byte(f); - qemu_get_buffer(f, (uint8_t *)id, len); - id[len] = 0; - length = qemu_get_be64(f); + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)id, len); + id[len] = 0; + length = qemu_get_be64(f); - QTAILQ_FOREACH(block, &ram_list.blocks, next) { - if (!strncmp(id, block->idstr, sizeof(id))) { - if (block->length != length) { - fprintf(stderr, - "Length mismatch: %s: " RAM_ADDR_FMT - " in != " RAM_ADDR_FMT "\n", id, length, - block->length); - ret = -EINVAL; - goto done; - } - break; + QTAILQ_FOREACH(block, &ram_list.blocks, next) { + if (!strncmp(id, block->idstr, sizeof(id))) { + if (block->length != length) { + fprintf(stderr, + "Length mismatch: %s: " RAM_ADDR_FMT + " in != " RAM_ADDR_FMT "\n", id, length, + block->length); + ret = -EINVAL; } + break; } - - if (!block) { - fprintf(stderr, "Unknown ramblock \"%s\", cannot " - "accept migration\n", id); - ret = -EINVAL; - goto done; - } - - total_ram_bytes -= length; } - } - } - if (flags & RAM_SAVE_FLAG_COMPRESS) { + if (!block) { + fprintf(stderr, "Unknown ramblock \"%s\", cannot " + "accept migration\n", id); + ret = -EINVAL; + } + if (ret) { + break; + } + + total_ram_bytes -= length; + } + } else if (flags & RAM_SAVE_FLAG_COMPRESS) { void *host; uint8_t ch; @@ -941,20 +937,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) } if (load_xbzrle(f, addr, host) < 0) { + error_report("Failed to decompress XBZRLE page at " + RAM_ADDR_FMT, addr); ret = -EINVAL; - goto done; + break; } } else if (flags & RAM_SAVE_FLAG_HOOK) { ram_control_load_hook(f, flags); + } else if (flags & RAM_SAVE_FLAG_EOS) { + /* normal exit */ + break; + } else { + error_report("Unknown migration flags: %#x", flags); + ret = -EINVAL; + break; } - error = qemu_file_get_error(f); - if (error) { - ret = error; - goto done; - } - } while (!(flags & RAM_SAVE_FLAG_EOS)); + ret = qemu_file_get_error(f); + } -done: DPRINTF("Completed load of VM with exit code %d seq iteration " "%" PRIu64 "\n", ret, seq_iter); return ret; diff --git a/async.c b/async.c index 5fb3fa61df..c6d810d186 100644 --- a/async.c +++ b/async.c @@ -117,15 +117,21 @@ void qemu_bh_schedule_idle(QEMUBH *bh) void qemu_bh_schedule(QEMUBH *bh) { + AioContext *ctx; + if (bh->scheduled) return; + ctx = bh->ctx; bh->idle = 0; - /* Make sure that idle & any writes needed by the callback are done - * before the locations are read in the aio_bh_poll. + /* Make sure that: + * 1. idle & any writes needed by the callback are done before the + * locations are read in the aio_bh_poll. + * 2. ctx is loaded before scheduled is set and the callback has a chance + * to execute. */ - smp_wmb(); + smp_mb(); bh->scheduled = 1; - aio_notify(bh->ctx); + aio_notify(ctx); } diff --git a/block.c b/block.c index 382ea71f4b..202d817382 100644 --- a/block.c +++ b/block.c @@ -966,14 +966,14 @@ fail: */ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) { - char backing_filename[PATH_MAX]; - int back_flags, ret; + char *backing_filename = g_malloc0(PATH_MAX); + int back_flags, ret = 0; BlockDriver *back_drv = NULL; Error *local_err = NULL; if (bs->backing_hd != NULL) { QDECREF(options); - return 0; + goto free_exit; } /* NULL means an empty set of options */ @@ -986,10 +986,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) backing_filename[0] = '\0'; } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { QDECREF(options); - return 0; + goto free_exit; } else { - bdrv_get_full_backing_filename(bs, backing_filename, - sizeof(backing_filename)); + bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX); } bs->backing_hd = bdrv_new(""); @@ -1012,11 +1011,14 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *options, Error **errp) error_setg(errp, "Could not open backing file: %s", error_get_pretty(local_err)); error_free(local_err); - return ret; + goto free_exit; } pstrcpy(bs->backing_file, sizeof(bs->backing_file), bs->backing_hd->file->filename); - return 0; + ret = 0; +free_exit: + g_free(backing_filename); + return ret; } /* @@ -1032,7 +1034,8 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, { int ret; /* TODO: extra byte is a hack to ensure MAX_PATH space on Windows. */ - char tmp_filename[PATH_MAX + 1]; + char *backing_filename = NULL; + char *tmp_filename = g_malloc0(PATH_MAX + 1); BlockDriverState *file = NULL; QDict *file_options = NULL; const char *drvname; @@ -1052,7 +1055,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, int64_t total_size; BlockDriver *bdrv_qcow2; QEMUOptionParameter *create_options; - char backing_filename[PATH_MAX]; + backing_filename = g_malloc0(PATH_MAX); if (qdict_size(options) != 0) { error_setg(errp, "Can't use snapshot=on with driver-specific options"); @@ -1064,9 +1067,9 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* if snapshot, we create a temporary backing file and open it instead of opening 'filename' directly */ - /* if there is a backing file, use it */ bs1 = bdrv_new(""); - ret = bdrv_open(bs1, filename, NULL, 0, drv, &local_err); + ret = bdrv_open(bs1, filename, NULL, BDRV_O_NO_BACKING, drv, + &local_err); if (ret < 0) { bdrv_unref(bs1); goto fail; @@ -1075,7 +1078,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, bdrv_unref(bs1); - ret = get_tmp_filename(tmp_filename, sizeof(tmp_filename)); + ret = get_tmp_filename(tmp_filename, PATH_MAX + 1); if (ret < 0) { error_setg_errno(errp, -ret, "Could not get temporary filename"); goto fail; @@ -1083,8 +1086,7 @@ int bdrv_open(BlockDriverState *bs, const char *filename, QDict *options, /* Real path is meaningless for protocols */ if (path_has_protocol(filename)) { - snprintf(backing_filename, sizeof(backing_filename), - "%s", filename); + snprintf(backing_filename, PATH_MAX, "%s", filename); } else if (!realpath(filename, backing_filename)) { ret = -errno; error_setg_errno(errp, errno, "Could not resolve path '%s'", filename); @@ -1206,6 +1208,8 @@ fail: if (error_is_set(&local_err)) { error_propagate(errp, local_err); } + g_free(tmp_filename); + g_free(backing_filename); return ret; close_and_fail: @@ -1214,6 +1218,8 @@ close_and_fail: if (error_is_set(&local_err)) { error_propagate(errp, local_err); } + g_free(tmp_filename); + g_free(backing_filename); return ret; } @@ -2271,6 +2277,10 @@ static int bdrv_check_byte_request(BlockDriverState *bs, int64_t offset, static int bdrv_check_request(BlockDriverState *bs, int64_t sector_num, int nb_sectors) { + if (nb_sectors > INT_MAX / BDRV_SECTOR_SIZE) { + return -EIO; + } + return bdrv_check_byte_request(bs, sector_num * BDRV_SECTOR_SIZE, nb_sectors * BDRV_SECTOR_SIZE); } diff --git a/block/bochs.c b/block/bochs.c index 51d9a90577..5c74223964 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -38,57 +38,42 @@ // not allocated: 0xffffffff -// always little-endian -struct bochs_header_v1 { - char magic[32]; // "Bochs Virtual HD Image" - char type[16]; // "Redolog" - char subtype[16]; // "Undoable" / "Volatile" / "Growing" - uint32_t version; - uint32_t header; // size of header - - union { - struct { - uint32_t catalog; // num of entries - uint32_t bitmap; // bitmap size - uint32_t extent; // extent size - uint64_t disk; // disk size - char padding[HEADER_SIZE - 64 - 8 - 20]; - } redolog; - char padding[HEADER_SIZE - 64 - 8]; - } extra; -}; - // always little-endian struct bochs_header { - char magic[32]; // "Bochs Virtual HD Image" - char type[16]; // "Redolog" - char subtype[16]; // "Undoable" / "Volatile" / "Growing" + char magic[32]; /* "Bochs Virtual HD Image" */ + char type[16]; /* "Redolog" */ + char subtype[16]; /* "Undoable" / "Volatile" / "Growing" */ uint32_t version; - uint32_t header; // size of header + uint32_t header; /* size of header */ + + uint32_t catalog; /* num of entries */ + uint32_t bitmap; /* bitmap size */ + uint32_t extent; /* extent size */ union { - struct { - uint32_t catalog; // num of entries - uint32_t bitmap; // bitmap size - uint32_t extent; // extent size - uint32_t reserved; // for ??? - uint64_t disk; // disk size - char padding[HEADER_SIZE - 64 - 8 - 24]; - } redolog; - char padding[HEADER_SIZE - 64 - 8]; + struct { + uint32_t reserved; /* for ??? */ + uint64_t disk; /* disk size */ + char padding[HEADER_SIZE - 64 - 20 - 12]; + } QEMU_PACKED redolog; + struct { + uint64_t disk; /* disk size */ + char padding[HEADER_SIZE - 64 - 20 - 8]; + } QEMU_PACKED redolog_v1; + char padding[HEADER_SIZE - 64 - 20]; } extra; -}; +} QEMU_PACKED; typedef struct BDRVBochsState { CoMutex lock; uint32_t *catalog_bitmap; - int catalog_size; + uint32_t catalog_size; - int data_offset; + uint32_t data_offset; - int bitmap_blocks; - int extent_blocks; - int extent_size; + uint32_t bitmap_blocks; + uint32_t extent_blocks; + uint32_t extent_size; } BDRVBochsState; static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) @@ -112,9 +97,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVBochsState *s = bs->opaque; - int i; + uint32_t i; struct bochs_header bochs; - struct bochs_header_v1 header_v1; int ret; bs->read_only = 1; // no write support yet @@ -133,13 +117,19 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, } if (le32_to_cpu(bochs.version) == HEADER_V1) { - memcpy(&header_v1, &bochs, sizeof(bochs)); - bs->total_sectors = le64_to_cpu(header_v1.extra.redolog.disk) / 512; + bs->total_sectors = le64_to_cpu(bochs.extra.redolog_v1.disk) / 512; } else { - bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; + bs->total_sectors = le64_to_cpu(bochs.extra.redolog.disk) / 512; + } + + /* Limit to 1M entries to avoid unbounded allocation. This is what is + * needed for the largest image that bximage can create (~8 TB). */ + s->catalog_size = le32_to_cpu(bochs.catalog); + if (s->catalog_size > 0x100000) { + error_setg(errp, "Catalog size is too large"); + return -EFBIG; } - s->catalog_size = le32_to_cpu(bochs.extra.redolog.catalog); s->catalog_bitmap = g_malloc(s->catalog_size * 4); ret = bdrv_pread(bs->file, le32_to_cpu(bochs.header), s->catalog_bitmap, @@ -153,10 +143,24 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); - s->bitmap_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.bitmap) - 1) / 512; - s->extent_blocks = 1 + (le32_to_cpu(bochs.extra.redolog.extent) - 1) / 512; + s->bitmap_blocks = 1 + (le32_to_cpu(bochs.bitmap) - 1) / 512; + s->extent_blocks = 1 + (le32_to_cpu(bochs.extent) - 1) / 512; - s->extent_size = le32_to_cpu(bochs.extra.redolog.extent); + s->extent_size = le32_to_cpu(bochs.extent); + if (s->extent_size == 0) { + error_setg(errp, "Extent size may not be zero"); + return -EINVAL; + } else if (s->extent_size > 0x800000) { + error_setg(errp, "Extent size %" PRIu32 " is too large", + s->extent_size); + return -EINVAL; + } + + if (s->catalog_size < bs->total_sectors / s->extent_size) { + error_setg(errp, "Catalog size is too small for this disk size"); + ret = -EINVAL; + goto fail; + } qemu_co_mutex_init(&s->lock); return 0; @@ -169,8 +173,8 @@ fail: static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) { BDRVBochsState *s = bs->opaque; - int64_t offset = sector_num * 512; - int64_t extent_index, extent_offset, bitmap_offset; + uint64_t offset = sector_num * 512; + uint64_t extent_index, extent_offset, bitmap_offset; char bitmap_entry; // seek to sector @@ -181,8 +185,9 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) return -1; /* not allocated */ } - bitmap_offset = s->data_offset + (512 * s->catalog_bitmap[extent_index] * - (s->extent_blocks + s->bitmap_blocks)); + bitmap_offset = s->data_offset + + (512 * (uint64_t) s->catalog_bitmap[extent_index] * + (s->extent_blocks + s->bitmap_blocks)); /* read in bitmap for current extent */ if (bdrv_pread(bs->file, bitmap_offset + (extent_offset / 8), diff --git a/block/cloop.c b/block/cloop.c index b907023e10..b6ad50fbb4 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -26,6 +26,9 @@ #include "qemu/module.h" #include +/* Maximum compressed block size */ +#define MAX_BLOCK_SIZE (64 * 1024 * 1024) + typedef struct BDRVCloopState { CoMutex lock; uint32_t block_size; @@ -68,6 +71,26 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, return ret; } s->block_size = be32_to_cpu(s->block_size); + if (s->block_size % 512) { + error_setg(errp, "block_size %u must be a multiple of 512", + s->block_size); + return -EINVAL; + } + if (s->block_size == 0) { + error_setg(errp, "block_size cannot be zero"); + return -EINVAL; + } + + /* cloop's create_compressed_fs.c warns about block sizes beyond 256 KB but + * we can accept more. Prevent ridiculous values like 4 GB - 1 since we + * need a buffer this big. + */ + if (s->block_size > MAX_BLOCK_SIZE) { + error_setg(errp, "block_size %u must be %u MB or less", + s->block_size, + MAX_BLOCK_SIZE / (1024 * 1024)); + return -EINVAL; + } ret = bdrv_pread(bs->file, 128 + 4, &s->n_blocks, 4); if (ret < 0) { @@ -76,7 +99,23 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, s->n_blocks = be32_to_cpu(s->n_blocks); /* read offsets */ - offsets_size = s->n_blocks * sizeof(uint64_t); + if (s->n_blocks > (UINT32_MAX - 1) / sizeof(uint64_t)) { + /* Prevent integer overflow */ + error_setg(errp, "n_blocks %u must be %zu or less", + s->n_blocks, + (UINT32_MAX - 1) / sizeof(uint64_t)); + return -EINVAL; + } + offsets_size = (s->n_blocks + 1) * sizeof(uint64_t); + if (offsets_size > 512 * 1024 * 1024) { + /* Prevent ridiculous offsets_size which causes memory allocation to + * fail or overflows bdrv_pread() size. In practice the 512 MB + * offsets[] limit supports 16 TB images at 256 KB block size. + */ + error_setg(errp, "image requires too many offsets, " + "try increasing block size"); + return -EINVAL; + } s->offsets = g_malloc(offsets_size); ret = bdrv_pread(bs->file, 128 + 4 + 4, s->offsets, offsets_size); @@ -84,13 +123,37 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - for(i=0;in_blocks;i++) { + for (i = 0; i < s->n_blocks + 1; i++) { + uint64_t size; + s->offsets[i] = be64_to_cpu(s->offsets[i]); - if (i > 0) { - uint32_t size = s->offsets[i] - s->offsets[i - 1]; - if (size > max_compressed_block_size) { - max_compressed_block_size = size; - } + if (i == 0) { + continue; + } + + if (s->offsets[i] < s->offsets[i - 1]) { + error_setg(errp, "offsets not monotonically increasing at " + "index %u, image file is corrupt", i); + ret = -EINVAL; + goto fail; + } + + size = s->offsets[i] - s->offsets[i - 1]; + + /* Compressed blocks should be smaller than the uncompressed block size + * but maybe compression performed poorly so the compressed block is + * actually bigger. Clamp down on unrealistic values to prevent + * ridiculous s->compressed_block allocation. + */ + if (size > 2 * MAX_BLOCK_SIZE) { + error_setg(errp, "invalid compressed block size at index %u, " + "image file is corrupt", i); + ret = -EINVAL; + goto fail; + } + + if (size > max_compressed_block_size) { + max_compressed_block_size = size; } } @@ -180,9 +243,7 @@ static coroutine_fn int cloop_co_read(BlockDriverState *bs, int64_t sector_num, static void cloop_close(BlockDriverState *bs) { BDRVCloopState *s = bs->opaque; - if (s->n_blocks > 0) { - g_free(s->offsets); - } + g_free(s->offsets); g_free(s->compressed_block); g_free(s->uncompressed_block); inflateEnd(&s->zstream); diff --git a/block/curl.c b/block/curl.c index 5a46f9707c..47cf70a159 100644 --- a/block/curl.c +++ b/block/curl.c @@ -34,6 +34,11 @@ #define DPRINTF(fmt, ...) do { } while (0) #endif +#if LIBCURL_VERSION_NUM >= 0x071000 +/* The multi interface timer callback was introduced in 7.16.0 */ +#define NEED_CURL_TIMER_CALLBACK +#endif + #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ CURLPROTO_FTP | CURLPROTO_FTPS | \ CURLPROTO_TFTP) @@ -77,6 +82,7 @@ typedef struct CURLState typedef struct BDRVCURLState { CURLM *multi; + QEMUTimer timer; size_t len; CURLState states[CURL_NUM_STATES]; char *url; @@ -87,6 +93,23 @@ typedef struct BDRVCURLState { static void curl_clean_state(CURLState *s); static void curl_multi_do(void *arg); +#ifdef NEED_CURL_TIMER_CALLBACK +static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) +{ + BDRVCURLState *s = opaque; + + DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); + if (timeout_ms == -1) { + timer_del(&s->timer); + } else { + int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000; + timer_mod(&s->timer, + qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns); + } + return 0; +} +#endif + static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, void *s, void *sp) { @@ -134,6 +157,11 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) if (!s || !s->orig_buf) goto read_end; + if (s->buf_off >= s->buf_len) { + /* buffer full, read nothing */ + return 0; + } + realsize = MIN(realsize, s->buf_len - s->buf_off); memcpy(s->orig_buf + s->buf_off, ptr, realsize); s->buf_off += realsize; @@ -209,20 +237,10 @@ static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, return FIND_RET_NONE; } -static void curl_multi_do(void *arg) +static void curl_multi_read(BDRVCURLState *s) { - BDRVCURLState *s = (BDRVCURLState *)arg; - int running; - int r; int msgs_in_queue; - if (!s->multi) - return; - - do { - r = curl_multi_socket_all(s->multi, &running); - } while(r == CURLM_CALL_MULTI_PERFORM); - /* Try to find done transfers, so we can free the easy * handle again. */ do { @@ -266,6 +284,41 @@ static void curl_multi_do(void *arg) } while(msgs_in_queue); } +static void curl_multi_do(void *arg) +{ + BDRVCURLState *s = (BDRVCURLState *)arg; + int running; + int r; + + if (!s->multi) { + return; + } + + do { + r = curl_multi_socket_all(s->multi, &running); + } while(r == CURLM_CALL_MULTI_PERFORM); + + curl_multi_read(s); +} + +static void curl_multi_timeout_do(void *arg) +{ +#ifdef NEED_CURL_TIMER_CALLBACK + BDRVCURLState *s = (BDRVCURLState *)arg; + int running; + + if (!s->multi) { + return; + } + + curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); + + curl_multi_read(s); +#else + abort(); +#endif +} + static CURLState *curl_init_state(BDRVCURLState *s) { CURLState *state = NULL; @@ -473,12 +526,20 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, curl_easy_cleanup(state->curl); state->curl = NULL; + aio_timer_init(bdrv_get_aio_context(bs), &s->timer, + QEMU_CLOCK_REALTIME, SCALE_NS, + curl_multi_timeout_do, s); + // Now we know the file exists and its size, so let's // initialize the multi interface! s->multi = curl_multi_init(); curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s); curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); +#ifdef NEED_CURL_TIMER_CALLBACK + curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); + curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); +#endif curl_multi_do(s); qemu_opts_del(opts); @@ -597,6 +658,9 @@ static void curl_close(BlockDriverState *bs) } if (s->multi) curl_multi_cleanup(s->multi); + + timer_del(&s->timer); + g_free(s->url); } diff --git a/block/dmg.c b/block/dmg.c index d5e9b1ff01..856402e1f2 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -27,6 +27,14 @@ #include "qemu/module.h" #include +enum { + /* Limit chunk sizes to prevent unreasonable amounts of memory being used + * or truncating when converting to 32-bit types + */ + DMG_LENGTHS_MAX = 64 * 1024 * 1024, /* 64 MB */ + DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, +}; + typedef struct BDRVDMGState { CoMutex lock; /* each chunk contains a certain number of sectors, @@ -92,13 +100,44 @@ static int read_uint32(BlockDriverState *bs, int64_t offset, uint32_t *result) return 0; } +/* Increase max chunk sizes, if necessary. This function is used to calculate + * the buffer sizes needed for compressed/uncompressed chunk I/O. + */ +static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, + uint32_t *max_compressed_size, + uint32_t *max_sectors_per_chunk) +{ + uint32_t compressed_size = 0; + uint32_t uncompressed_sectors = 0; + + switch (s->types[chunk]) { + case 0x80000005: /* zlib compressed */ + compressed_size = s->lengths[chunk]; + uncompressed_sectors = s->sectorcounts[chunk]; + break; + case 1: /* copy */ + uncompressed_sectors = (s->lengths[chunk] + 511) / 512; + break; + case 2: /* zero */ + uncompressed_sectors = s->sectorcounts[chunk]; + break; + } + + if (compressed_size > *max_compressed_size) { + *max_compressed_size = compressed_size; + } + if (uncompressed_sectors > *max_sectors_per_chunk) { + *max_sectors_per_chunk = uncompressed_sectors; + } +} + static int dmg_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVDMGState *s = bs->opaque; - uint64_t info_begin,info_end,last_in_offset,last_out_offset; + uint64_t info_begin, info_end, last_in_offset, last_out_offset; uint32_t count, tmp; - uint32_t max_compressed_size=1,max_sectors_per_chunk=1,i; + uint32_t max_compressed_size = 1, max_sectors_per_chunk = 1, i; int64_t offset; int ret; @@ -160,37 +199,40 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - if (type == 0x6d697368 && count >= 244) { - int new_size, chunk_count; + if (type == 0x6d697368 && count >= 244) { + size_t new_size; + uint32_t chunk_count; offset += 4; offset += 200; - chunk_count = (count-204)/40; - new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); - s->types = g_realloc(s->types, new_size/2); - s->offsets = g_realloc(s->offsets, new_size); - s->lengths = g_realloc(s->lengths, new_size); - s->sectors = g_realloc(s->sectors, new_size); - s->sectorcounts = g_realloc(s->sectorcounts, new_size); + chunk_count = (count - 204) / 40; + new_size = sizeof(uint64_t) * (s->n_chunks + chunk_count); + s->types = g_realloc(s->types, new_size / 2); + s->offsets = g_realloc(s->offsets, new_size); + s->lengths = g_realloc(s->lengths, new_size); + s->sectors = g_realloc(s->sectors, new_size); + s->sectorcounts = g_realloc(s->sectorcounts, new_size); for (i = s->n_chunks; i < s->n_chunks + chunk_count; i++) { ret = read_uint32(bs, offset, &s->types[i]); if (ret < 0) { goto fail; } - offset += 4; - if(s->types[i]!=0x80000005 && s->types[i]!=1 && s->types[i]!=2) { - if(s->types[i]==0xffffffff) { - last_in_offset = s->offsets[i-1]+s->lengths[i-1]; - last_out_offset = s->sectors[i-1]+s->sectorcounts[i-1]; - } - chunk_count--; - i--; - offset += 36; - continue; - } - offset += 4; + offset += 4; + if (s->types[i] != 0x80000005 && s->types[i] != 1 && + s->types[i] != 2) { + if (s->types[i] == 0xffffffff && i > 0) { + last_in_offset = s->offsets[i - 1] + s->lengths[i - 1]; + last_out_offset = s->sectors[i - 1] + + s->sectorcounts[i - 1]; + } + chunk_count--; + i--; + offset += 36; + continue; + } + offset += 4; ret = read_uint64(bs, offset, &s->sectors[i]); if (ret < 0) { @@ -205,6 +247,14 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, } offset += 8; + if (s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { + error_report("sector count %" PRIu64 " for chunk %u is " + "larger than max (%u)", + s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); + ret = -EINVAL; + goto fail; + } + ret = read_uint64(bs, offset, &s->offsets[i]); if (ret < 0) { goto fail; @@ -218,19 +268,25 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, } offset += 8; - if(s->lengths[i]>max_compressed_size) - max_compressed_size = s->lengths[i]; - if(s->sectorcounts[i]>max_sectors_per_chunk) - max_sectors_per_chunk = s->sectorcounts[i]; - } - s->n_chunks+=chunk_count; - } + if (s->lengths[i] > DMG_LENGTHS_MAX) { + error_report("length %" PRIu64 " for chunk %u is larger " + "than max (%u)", + s->lengths[i], i, DMG_LENGTHS_MAX); + ret = -EINVAL; + goto fail; + } + + update_max_chunk_size(s, i, &max_compressed_size, + &max_sectors_per_chunk); + } + s->n_chunks += chunk_count; + } } /* initialize zlib engine */ - s->compressed_chunk = g_malloc(max_compressed_size+1); - s->uncompressed_chunk = g_malloc(512*max_sectors_per_chunk); - if(inflateInit(&s->zstream) != Z_OK) { + s->compressed_chunk = g_malloc(max_compressed_size + 1); + s->uncompressed_chunk = g_malloc(512 * max_sectors_per_chunk); + if (inflateInit(&s->zstream) != Z_OK) { ret = -EINVAL; goto fail; } @@ -252,83 +308,82 @@ fail: } static inline int is_sector_in_chunk(BDRVDMGState* s, - uint32_t chunk_num,int sector_num) + uint32_t chunk_num, uint64_t sector_num) { - if(chunk_num>=s->n_chunks || s->sectors[chunk_num]>sector_num || - s->sectors[chunk_num]+s->sectorcounts[chunk_num]<=sector_num) - return 0; - else - return -1; + if (chunk_num >= s->n_chunks || s->sectors[chunk_num] > sector_num || + s->sectors[chunk_num] + s->sectorcounts[chunk_num] <= sector_num) { + return 0; + } else { + return -1; + } } -static inline uint32_t search_chunk(BDRVDMGState* s,int sector_num) +static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) { /* binary search */ - uint32_t chunk1=0,chunk2=s->n_chunks,chunk3; - while(chunk1!=chunk2) { - chunk3 = (chunk1+chunk2)/2; - if(s->sectors[chunk3]>sector_num) - chunk2 = chunk3; - else if(s->sectors[chunk3]+s->sectorcounts[chunk3]>sector_num) - return chunk3; - else - chunk1 = chunk3; + uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; + while (chunk1 != chunk2) { + chunk3 = (chunk1 + chunk2) / 2; + if (s->sectors[chunk3] > sector_num) { + chunk2 = chunk3; + } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { + return chunk3; + } else { + chunk1 = chunk3; + } } return s->n_chunks; /* error */ } -static inline int dmg_read_chunk(BlockDriverState *bs, int sector_num) +static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) { BDRVDMGState *s = bs->opaque; - if(!is_sector_in_chunk(s,s->current_chunk,sector_num)) { - int ret; - uint32_t chunk = search_chunk(s,sector_num); + if (!is_sector_in_chunk(s, s->current_chunk, sector_num)) { + int ret; + uint32_t chunk = search_chunk(s, sector_num); - if(chunk>=s->n_chunks) - return -1; + if (chunk >= s->n_chunks) { + return -1; + } - s->current_chunk = s->n_chunks; - switch(s->types[chunk]) { - case 0x80000005: { /* zlib compressed */ - int i; + s->current_chunk = s->n_chunks; + switch (s->types[chunk]) { + case 0x80000005: { /* zlib compressed */ + /* we need to buffer, because only the chunk as whole can be + * inflated. */ + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->compressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) { + return -1; + } - /* we need to buffer, because only the chunk as whole can be - * inflated. */ - i=0; - do { - ret = bdrv_pread(bs->file, s->offsets[chunk] + i, - s->compressed_chunk+i, s->lengths[chunk]-i); - if(ret<0 && errno==EINTR) - ret=0; - i+=ret; - } while(ret>=0 && ret+ilengths[chunk]); - - if (ret != s->lengths[chunk]) - return -1; - - s->zstream.next_in = s->compressed_chunk; - s->zstream.avail_in = s->lengths[chunk]; - s->zstream.next_out = s->uncompressed_chunk; - s->zstream.avail_out = 512*s->sectorcounts[chunk]; - ret = inflateReset(&s->zstream); - if(ret != Z_OK) - return -1; - ret = inflate(&s->zstream, Z_FINISH); - if(ret != Z_STREAM_END || s->zstream.total_out != 512*s->sectorcounts[chunk]) - return -1; - break; } - case 1: /* copy */ - ret = bdrv_pread(bs->file, s->offsets[chunk], + s->zstream.next_in = s->compressed_chunk; + s->zstream.avail_in = s->lengths[chunk]; + s->zstream.next_out = s->uncompressed_chunk; + s->zstream.avail_out = 512 * s->sectorcounts[chunk]; + ret = inflateReset(&s->zstream); + if (ret != Z_OK) { + return -1; + } + ret = inflate(&s->zstream, Z_FINISH); + if (ret != Z_STREAM_END || + s->zstream.total_out != 512 * s->sectorcounts[chunk]) { + return -1; + } + break; } + case 1: /* copy */ + ret = bdrv_pread(bs->file, s->offsets[chunk], s->uncompressed_chunk, s->lengths[chunk]); - if (ret != s->lengths[chunk]) - return -1; - break; - case 2: /* zero */ - memset(s->uncompressed_chunk, 0, 512*s->sectorcounts[chunk]); - break; - } - s->current_chunk = chunk; + if (ret != s->lengths[chunk]) { + return -1; + } + break; + case 2: /* zero */ + memset(s->uncompressed_chunk, 0, 512 * s->sectorcounts[chunk]); + break; + } + s->current_chunk = chunk; } return 0; } @@ -339,12 +394,14 @@ static int dmg_read(BlockDriverState *bs, int64_t sector_num, BDRVDMGState *s = bs->opaque; int i; - for(i=0;isectors[s->current_chunk]; - memcpy(buf+i*512,s->uncompressed_chunk+sector_offset_in_chunk*512,512); + for (i = 0; i < nb_sectors; i++) { + uint32_t sector_offset_in_chunk; + if (dmg_read_chunk(bs, sector_num + i) != 0) { + return -1; + } + sector_offset_in_chunk = sector_num + i - s->sectors[s->current_chunk]; + memcpy(buf + i * 512, + s->uncompressed_chunk + sector_offset_in_chunk * 512, 512); } return 0; } @@ -376,12 +433,12 @@ static void dmg_close(BlockDriverState *bs) } static BlockDriver bdrv_dmg = { - .format_name = "dmg", - .instance_size = sizeof(BDRVDMGState), - .bdrv_probe = dmg_probe, - .bdrv_open = dmg_open, - .bdrv_read = dmg_co_read, - .bdrv_close = dmg_close, + .format_name = "dmg", + .instance_size = sizeof(BDRVDMGState), + .bdrv_probe = dmg_probe, + .bdrv_open = dmg_open, + .bdrv_read = dmg_co_read, + .bdrv_close = dmg_close, }; static void bdrv_dmg_init(void) diff --git a/block/iscsi.c b/block/iscsi.c index a2d578c0a7..75a4001c61 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -65,6 +65,7 @@ typedef struct IscsiTask { int do_retry; struct scsi_task *task; Coroutine *co; + QEMUBH *bh; } IscsiTask; typedef struct IscsiAIOCB { @@ -121,6 +122,13 @@ iscsi_schedule_bh(IscsiAIOCB *acb) qemu_bh_schedule(acb->bh); } +static void iscsi_co_generic_bh_cb(void *opaque) +{ + struct IscsiTask *iTask = opaque; + qemu_bh_delete(iTask->bh); + qemu_coroutine_enter(iTask->co, NULL); +} + static void iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, void *command_data, void *opaque) @@ -135,17 +143,19 @@ iscsi_co_generic_cb(struct iscsi_context *iscsi, int status, if (iTask->retries-- > 0 && status == SCSI_STATUS_CHECK_CONDITION && task->sense.key == SCSI_SENSE_UNIT_ATTENTION) { + error_report("iSCSI CheckCondition: %s", iscsi_get_error(iscsi)); iTask->do_retry = 1; goto out; } if (status != SCSI_STATUS_GOOD) { - error_report("iSCSI: Failure. %s", iscsi_get_error(iscsi)); + error_report("iSCSI Failure: %s", iscsi_get_error(iscsi)); } out: if (iTask->co) { - qemu_coroutine_enter(iTask->co, NULL); + iTask->bh = qemu_bh_new(iscsi_co_generic_bh_cb, iTask); + qemu_bh_schedule(iTask->bh); } } @@ -859,6 +869,7 @@ retry: scsi_free_scsi_task(iTask.task); iTask.task = NULL; } + iTask.complete = 0; goto retry; } @@ -955,6 +966,7 @@ retry: } if (iTask.do_retry) { + iTask.complete = 0; goto retry; } diff --git a/block/mirror.c b/block/mirror.c index 7b95acf88c..2d9104efc0 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -95,7 +95,14 @@ static void mirror_iteration_done(MirrorOp *op, int ret) } g_slice_free(MirrorOp, op); - qemu_coroutine_enter(s->common.co, NULL); + + /* Enter coroutine when it is not sleeping. The coroutine sleeps to + * rate-limit itself. The coroutine will eventually resume since there is + * a sleep timeout so don't wake it early. + */ + if (s->common.busy) { + qemu_coroutine_enter(s->common.co, NULL); + } } static void mirror_write_complete(void *opaque, int ret) @@ -136,11 +143,12 @@ static void mirror_read_complete(void *opaque, int ret) mirror_write_complete, op); } -static void coroutine_fn mirror_iteration(MirrorBlockJob *s) +static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s) { BlockDriverState *source = s->common.bs; int nb_sectors, sectors_per_chunk, nb_chunks; int64_t end, sector_num, next_chunk, next_sector, hbitmap_next_sector; + uint64_t delay_ns; MirrorOp *op; s->sector_num = hbitmap_iter_next(&s->hbi); @@ -227,7 +235,12 @@ static void coroutine_fn mirror_iteration(MirrorBlockJob *s) nb_chunks += added_chunks; next_sector += added_sectors; next_chunk += added_chunks; - } while (next_sector < end); + if (!s->synced && s->common.speed) { + delay_ns = ratelimit_calculate_delay(&s->limit, added_sectors); + } else { + delay_ns = 0; + } + } while (delay_ns == 0 && next_sector < end); /* Allocate a MirrorOp that is used as an AIO callback. */ op = g_slice_new(MirrorOp); @@ -263,6 +276,7 @@ static void coroutine_fn mirror_iteration(MirrorBlockJob *s) trace_mirror_one_iteration(s, sector_num, nb_sectors); bdrv_aio_readv(source, sector_num, &op->qiov, nb_sectors, mirror_read_complete, op); + return delay_ns; } static void mirror_free_init(MirrorBlockJob *s) @@ -358,7 +372,7 @@ static void coroutine_fn mirror_run(void *opaque) bdrv_dirty_iter_init(bs, &s->hbi); last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); for (;;) { - uint64_t delay_ns; + uint64_t delay_ns = 0; int64_t cnt; bool should_complete; @@ -382,8 +396,10 @@ static void coroutine_fn mirror_run(void *opaque) qemu_coroutine_yield(); continue; } else if (cnt != 0) { - mirror_iteration(s); - continue; + delay_ns = mirror_iteration(s); + if (delay_ns == 0) { + continue; + } } } @@ -428,17 +444,10 @@ static void coroutine_fn mirror_run(void *opaque) } ret = 0; - trace_mirror_before_sleep(s, cnt, s->synced); + trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); if (!s->synced) { /* Publish progress */ s->common.offset = (end - cnt) * BDRV_SECTOR_SIZE; - - if (s->common.speed) { - delay_ns = ratelimit_calculate_delay(&s->limit, sectors_per_chunk); - } else { - delay_ns = 0; - } - block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns); if (block_job_is_cancelled(&s->common)) { break; diff --git a/block/parallels.c b/block/parallels.c index 2121e43204..d83cde4a2a 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -49,9 +49,9 @@ typedef struct BDRVParallelsState { CoMutex lock; uint32_t *catalog_bitmap; - int catalog_size; + unsigned int catalog_size; - int tracks; + unsigned int tracks; } BDRVParallelsState; static int parallels_probe(const uint8_t *buf, int buf_size, const char *filename) @@ -92,8 +92,18 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, bs->total_sectors = le32_to_cpu(ph.nb_sectors); s->tracks = le32_to_cpu(ph.tracks); + if (s->tracks == 0) { + error_setg(errp, "Invalid image: Zero sectors per track"); + ret = -EINVAL; + goto fail; + } s->catalog_size = le32_to_cpu(ph.catalog_entries); + if (s->catalog_size > INT_MAX / 4) { + error_setg(errp, "Catalog too large"); + ret = -EFBIG; + goto fail; + } s->catalog_bitmap = g_malloc(s->catalog_size * 4); ret = bdrv_pread(bs->file, 64, s->catalog_bitmap, s->catalog_size * 4); diff --git a/block/qapi.c b/block/qapi.c index 5880b3e42b..e9007c9ae5 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -471,6 +471,7 @@ static void dump_qobject(fprintf_function func_fprintf, void *f, case QTYPE_QERROR: { QString *value = qerror_human((QError *)obj); func_fprintf(f, "%s", qstring_get_str(value)); + QDECREF(value); break; } case QTYPE_NONE: diff --git a/block/qcow.c b/block/qcow.c index c470e05f60..276a4d0335 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -48,9 +48,10 @@ typedef struct QCowHeader { uint64_t size; /* in bytes */ uint8_t cluster_bits; uint8_t l2_bits; + uint16_t padding; uint32_t crypt_method; uint64_t l1_table_offset; -} QCowHeader; +} QEMU_PACKED QCowHeader; #define L2_CACHE_SIZE 16 @@ -60,7 +61,7 @@ typedef struct BDRVQcowState { int cluster_sectors; int l2_bits; int l2_size; - int l1_size; + unsigned int l1_size; uint64_t cluster_offset_mask; uint64_t l1_table_offset; uint64_t *l1_table; @@ -96,7 +97,8 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVQcowState *s = bs->opaque; - int len, i, shift, ret; + unsigned int len, i, shift; + int ret; QCowHeader header; ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); @@ -125,10 +127,25 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } - if (header.size <= 1 || header.cluster_bits < 9) { + if (header.size <= 1) { + error_setg(errp, "Image size is too small (must be at least 2 bytes)"); ret = -EINVAL; goto fail; } + if (header.cluster_bits < 9 || header.cluster_bits > 16) { + error_setg(errp, "Cluster size must be between 512 and 64k"); + ret = -EINVAL; + goto fail; + } + + /* l2_bits specifies number of entries; storing a uint64_t in each entry, + * so bytes = num_entries << 3. */ + if (header.l2_bits < 9 - 3 || header.l2_bits > 16 - 3) { + error_setg(errp, "L2 table size must be between 512 and 64k"); + ret = -EINVAL; + goto fail; + } + if (header.crypt_method > QCOW_CRYPT_AES) { ret = -EINVAL; goto fail; @@ -147,7 +164,19 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, /* read the level 1 table */ shift = s->cluster_bits + s->l2_bits; - s->l1_size = (header.size + (1LL << shift) - 1) >> shift; + if (header.size > UINT64_MAX - (1LL << shift)) { + error_setg(errp, "Image too large"); + ret = -EINVAL; + goto fail; + } else { + uint64_t l1_size = (header.size + (1LL << shift) - 1) >> shift; + if (l1_size > INT_MAX / sizeof(uint64_t)) { + error_setg(errp, "Image too large"); + ret = -EINVAL; + goto fail; + } + s->l1_size = l1_size; + } s->l1_table_offset = header.l1_table_offset; s->l1_table = g_malloc(s->l1_size * sizeof(uint64_t)); @@ -171,7 +200,9 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, if (header.backing_file_offset != 0) { len = header.backing_file_size; if (len > 1023) { - len = 1023; + error_setg(errp, "Backing file name too long"); + ret = -EINVAL; + goto fail; } ret = bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len); diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 791083a0ef..23f28786ad 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -55,7 +55,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, } } - if (new_l1_size > INT_MAX) { + if (new_l1_size > INT_MAX / sizeof(uint64_t)) { return -EFBIG; } @@ -359,15 +359,6 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs, struct iovec iov; int n, ret; - /* - * If this is the last cluster and it is only partially used, we must only - * copy until the end of the image, or bdrv_check_request will fail for the - * bdrv_read/write calls below. - */ - if (start_sect + n_end > bs->total_sectors) { - n_end = bs->total_sectors - start_sect; - } - n = n_end - n_start; if (n <= 0) { return 0; diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 1ff43d0906..99bb9b0f81 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -28,7 +28,7 @@ #include "qemu/range.h" #include "qapi/qmp/types.h" -static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size); +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, int addend, enum qcow2_discard_type type); @@ -40,8 +40,10 @@ static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, int qcow2_refcount_init(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; - int ret, refcount_table_size2, i; + unsigned int refcount_table_size2, i; + int ret; + assert(s->refcount_table_size <= INT_MAX / sizeof(uint64_t)); refcount_table_size2 = s->refcount_table_size * sizeof(uint64_t); s->refcount_table = g_malloc(refcount_table_size2); if (s->refcount_table_size > 0) { @@ -87,7 +89,7 @@ static int load_refcount_block(BlockDriverState *bs, static int get_refcount(BlockDriverState *bs, int64_t cluster_index) { BDRVQcowState *s = bs->opaque; - int refcount_table_index, block_index; + uint64_t refcount_table_index, block_index; int64_t refcount_block_offset; int ret; uint16_t *refcount_block; @@ -191,10 +193,11 @@ static int alloc_refcount_block(BlockDriverState *bs, * they can describe them themselves. * * - We need to consider that at this point we are inside update_refcounts - * and doing the initial refcount increase. This means that some clusters - * have already been allocated by the caller, but their refcount isn't - * accurate yet. free_cluster_index tells us where this allocation ends - * as long as we don't overwrite it by freeing clusters. + * and potentially doing an initial refcount increase. This means that + * some clusters have already been allocated by the caller, but their + * refcount isn't accurate yet. If we allocate clusters for metadata, we + * need to return -EAGAIN to signal the caller that it needs to restart + * the search for free clusters. * * - alloc_clusters_noref and qcow2_free_clusters may load a different * refcount block into the cache @@ -279,7 +282,10 @@ static int alloc_refcount_block(BlockDriverState *bs, } s->refcount_table[refcount_table_index] = new_block; - return 0; + + /* The new refcount block may be where the caller intended to put its + * data, so let it restart the search. */ + return -EAGAIN; } ret = qcow2_cache_put(bs, s->refcount_block_cache, (void**) refcount_block); @@ -302,8 +308,11 @@ static int alloc_refcount_block(BlockDriverState *bs, /* Calculate the number of refcount blocks needed so far */ uint64_t refcount_block_clusters = 1 << (s->cluster_bits - REFCOUNT_SHIFT); - uint64_t blocks_used = (s->free_cluster_index + - refcount_block_clusters - 1) / refcount_block_clusters; + uint64_t blocks_used = DIV_ROUND_UP(cluster_index, refcount_block_clusters); + + if (blocks_used > QCOW_MAX_REFTABLE_SIZE / sizeof(uint64_t)) { + return -EFBIG; + } /* And now we need at least one block more for the new metadata */ uint64_t table_size = next_refcount_table_size(s, blocks_used + 1); @@ -336,8 +345,6 @@ static int alloc_refcount_block(BlockDriverState *bs, uint16_t *new_blocks = g_malloc0(blocks_clusters * s->cluster_size); uint64_t *new_table = g_malloc0(table_size * sizeof(uint64_t)); - assert(meta_offset >= (s->free_cluster_index * s->cluster_size)); - /* Fill the new refcount table */ memcpy(new_table, s->refcount_table, s->refcount_table_size * sizeof(uint64_t)); @@ -400,18 +407,19 @@ static int alloc_refcount_block(BlockDriverState *bs, s->refcount_table_size = table_size; s->refcount_table_offset = table_offset; - /* Free old table. Remember, we must not change free_cluster_index */ - uint64_t old_free_cluster_index = s->free_cluster_index; + /* Free old table. */ qcow2_free_clusters(bs, old_table_offset, old_table_size * sizeof(uint64_t), QCOW2_DISCARD_OTHER); - s->free_cluster_index = old_free_cluster_index; ret = load_refcount_block(bs, new_block, (void**) refcount_block); if (ret < 0) { return ret; } - return 0; + /* If we were trying to do the initial refcount update for some cluster + * allocation, we might have used the same clusters to store newly + * allocated metadata. Make the caller search some new space. */ + return -EAGAIN; fail_table: g_free(new_table); @@ -626,15 +634,16 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, /* return < 0 if error */ -static int64_t alloc_clusters_noref(BlockDriverState *bs, int64_t size) +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) { BDRVQcowState *s = bs->opaque; - int i, nb_clusters, refcount; + uint64_t i, nb_clusters; + int refcount; nb_clusters = size_to_clusters(s, size); retry: for(i = 0; i < nb_clusters; i++) { - int64_t next_cluster_index = s->free_cluster_index++; + uint64_t next_cluster_index = s->free_cluster_index++; refcount = get_refcount(bs, next_cluster_index); if (refcount < 0) { @@ -651,18 +660,21 @@ retry: return (s->free_cluster_index - nb_clusters) << s->cluster_bits; } -int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size) +int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) { int64_t offset; int ret; BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); - offset = alloc_clusters_noref(bs, size); - if (offset < 0) { - return offset; - } + do { + offset = alloc_clusters_noref(bs, size); + if (offset < 0) { + return offset; + } + + ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); + } while (ret == -EAGAIN); - ret = update_refcount(bs, offset, size, 1, QCOW2_DISCARD_NEVER); if (ret < 0) { return ret; } @@ -675,33 +687,36 @@ int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, { BDRVQcowState *s = bs->opaque; uint64_t cluster_index; - uint64_t old_free_cluster_index; - int i, refcount, ret; + uint64_t i; + int refcount, ret; - /* Check how many clusters there are free */ - cluster_index = offset >> s->cluster_bits; - for(i = 0; i < nb_clusters; i++) { - refcount = get_refcount(bs, cluster_index++); - - if (refcount < 0) { - return refcount; - } else if (refcount != 0) { - break; - } + assert(nb_clusters >= 0); + if (nb_clusters == 0) { + return 0; } - /* And then allocate them */ - old_free_cluster_index = s->free_cluster_index; - s->free_cluster_index = cluster_index + i; + do { + /* Check how many clusters there are free */ + cluster_index = offset >> s->cluster_bits; + for(i = 0; i < nb_clusters; i++) { + refcount = get_refcount(bs, cluster_index++); + + if (refcount < 0) { + return refcount; + } else if (refcount != 0) { + break; + } + } + + /* And then allocate them */ + ret = update_refcount(bs, offset, i << s->cluster_bits, 1, + QCOW2_DISCARD_NEVER); + } while (ret == -EAGAIN); - ret = update_refcount(bs, offset, i << s->cluster_bits, 1, - QCOW2_DISCARD_NEVER); if (ret < 0) { return ret; } - s->free_cluster_index = old_free_cluster_index; - return i; } @@ -1004,8 +1019,7 @@ static void inc_refcounts(BlockDriverState *bs, int64_t offset, int64_t size) { BDRVQcowState *s = bs->opaque; - int64_t start, last, cluster_offset; - int k; + uint64_t start, last, cluster_offset, k; if (size <= 0) return; @@ -1015,11 +1029,7 @@ static void inc_refcounts(BlockDriverState *bs, for(cluster_offset = start; cluster_offset <= last; cluster_offset += s->cluster_size) { k = cluster_offset >> s->cluster_bits; - if (k < 0) { - fprintf(stderr, "ERROR: invalid cluster offset=0x%" PRIx64 "\n", - cluster_offset); - res->corruptions++; - } else if (k >= refcount_table_size) { + if (k >= refcount_table_size) { fprintf(stderr, "Warning: cluster offset=0x%" PRIx64 " is after " "the end of the image file, can't properly check refcounts.\n", cluster_offset); @@ -1460,14 +1470,19 @@ int qcow2_check_refcounts(BlockDriverState *bs, BdrvCheckResult *res, BdrvCheckMode fix) { BDRVQcowState *s = bs->opaque; - int64_t size, i, highest_cluster; - int nb_clusters, refcount1, refcount2; + int64_t size, i, highest_cluster, nb_clusters; + int refcount1, refcount2; QCowSnapshot *sn; uint16_t *refcount_table; int ret; size = bdrv_getlength(bs->file); nb_clusters = size_to_clusters(s, size); + if (nb_clusters > INT_MAX) { + res->check_errors++; + return -EFBIG; + } + refcount_table = g_malloc0(nb_clusters * sizeof(uint16_t)); res->bfi.total_clusters = diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index 3529c683c6..621871046d 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -26,31 +26,6 @@ #include "block/block_int.h" #include "block/qcow2.h" -typedef struct QEMU_PACKED QCowSnapshotHeader { - /* header is 8 byte aligned */ - uint64_t l1_table_offset; - - uint32_t l1_size; - uint16_t id_str_size; - uint16_t name_size; - - uint32_t date_sec; - uint32_t date_nsec; - - uint64_t vm_clock_nsec; - - uint32_t vm_state_size; - uint32_t extra_data_size; /* for extension */ - /* extra data follows */ - /* id_str follows */ - /* name follows */ -} QCowSnapshotHeader; - -typedef struct QEMU_PACKED QCowSnapshotExtraData { - uint64_t vm_state_size_large; - uint64_t disk_size; -} QCowSnapshotExtraData; - void qcow2_free_snapshots(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -357,6 +332,10 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) uint64_t *l1_table = NULL; int64_t l1_table_offset; + if (s->nb_snapshots >= QCOW_MAX_SNAPSHOTS) { + return -EFBIG; + } + memset(sn, 0, sizeof(*sn)); /* Generate an ID if it wasn't passed */ @@ -694,7 +673,11 @@ int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) sn = &s->snapshots[snapshot_index]; /* Allocate and read in the snapshot's L1 table */ - new_l1_bytes = s->l1_size * sizeof(uint64_t); + if (sn->l1_size > QCOW_MAX_L1_SIZE) { + error_report("Snapshot L1 table too large"); + return -EFBIG; + } + new_l1_bytes = sn->l1_size * sizeof(uint64_t); new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512)); ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes); diff --git a/block/qcow2.c b/block/qcow2.c index 6e5d98dc48..37b0f6c9fb 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -269,12 +269,15 @@ static int qcow2_mark_clean(BlockDriverState *bs) BDRVQcowState *s = bs->opaque; if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { - int ret = bdrv_flush(bs); + int ret; + + s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; + + ret = bdrv_flush(bs); if (ret < 0) { return ret; } - s->incompatible_features &= ~QCOW2_INCOMPAT_DIRTY; return qcow2_update_header(bs); } return 0; @@ -329,6 +332,32 @@ static int qcow2_check(BlockDriverState *bs, BdrvCheckResult *result, return ret; } +static int validate_table_offset(BlockDriverState *bs, uint64_t offset, + uint64_t entries, size_t entry_len) +{ + BDRVQcowState *s = bs->opaque; + uint64_t size; + + /* Use signed INT64_MAX as the maximum even for uint64_t header fields, + * because values will be passed to qemu functions taking int64_t. */ + if (entries > INT64_MAX / entry_len) { + return -EINVAL; + } + + size = entries * entry_len; + + if (INT64_MAX - size < offset) { + return -EINVAL; + } + + /* Tables must be cluster aligned */ + if (offset & (s->cluster_size - 1)) { + return -EINVAL; + } + + return 0; +} + static QemuOptsList qcow2_runtime_opts = { .name = "qcow2", .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head), @@ -419,7 +448,8 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVQcowState *s = bs->opaque; - int len, i, ret = 0; + unsigned int len, i; + int ret = 0; QCowHeader header; QemuOpts *opts; Error *local_err = NULL; @@ -460,6 +490,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, s->qcow_version = header.version; + /* Initialise cluster size */ + if (header.cluster_bits < MIN_CLUSTER_BITS || + header.cluster_bits > MAX_CLUSTER_BITS) { + error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits); + ret = -EINVAL; + goto fail; + } + + s->cluster_bits = header.cluster_bits; + s->cluster_size = 1 << s->cluster_bits; + s->cluster_sectors = 1 << (s->cluster_bits - 9); + /* Initialise version 3 header fields */ if (header.version == 2) { header.incompatible_features = 0; @@ -473,6 +515,18 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, be64_to_cpus(&header.autoclear_features); be32_to_cpus(&header.refcount_order); be32_to_cpus(&header.header_length); + + if (header.header_length < 104) { + error_setg(errp, "qcow2 header too short"); + ret = -EINVAL; + goto fail; + } + } + + if (header.header_length > s->cluster_size) { + error_setg(errp, "qcow2 header exceeds cluster size"); + ret = -EINVAL; + goto fail; } if (header.header_length > sizeof(header)) { @@ -487,6 +541,12 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } } + if (header.backing_file_offset > s->cluster_size) { + error_setg(errp, "Invalid backing file offset"); + ret = -EINVAL; + goto fail; + } + if (header.backing_file_offset) { ext_end = header.backing_file_offset; } else { @@ -529,12 +589,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, } s->refcount_order = header.refcount_order; - if (header.cluster_bits < MIN_CLUSTER_BITS || - header.cluster_bits > MAX_CLUSTER_BITS) { - error_setg(errp, "Unsupported cluster size: 2^%i", header.cluster_bits); - ret = -EINVAL; - goto fail; - } if (header.crypt_method > QCOW_CRYPT_AES) { error_setg(errp, "Unsupported encryption method: %i", header.crypt_method); @@ -545,23 +599,52 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, if (s->crypt_method_header) { bs->encrypted = 1; } - s->cluster_bits = header.cluster_bits; - s->cluster_size = 1 << s->cluster_bits; - s->cluster_sectors = 1 << (s->cluster_bits - 9); + s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ s->l2_size = 1 << s->l2_bits; bs->total_sectors = header.size / 512; s->csize_shift = (62 - (s->cluster_bits - 8)); s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; s->cluster_offset_mask = (1LL << s->csize_shift) - 1; + s->refcount_table_offset = header.refcount_table_offset; s->refcount_table_size = header.refcount_table_clusters << (s->cluster_bits - 3); - s->snapshots_offset = header.snapshots_offset; - s->nb_snapshots = header.nb_snapshots; + if (header.refcount_table_clusters > qcow2_max_refcount_clusters(s)) { + error_setg(errp, "Reference count table too large"); + ret = -EINVAL; + goto fail; + } + + ret = validate_table_offset(bs, s->refcount_table_offset, + s->refcount_table_size, sizeof(uint64_t)); + if (ret < 0) { + error_setg(errp, "Invalid reference count table offset"); + goto fail; + } + + /* Snapshot table offset/length */ + if (header.nb_snapshots > QCOW_MAX_SNAPSHOTS) { + error_setg(errp, "Too many snapshots"); + ret = -EINVAL; + goto fail; + } + + ret = validate_table_offset(bs, header.snapshots_offset, + header.nb_snapshots, + sizeof(QCowSnapshotHeader)); + if (ret < 0) { + error_setg(errp, "Invalid snapshot table offset"); + goto fail; + } /* read the level 1 table */ + if (header.l1_size > QCOW_MAX_L1_SIZE) { + error_setg(errp, "Active L1 table too large"); + ret = -EFBIG; + goto fail; + } s->l1_size = header.l1_size; l1_vm_state_index = size_to_l1(s, header.size); @@ -579,7 +662,16 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, ret = -EINVAL; goto fail; } + + ret = validate_table_offset(bs, header.l1_table_offset, + header.l1_size, sizeof(uint64_t)); + if (ret < 0) { + error_setg(errp, "Invalid L1 table offset"); + goto fail; + } s->l1_table_offset = header.l1_table_offset; + + if (s->l1_size > 0) { s->l1_table = g_malloc0( align_offset(s->l1_size * sizeof(uint64_t), 512)); @@ -625,8 +717,10 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* read the backing file name */ if (header.backing_file_offset != 0) { len = header.backing_file_size; - if (len > 1023) { - len = 1023; + if (len > MIN(1023, s->cluster_size - header.backing_file_offset)) { + error_setg(errp, "Backing file name too long"); + ret = -EINVAL; + goto fail; } ret = bdrv_pread(bs->file, header.backing_file_offset, bs->backing_file, len); @@ -637,6 +731,10 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, bs->backing_file[len] = '\0'; } + /* Internal snapshots */ + s->snapshots_offset = header.snapshots_offset; + s->nb_snapshots = header.nb_snapshots; + ret = qcow2_read_snapshots(bs); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read snapshots"); @@ -792,11 +890,25 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key) return 0; } -/* We have nothing to do for QCOW2 reopen, stubs just return - * success */ +/* We have no actual commit/abort logic for qcow2, but we need to write out any + * unwritten data if we reopen read-only. */ static int qcow2_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { + int ret; + + if ((state->flags & BDRV_O_RDWR) == 0) { + ret = bdrv_flush(state->bs); + if (ret < 0) { + return ret; + } + + ret = qcow2_mark_clean(state->bs); + if (ret < 0) { + return ret; + } + } + return 0; } @@ -1471,8 +1583,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, * size for any qcow2 image. */ BlockDriverState* bs; - QCowHeader header; - uint8_t* refcount_table; + QCowHeader *header; + uint64_t* refcount_table; Error *local_err = NULL; int ret; @@ -1489,38 +1601,43 @@ static int qcow2_create2(const char *filename, int64_t total_size, } /* Write the header */ - memset(&header, 0, sizeof(header)); - header.magic = cpu_to_be32(QCOW_MAGIC); - header.version = cpu_to_be32(version); - header.cluster_bits = cpu_to_be32(cluster_bits); - header.size = cpu_to_be64(0); - header.l1_table_offset = cpu_to_be64(0); - header.l1_size = cpu_to_be32(0); - header.refcount_table_offset = cpu_to_be64(cluster_size); - header.refcount_table_clusters = cpu_to_be32(1); - header.refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT); - header.header_length = cpu_to_be32(sizeof(header)); + QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); + header = g_malloc0(cluster_size); + *header = (QCowHeader) { + .magic = cpu_to_be32(QCOW_MAGIC), + .version = cpu_to_be32(version), + .cluster_bits = cpu_to_be32(cluster_bits), + .size = cpu_to_be64(0), + .l1_table_offset = cpu_to_be64(0), + .l1_size = cpu_to_be32(0), + .refcount_table_offset = cpu_to_be64(cluster_size), + .refcount_table_clusters = cpu_to_be32(1), + .refcount_order = cpu_to_be32(3 + REFCOUNT_SHIFT), + .header_length = cpu_to_be32(sizeof(*header)), + }; if (flags & BLOCK_FLAG_ENCRYPT) { - header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES); + header->crypt_method = cpu_to_be32(QCOW_CRYPT_AES); } else { - header.crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); + header->crypt_method = cpu_to_be32(QCOW_CRYPT_NONE); } if (flags & BLOCK_FLAG_LAZY_REFCOUNTS) { - header.compatible_features |= + header->compatible_features |= cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); } - ret = bdrv_pwrite(bs, 0, &header, sizeof(header)); + ret = bdrv_pwrite(bs, 0, header, cluster_size); + g_free(header); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write qcow2 header"); goto out; } - /* Write an empty refcount table */ - refcount_table = g_malloc0(cluster_size); - ret = bdrv_pwrite(bs, cluster_size, refcount_table, cluster_size); + /* Write a refcount table with one refcount block */ + refcount_table = g_malloc0(2 * cluster_size); + refcount_table[0] = cpu_to_be64(2 * cluster_size); + ret = bdrv_pwrite(bs, cluster_size, refcount_table, 2 * cluster_size); g_free(refcount_table); if (ret < 0) { @@ -1544,7 +1661,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, goto out; } - ret = qcow2_alloc_clusters(bs, 2 * cluster_size); + ret = qcow2_alloc_clusters(bs, 3 * cluster_size); if (ret < 0) { error_setg_errno(errp, -ret, "Could not allocate clusters for qcow2 " "header and refcount table"); @@ -1588,7 +1705,8 @@ static int qcow2_create2(const char *filename, int64_t total_size, /* Reopen the image without BDRV_O_NO_FLUSH to flush it before returning */ ret = bdrv_open(bs, filename, NULL, - BDRV_O_RDWR | BDRV_O_CACHE_WB, drv, &local_err); + BDRV_O_RDWR | BDRV_O_CACHE_WB | BDRV_O_NO_BACKING, + drv, &local_err); if (error_is_set(&local_err)) { error_propagate(errp, local_err); goto out; diff --git a/block/qcow2.h b/block/qcow2.h index 922e19062a..29afb59e48 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -38,6 +38,15 @@ #define QCOW_CRYPT_AES 1 #define QCOW_MAX_CRYPT_CLUSTERS 32 +#define QCOW_MAX_SNAPSHOTS 65536 + +/* 8 MB refcount table is enough for 2 PB images at 64k cluster size + * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ +#define QCOW_MAX_REFTABLE_SIZE 0x800000 + +/* 32 MB L1 table is enough for 2 PB images at 64k cluster size + * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ +#define QCOW_MAX_L1_SIZE 0x2000000 /* indicate that the refcount of the referenced cluster is exactly one. */ #define QCOW_OFLAG_COPIED (1ULL << 63) @@ -97,6 +106,32 @@ typedef struct QCowHeader { uint32_t header_length; } QEMU_PACKED QCowHeader; +typedef struct QEMU_PACKED QCowSnapshotHeader { + /* header is 8 byte aligned */ + uint64_t l1_table_offset; + + uint32_t l1_size; + uint16_t id_str_size; + uint16_t name_size; + + uint32_t date_sec; + uint32_t date_nsec; + + uint64_t vm_clock_nsec; + + uint32_t vm_state_size; + uint32_t extra_data_size; /* for extension */ + /* extra data follows */ + /* id_str follows */ + /* name follows */ +} QCowSnapshotHeader; + +typedef struct QEMU_PACKED QCowSnapshotExtraData { + uint64_t vm_state_size_large; + uint64_t disk_size; +} QCowSnapshotExtraData; + + typedef struct QCowSnapshot { uint64_t l1_table_offset; uint32_t l1_size; @@ -191,8 +226,8 @@ typedef struct BDRVQcowState { uint64_t *refcount_table; uint64_t refcount_table_offset; uint32_t refcount_table_size; - int64_t free_cluster_index; - int64_t free_byte_offset; + uint64_t free_cluster_index; + uint64_t free_byte_offset; CoMutex lock; @@ -202,7 +237,7 @@ typedef struct BDRVQcowState { AES_KEY aes_decrypt_key; uint64_t snapshots_offset; int snapshots_size; - int nb_snapshots; + unsigned int nb_snapshots; QCowSnapshot *snapshots; int flags; @@ -383,6 +418,11 @@ static inline int64_t qcow2_vm_state_offset(BDRVQcowState *s) return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); } +static inline uint64_t qcow2_max_refcount_clusters(BDRVQcowState *s) +{ + return QCOW_MAX_REFTABLE_SIZE >> s->cluster_bits; +} + static inline int qcow2_get_cluster_type(uint64_t l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { @@ -431,7 +471,7 @@ void qcow2_refcount_close(BlockDriverState *bs); int qcow2_update_cluster_refcount(BlockDriverState *bs, int64_t cluster_index, int addend, enum qcow2_discard_type type); -int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size); +int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size); int qcow2_alloc_clusters_at(BlockDriverState *bs, uint64_t offset, int nb_clusters); int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size); diff --git a/block/sheepdog.c b/block/sheepdog.c index ef387de71f..43a23df803 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -2082,6 +2082,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) strncpy(s->inode.tag, sn_info->name, sizeof(s->inode.tag)); /* we don't need to update entire object */ datalen = SD_INODE_SIZE - sizeof(s->inode.data_vdi_id); + inode = g_malloc(datalen); /* refresh inode. */ fd = connect_to_sdog(s); @@ -2105,8 +2106,6 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto cleanup; } - inode = (SheepdogInode *)g_malloc(datalen); - ret = read_object(fd, (char *)inode, vid_to_vdi_oid(new_vid), s->inode.nr_copies, datalen, 0, s->cache_flags); @@ -2120,6 +2119,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) s->inode.name, s->inode.snap_id, s->inode.vdi_id); cleanup: + g_free(inode); closesocket(fd); return ret; } diff --git a/block/vdi.c b/block/vdi.c index b6ec0020dc..204a3e5c45 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -120,6 +120,11 @@ typedef unsigned char uuid_t[16]; #define VDI_IS_ALLOCATED(X) ((X) < VDI_DISCARDED) +/* max blocks in image is (0xffffffff / 4) */ +#define VDI_BLOCKS_IN_IMAGE_MAX 0x3fffffff +#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \ + (uint64_t)DEFAULT_CLUSTER_SIZE) + #if !defined(CONFIG_UUID) static inline void uuid_generate(uuid_t out) { @@ -384,6 +389,13 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, vdi_header_print(&header); #endif + if (header.disk_size > VDI_DISK_SIZE_MAX) { + logout("disk size is 0x%" PRIx64 ", max supported is 0x%" PRIx64, + header.disk_size, VDI_DISK_SIZE_MAX); + ret = -ENOTSUP; + goto fail; + } + if (header.disk_size % SECTOR_SIZE != 0) { /* 'VBoxManage convertfromraw' can create images with odd disk sizes. We accept them but round the disk size to the next multiple of @@ -416,7 +428,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, logout("unsupported sector size %u B\n", header.sector_size); ret = -ENOTSUP; goto fail; - } else if (header.block_size != 1 * MiB) { + } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { logout("unsupported block size %u B\n", header.block_size); ret = -ENOTSUP; goto fail; @@ -433,6 +445,11 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, logout("parent uuid != 0, unsupported\n"); ret = -ENOTSUP; goto fail; + } else if (header.blocks_in_image > VDI_BLOCKS_IN_IMAGE_MAX) { + logout("too many blocks %u, max is %u)", + header.blocks_in_image, VDI_BLOCKS_IN_IMAGE_MAX); + ret = -ENOTSUP; + goto fail; } bs->total_sectors = header.disk_size / SECTOR_SIZE; @@ -681,11 +698,20 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options, options++; } + if (bytes > VDI_DISK_SIZE_MAX) { + result = -ENOTSUP; + logout("image size (size is 0x%" PRIx64 + ", max supported is 0x%" PRIx64 ")", + bytes, VDI_DISK_SIZE_MAX); + goto exit; + } + fd = qemu_open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_LARGEFILE, 0644); if (fd < 0) { - return -errno; + result = -errno; + goto exit; } /* We need enough blocks to store the given disk size, @@ -746,6 +772,7 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options, result = -errno; } +exit: return result; } diff --git a/block/vhdx.c b/block/vhdx.c index 7d1af9663b..8a70ae8a12 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -785,12 +785,20 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) le32_to_cpus(&s->logical_sector_size); le32_to_cpus(&s->physical_sector_size); - if (s->logical_sector_size == 0 || s->params.block_size == 0) { + if (s->params.block_size < VHDX_BLOCK_SIZE_MIN || + s->params.block_size > VHDX_BLOCK_SIZE_MAX) { ret = -EINVAL; goto exit; } - /* both block_size and sector_size are guaranteed powers of 2 */ + /* only 2 supported sector sizes */ + if (s->logical_sector_size != 512 && s->logical_sector_size != 4096) { + ret = -EINVAL; + goto exit; + } + + /* Both block_size and sector_size are guaranteed powers of 2, below. + Due to range checks above, s->sectors_per_block can never be < 256 */ s->sectors_per_block = s->params.block_size / s->logical_sector_size; s->chunk_ratio = (VHDX_MAX_SECTORS_PER_BLOCK) * (uint64_t)s->logical_sector_size / diff --git a/block/vmdk.c b/block/vmdk.c index a7ebd0f125..d35a9d7e02 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1689,7 +1689,7 @@ static int vmdk_create(const char *filename, QEMUOptionParameter *options, } if (backing_file) { BlockDriverState *bs = bdrv_new(""); - ret = bdrv_open(bs, backing_file, NULL, 0, NULL, errp); + ret = bdrv_open(bs, backing_file, NULL, BDRV_O_NO_BACKING, NULL, errp); if (ret != 0) { bdrv_unref(bs); return ret; diff --git a/block/vpc.c b/block/vpc.c index 577cc45992..be4f8ab3ca 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -45,6 +45,8 @@ enum vhd_type { // Seconds since Jan 1, 2000 0:00:00 (UTC) #define VHD_TIMESTAMP_BASE 946684800 +#define VHD_MAX_SECTORS (65535LL * 255 * 255) + // always big-endian typedef struct vhd_footer { char creator[8]; // "conectix" @@ -164,6 +166,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, VHDDynDiskHeader *dyndisk_header; uint8_t buf[HEADER_SIZE]; uint32_t checksum; + uint64_t computed_size; int disk_type = VHD_DYNAMIC; int ret; @@ -221,7 +224,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } /* Allow a maximum disk size of approximately 2 TB */ - if (bs->total_sectors >= 65535LL * 255 * 255) { + if (bs->total_sectors >= VHD_MAX_SECTORS) { ret = -EFBIG; goto fail; } @@ -241,10 +244,31 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } s->block_size = be32_to_cpu(dyndisk_header->block_size); + if (!is_power_of_2(s->block_size) || s->block_size < BDRV_SECTOR_SIZE) { + error_setg(errp, "Invalid block size %" PRIu32, s->block_size); + ret = -EINVAL; + goto fail; + } s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); - s->pagetable = g_malloc(s->max_table_entries * 4); + + if ((bs->total_sectors * 512) / s->block_size > 0xffffffffU) { + ret = -EINVAL; + goto fail; + } + if (s->max_table_entries > (VHD_MAX_SECTORS * 512) / s->block_size) { + ret = -EINVAL; + goto fail; + } + + computed_size = (uint64_t) s->max_table_entries * s->block_size; + if (computed_size < bs->total_sectors * 512) { + ret = -EINVAL; + goto fail; + } + + s->pagetable = qemu_blockalign(bs, s->max_table_entries * 4); s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); @@ -297,7 +321,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, return 0; fail: - g_free(s->pagetable); + qemu_vfree(s->pagetable); #ifdef CACHE g_free(s->pageentry_u8); #endif @@ -819,7 +843,7 @@ static int vpc_has_zero_init(BlockDriverState *bs) static void vpc_close(BlockDriverState *bs) { BDRVVPCState *s = bs->opaque; - g_free(s->pagetable); + qemu_vfree(s->pagetable); #ifdef CACHE g_free(s->pageentry_u8); #endif diff --git a/block/vvfat.c b/block/vvfat.c index 3ddaa0bcce..e9e4fad44d 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -788,7 +788,9 @@ static int read_directory(BDRVVVFATState* s, int mapping_index) s->current_mapping->path=buffer; s->current_mapping->read_only = (st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0; - } + } else { + g_free(buffer); + } } closedir(dir); @@ -1866,7 +1868,7 @@ static int check_directory_consistency(BDRVVVFATState *s, if (s->used_clusters[cluster_num] & USED_ANY) { fprintf(stderr, "cluster %d used more than once\n", (int)cluster_num); - return 0; + goto fail; } s->used_clusters[cluster_num] = USED_DIRECTORY; diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 922cf5657b..b3a24740b2 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -27,8 +27,9 @@ static void nbd_accept(void *opaque) socklen_t addr_len = sizeof(addr); int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - if (fd >= 0) { - nbd_client_new(NULL, fd, nbd_client_put); + if (fd >= 0 && !nbd_client_new(NULL, fd, nbd_client_put)) { + shutdown(fd, 2); + close(fd); } } @@ -91,6 +92,10 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, error_set(errp, QERR_DEVICE_NOT_FOUND, device); return; } + if (!bdrv_is_inserted(bs)) { + error_set(errp, QERR_DEVICE_HAS_NO_MEDIUM, device); + return; + } if (!has_writable) { writable = false; diff --git a/blockdev.c b/blockdev.c index e14113805f..d1dffb3b1f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -340,7 +340,7 @@ static DriveInfo *blockdev_init(QDict *bs_opts, opts = qemu_opts_create(&qemu_common_drive_opts, id, 1, &error); if (error_is_set(&error)) { error_propagate(errp, error); - return NULL; + goto err_no_opts; } qemu_opts_absorb_qdict(opts, bs_opts, &error); @@ -544,8 +544,9 @@ err: QTAILQ_REMOVE(&drives, dinfo, next); g_free(dinfo); early_err: - QDECREF(bs_opts); qemu_opts_del(opts); +err_no_opts: + QDECREF(bs_opts); return NULL; } @@ -876,6 +877,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) /* Actual block device init: Functionality shared with blockdev-add */ dinfo = blockdev_init(bs_opts, type, &local_err); + bs_opts = NULL; if (dinfo == NULL) { if (error_is_set(&local_err)) { qerror_report_err(local_err); @@ -912,6 +914,7 @@ DriveInfo *drive_init(QemuOpts *all_opts, BlockInterfaceType block_default_type) fail: qemu_opts_del(legacy_opts); + QDECREF(bs_opts); return dinfo; } @@ -1795,6 +1798,10 @@ void qmp_block_commit(const char *device, */ BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT; + if (!has_speed) { + speed = 0; + } + /* drain all i/o before commits */ bdrv_drain_all(); diff --git a/configure b/configure index 52a00cebbb..c2bb66e074 100755 --- a/configure +++ b/configure @@ -1357,6 +1357,11 @@ EOF pie="no" fi fi + + if compile_prog "-fno-pie" "-nopie"; then + CFLAGS_NOPIE="-fno-pie" + LDFLAGS_NOPIE="-nopie" + fi fi ########################################## @@ -3539,6 +3544,11 @@ fi int128=no cat > $TMPC << EOF +#if defined(__clang_major__) && defined(__clang_minor__) +# if ((__clang_major__ < 3) || (__clang_major__ == 3) && (__clang_minor__ < 2)) +# error __int128_t does not work in CLANG before 3.2 +# endif +#endif __int128_t a; __uint128_t b; int main (void) { @@ -4312,6 +4322,7 @@ echo "LD=$ld" >> $config_host_mak echo "WINDRES=$windres" >> $config_host_mak echo "LIBTOOL=$libtool" >> $config_host_mak echo "CFLAGS=$CFLAGS" >> $config_host_mak +echo "CFLAGS_NOPIE=$CFLAGS_NOPIE" >> $config_host_mak echo "QEMU_CFLAGS=$QEMU_CFLAGS" >> $config_host_mak echo "QEMU_INCLUDES=$QEMU_INCLUDES" >> $config_host_mak if test "$sparse" = "yes" ; then @@ -4325,6 +4336,7 @@ else echo "AUTOCONF_HOST := " >> $config_host_mak fi echo "LDFLAGS=$LDFLAGS" >> $config_host_mak +echo "LDFLAGS_NOPIE=$LDFLAGS_NOPIE" >> $config_host_mak echo "LIBTOOLFLAGS=$LIBTOOLFLAGS" >> $config_host_mak echo "LIBS+=$LIBS" >> $config_host_mak echo "LIBS_TOOLS+=$libs_tools" >> $config_host_mak diff --git a/coroutine-win32.c b/coroutine-win32.c index edc1f72c18..17ace37dee 100644 --- a/coroutine-win32.c +++ b/coroutine-win32.c @@ -36,8 +36,17 @@ typedef struct static __thread CoroutineWin32 leader; static __thread Coroutine *current; -CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, - CoroutineAction action) +/* This function is marked noinline to prevent GCC from inlining it + * into coroutine_trampoline(). If we allow it to do that then it + * hoists the code to get the address of the TLS variable "current" + * out of the while() loop. This is an invalid transformation because + * the SwitchToFiber() call may be called when running thread A but + * return in thread B, and so we might be in a different thread + * context each time round the loop. + */ +CoroutineAction __attribute__((noinline)) +qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, + CoroutineAction action) { CoroutineWin32 *from = DO_UPCAST(CoroutineWin32, base, from_); CoroutineWin32 *to = DO_UPCAST(CoroutineWin32, base, to_); diff --git a/cputlb.c b/cputlb.c index fff0afbd4a..6c030c8154 100644 --- a/cputlb.c +++ b/cputlb.c @@ -344,8 +344,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env1, target_ulong addr) } #define MMUSUFFIX _cmmu -#undef GETPC -#define GETPC() ((uintptr_t)0) +#undef GETPC_ADJ +#define GETPC_ADJ 0 +#undef GETRA +#define GETRA() ((uintptr_t)0) #define SOFTMMU_CODE_ACCESS #define SHIFT 0 diff --git a/docs/migration.txt b/docs/migration.txt index 0e0a1d44da..fe1f2bb738 100644 --- a/docs/migration.txt +++ b/docs/migration.txt @@ -139,7 +139,6 @@ static const VMStateDescription vmstate_kbd = { .name = "pckbd", .version_id = 3, .minimum_version_id = 3, - .minimum_version_id_old = 3, .fields = (VMStateField []) { VMSTATE_UINT8(write_cmd, KBDState), VMSTATE_UINT8(status, KBDState), @@ -168,12 +167,13 @@ You can see that there are several version fields: - minimum_version_id: the minimum version_id that VMState is able to understand for that device. - minimum_version_id_old: For devices that were not able to port to vmstate, we can - assign a function that knows how to read this old state. + assign a function that knows how to read this old state. This field is + ignored if there is no load_state_old handler. So, VMState is able to read versions from minimum_version_id to -version_id. And the function load_state_old() is able to load state -from minimum_version_id_old to minimum_version_id. This function is -deprecated and will be removed when no more users are left. +version_id. And the function load_state_old() (if present) is able to +load state from minimum_version_id_old to minimum_version_id. This +function is deprecated and will be removed when no more users are left. === Massaging functions === @@ -255,7 +255,6 @@ const VMStateDescription vmstate_ide_drive_pio_state = { .name = "ide_drive/pio_state", .version_id = 1, .minimum_version_id = 1, - .minimum_version_id_old = 1, .pre_save = ide_drive_pio_pre_save, .post_load = ide_drive_pio_post_load, .fields = (VMStateField []) { @@ -275,7 +274,6 @@ const VMStateDescription vmstate_ide_drive = { .name = "ide_drive", .version_id = 3, .minimum_version_id = 0, - .minimum_version_id_old = 0, .post_load = ide_drive_post_load, .fields = (VMStateField []) { .... several fields .... diff --git a/exec.c b/exec.c index 95c4356c65..b324fcc95a 100644 --- a/exec.c +++ b/exec.c @@ -83,20 +83,37 @@ int use_icount; typedef struct PhysPageEntry PhysPageEntry; struct PhysPageEntry { - uint16_t is_leaf : 1; - /* index into phys_sections (is_leaf) or phys_map_nodes (!is_leaf) */ + /* How many bits skip to next level (in units of L2_SIZE). 0 for a leaf. */ + uint16_t skip : 1; + /* index into phys_sections (!skip) or phys_map_nodes (skip) */ uint16_t ptr : 15; }; -typedef PhysPageEntry Node[L2_SIZE]; +/* Size of the L2 (and L3, etc) page tables. */ +#define ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS + +#define P_L2_BITS 10 +#define P_L2_SIZE (1 << P_L2_BITS) + +#define P_L2_LEVELS (((ADDR_SPACE_BITS - TARGET_PAGE_BITS - 1) / P_L2_BITS) + 1) + +typedef PhysPageEntry Node[P_L2_SIZE]; + +typedef struct PhysPageMap { + unsigned sections_nb; + unsigned sections_nb_alloc; + unsigned nodes_nb; + unsigned nodes_nb_alloc; + Node *nodes; + MemoryRegionSection *sections; +} PhysPageMap; struct AddressSpaceDispatch { /* This is a multi-level map on the physical address space. * The bottom level has pointers to MemoryRegionSections. */ PhysPageEntry phys_map; - Node *nodes; - MemoryRegionSection *sections; + PhysPageMap map; AddressSpace *as; }; @@ -113,18 +130,6 @@ typedef struct subpage_t { #define PHYS_SECTION_ROM 2 #define PHYS_SECTION_WATCH 3 -typedef struct PhysPageMap { - unsigned sections_nb; - unsigned sections_nb_alloc; - unsigned nodes_nb; - unsigned nodes_nb_alloc; - Node *nodes; - MemoryRegionSection *sections; -} PhysPageMap; - -static PhysPageMap *prev_map; -static PhysPageMap next_map; - #define PHYS_MAP_NODE_NIL (((uint16_t)~0) >> 1) static void io_mem_init(void); @@ -135,63 +140,60 @@ static MemoryRegion io_mem_watch; #if !defined(CONFIG_USER_ONLY) -static void phys_map_node_reserve(unsigned nodes) +static void phys_map_node_reserve(PhysPageMap *map, unsigned nodes) { - if (next_map.nodes_nb + nodes > next_map.nodes_nb_alloc) { - next_map.nodes_nb_alloc = MAX(next_map.nodes_nb_alloc * 2, - 16); - next_map.nodes_nb_alloc = MAX(next_map.nodes_nb_alloc, - next_map.nodes_nb + nodes); - next_map.nodes = g_renew(Node, next_map.nodes, - next_map.nodes_nb_alloc); + if (map->nodes_nb + nodes > map->nodes_nb_alloc) { + map->nodes_nb_alloc = MAX(map->nodes_nb_alloc * 2, 16); + map->nodes_nb_alloc = MAX(map->nodes_nb_alloc, map->nodes_nb + nodes); + map->nodes = g_renew(Node, map->nodes, map->nodes_nb_alloc); } } -static uint16_t phys_map_node_alloc(void) +static uint16_t phys_map_node_alloc(PhysPageMap *map) { unsigned i; uint16_t ret; - ret = next_map.nodes_nb++; + ret = map->nodes_nb++; assert(ret != PHYS_MAP_NODE_NIL); - assert(ret != next_map.nodes_nb_alloc); - for (i = 0; i < L2_SIZE; ++i) { - next_map.nodes[ret][i].is_leaf = 0; - next_map.nodes[ret][i].ptr = PHYS_MAP_NODE_NIL; + assert(ret != map->nodes_nb_alloc); + for (i = 0; i < P_L2_SIZE; ++i) { + map->nodes[ret][i].skip = 1; + map->nodes[ret][i].ptr = PHYS_MAP_NODE_NIL; } return ret; } -static void phys_page_set_level(PhysPageEntry *lp, hwaddr *index, - hwaddr *nb, uint16_t leaf, +static void phys_page_set_level(PhysPageMap *map, PhysPageEntry *lp, + hwaddr *index, hwaddr *nb, uint16_t leaf, int level) { PhysPageEntry *p; int i; - hwaddr step = (hwaddr)1 << (level * L2_BITS); + hwaddr step = (hwaddr)1 << (level * P_L2_BITS); - if (!lp->is_leaf && lp->ptr == PHYS_MAP_NODE_NIL) { - lp->ptr = phys_map_node_alloc(); - p = next_map.nodes[lp->ptr]; + if (lp->skip && lp->ptr == PHYS_MAP_NODE_NIL) { + lp->ptr = phys_map_node_alloc(map); + p = map->nodes[lp->ptr]; if (level == 0) { - for (i = 0; i < L2_SIZE; i++) { - p[i].is_leaf = 1; + for (i = 0; i < P_L2_SIZE; i++) { + p[i].skip = 0; p[i].ptr = PHYS_SECTION_UNASSIGNED; } } } else { - p = next_map.nodes[lp->ptr]; + p = map->nodes[lp->ptr]; } - lp = &p[(*index >> (level * L2_BITS)) & (L2_SIZE - 1)]; + lp = &p[(*index >> (level * P_L2_BITS)) & (P_L2_SIZE - 1)]; - while (*nb && lp < &p[L2_SIZE]) { + while (*nb && lp < &p[P_L2_SIZE]) { if ((*index & (step - 1)) == 0 && *nb >= step) { - lp->is_leaf = true; + lp->skip = 0; lp->ptr = leaf; *index += step; *nb -= step; } else { - phys_page_set_level(lp, index, nb, leaf, level - 1); + phys_page_set_level(map, lp, index, nb, leaf, level - 1); } ++lp; } @@ -202,23 +204,24 @@ static void phys_page_set(AddressSpaceDispatch *d, uint16_t leaf) { /* Wildly overreserve - it doesn't matter much. */ - phys_map_node_reserve(3 * P_L2_LEVELS); + phys_map_node_reserve(&d->map, 3 * P_L2_LEVELS); - phys_page_set_level(&d->phys_map, &index, &nb, leaf, P_L2_LEVELS - 1); + phys_page_set_level(&d->map, &d->phys_map, &index, &nb, leaf, P_L2_LEVELS - 1); } -static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr index, +static MemoryRegionSection *phys_page_find(PhysPageEntry lp, hwaddr addr, Node *nodes, MemoryRegionSection *sections) { PhysPageEntry *p; + hwaddr index = addr >> TARGET_PAGE_BITS; int i; - for (i = P_L2_LEVELS - 1; i >= 0 && !lp.is_leaf; i--) { + for (i = P_L2_LEVELS; lp.skip && (i -= lp.skip) >= 0;) { if (lp.ptr == PHYS_MAP_NODE_NIL) { return §ions[PHYS_SECTION_UNASSIGNED]; } p = nodes[lp.ptr]; - lp = p[(index >> (i * L2_BITS)) & (L2_SIZE - 1)]; + lp = p[(index >> (i * P_L2_BITS)) & (P_L2_SIZE - 1)]; } return §ions[lp.ptr]; } @@ -236,11 +239,10 @@ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d, MemoryRegionSection *section; subpage_t *subpage; - section = phys_page_find(d->phys_map, addr >> TARGET_PAGE_BITS, - d->nodes, d->sections); + section = phys_page_find(d->phys_map, addr, d->map.nodes, d->map.sections); if (resolve_subpage && section->mr->subpage) { subpage = container_of(section->mr, subpage_t, iomem); - section = &d->sections[subpage->sub_section[SUBPAGE_IDX(addr)]]; + section = &d->map.sections[subpage->sub_section[SUBPAGE_IDX(addr)]]; } return section; } @@ -264,6 +266,18 @@ address_space_translate_internal(AddressSpaceDispatch *d, hwaddr addr, hwaddr *x return section; } +static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) +{ + if (memory_region_is_ram(mr)) { + return !(is_write && mr->readonly); + } + if (memory_region_is_romd(mr)) { + return !is_write; + } + + return false; +} + MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, hwaddr *xlat, hwaddr *plen, bool is_write) @@ -293,6 +307,11 @@ MemoryRegion *address_space_translate(AddressSpace *as, hwaddr addr, as = iotlb.target_as; } + if (memory_access_is_direct(mr, is_write)) { + hwaddr page = ((addr & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE) - addr; + len = MIN(page, len); + } + *plen = len; *xlat = addr; return mr; @@ -708,7 +727,7 @@ hwaddr memory_region_section_get_iotlb(CPUArchState *env, iotlb |= PHYS_SECTION_ROM; } } else { - iotlb = section - address_space_memory.dispatch->sections; + iotlb = section - address_space_memory.dispatch->map.sections; iotlb += xlat; } @@ -747,23 +766,23 @@ void phys_mem_set_alloc(void *(*alloc)(size_t)) phys_mem_alloc = alloc; } -static uint16_t phys_section_add(MemoryRegionSection *section) +static uint16_t phys_section_add(PhysPageMap *map, + MemoryRegionSection *section) { /* The physical section number is ORed with a page-aligned * pointer to produce the iotlb entries. Thus it should * never overflow into the page-aligned value. */ - assert(next_map.sections_nb < TARGET_PAGE_SIZE); + assert(map->sections_nb < TARGET_PAGE_SIZE); - if (next_map.sections_nb == next_map.sections_nb_alloc) { - next_map.sections_nb_alloc = MAX(next_map.sections_nb_alloc * 2, - 16); - next_map.sections = g_renew(MemoryRegionSection, next_map.sections, - next_map.sections_nb_alloc); + if (map->sections_nb == map->sections_nb_alloc) { + map->sections_nb_alloc = MAX(map->sections_nb_alloc * 2, 16); + map->sections = g_renew(MemoryRegionSection, map->sections, + map->sections_nb_alloc); } - next_map.sections[next_map.sections_nb] = *section; + map->sections[map->sections_nb] = *section; memory_region_ref(section->mr); - return next_map.sections_nb++; + return map->sections_nb++; } static void phys_section_destroy(MemoryRegion *mr) @@ -785,7 +804,6 @@ static void phys_sections_free(PhysPageMap *map) } g_free(map->sections); g_free(map->nodes); - g_free(map); } static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *section) @@ -793,8 +811,8 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti subpage_t *subpage; hwaddr base = section->offset_within_address_space & TARGET_PAGE_MASK; - MemoryRegionSection *existing = phys_page_find(d->phys_map, base >> TARGET_PAGE_BITS, - next_map.nodes, next_map.sections); + MemoryRegionSection *existing = phys_page_find(d->phys_map, base, + d->map.nodes, d->map.sections); MemoryRegionSection subsection = { .offset_within_address_space = base, .size = int128_make64(TARGET_PAGE_SIZE), @@ -807,13 +825,14 @@ static void register_subpage(AddressSpaceDispatch *d, MemoryRegionSection *secti subpage = subpage_init(d->as, base); subsection.mr = &subpage->iomem; phys_page_set(d, base >> TARGET_PAGE_BITS, 1, - phys_section_add(&subsection)); + phys_section_add(&d->map, &subsection)); } else { subpage = container_of(existing->mr, subpage_t, iomem); } start = section->offset_within_address_space & ~TARGET_PAGE_MASK; end = start + int128_get64(section->size) - 1; - subpage_register(subpage, start, end, phys_section_add(section)); + subpage_register(subpage, start, end, + phys_section_add(&d->map, section)); } @@ -821,7 +840,7 @@ static void register_multipage(AddressSpaceDispatch *d, MemoryRegionSection *section) { hwaddr start_addr = section->offset_within_address_space; - uint16_t section_index = phys_section_add(section); + uint16_t section_index = phys_section_add(&d->map, section); uint64_t num_pages = int128_get64(int128_rshift(section->size, TARGET_PAGE_BITS)); @@ -1605,7 +1624,7 @@ static subpage_t *subpage_init(AddressSpace *as, hwaddr base) return mmio; } -static uint16_t dummy_section(MemoryRegion *mr) +static uint16_t dummy_section(PhysPageMap *map, MemoryRegion *mr) { MemoryRegionSection section = { .mr = mr, @@ -1614,12 +1633,13 @@ static uint16_t dummy_section(MemoryRegion *mr) .size = int128_2_64(), }; - return phys_section_add(§ion); + return phys_section_add(map, §ion); } MemoryRegion *iotlb_to_region(hwaddr index) { - return address_space_memory.dispatch->sections[index & ~TARGET_PAGE_MASK].mr; + return address_space_memory.dispatch->map.sections[ + index & ~TARGET_PAGE_MASK].mr; } static void io_mem_init(void) @@ -1636,9 +1656,19 @@ static void io_mem_init(void) static void mem_begin(MemoryListener *listener) { AddressSpace *as = container_of(listener, AddressSpace, dispatch_listener); - AddressSpaceDispatch *d = g_new(AddressSpaceDispatch, 1); + AddressSpaceDispatch *d = g_new0(AddressSpaceDispatch, 1); + uint16_t n; - d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .is_leaf = 0 }; + n = dummy_section(&d->map, &io_mem_unassigned); + assert(n == PHYS_SECTION_UNASSIGNED); + n = dummy_section(&d->map, &io_mem_notdirty); + assert(n == PHYS_SECTION_NOTDIRTY); + n = dummy_section(&d->map, &io_mem_rom); + assert(n == PHYS_SECTION_ROM); + n = dummy_section(&d->map, &io_mem_watch); + assert(n == PHYS_SECTION_WATCH); + + d->phys_map = (PhysPageEntry) { .ptr = PHYS_MAP_NODE_NIL, .skip = 1 }; d->as = as; as->next_dispatch = d; } @@ -1649,37 +1679,12 @@ static void mem_commit(MemoryListener *listener) AddressSpaceDispatch *cur = as->dispatch; AddressSpaceDispatch *next = as->next_dispatch; - next->nodes = next_map.nodes; - next->sections = next_map.sections; - as->dispatch = next; - g_free(cur); -} -static void core_begin(MemoryListener *listener) -{ - uint16_t n; - - prev_map = g_new(PhysPageMap, 1); - *prev_map = next_map; - - memset(&next_map, 0, sizeof(next_map)); - n = dummy_section(&io_mem_unassigned); - assert(n == PHYS_SECTION_UNASSIGNED); - n = dummy_section(&io_mem_notdirty); - assert(n == PHYS_SECTION_NOTDIRTY); - n = dummy_section(&io_mem_rom); - assert(n == PHYS_SECTION_ROM); - n = dummy_section(&io_mem_watch); - assert(n == PHYS_SECTION_WATCH); -} - -/* This listener's commit run after the other AddressSpaceDispatch listeners'. - * All AddressSpaceDispatch instances have switched to the next map. - */ -static void core_commit(MemoryListener *listener) -{ - phys_sections_free(prev_map); + if (cur) { + phys_sections_free(&cur->map); + g_free(cur); + } } static void tcg_commit(MemoryListener *listener) @@ -1707,8 +1712,6 @@ static void core_log_global_stop(MemoryListener *listener) } static MemoryListener core_memory_listener = { - .begin = core_begin, - .commit = core_commit, .log_global_start = core_log_global_start, .log_global_stop = core_log_global_stop, .priority = 1, @@ -1743,7 +1746,12 @@ void address_space_destroy_dispatch(AddressSpace *as) static void memory_map_init(void) { system_memory = g_malloc(sizeof(*system_memory)); - memory_region_init(system_memory, NULL, "system", INT64_MAX); + + assert(ADDR_SPACE_BITS <= 64); + + memory_region_init(system_memory, NULL, "system", + ADDR_SPACE_BITS == 64 ? + UINT64_MAX : (0x1ULL << ADDR_SPACE_BITS)); address_space_init(&address_space_memory, system_memory, "memory"); system_io = g_malloc(sizeof(*system_io)); @@ -1824,18 +1832,6 @@ static void invalidate_and_set_dirty(hwaddr addr, xen_modified_memory(addr, length); } -static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) -{ - if (memory_region_is_ram(mr)) { - return !(is_write && mr->readonly); - } - if (memory_region_is_romd(mr)) { - return !is_write; - } - - return false; -} - static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) { unsigned access_size_max = mr->ops->valid.max_access_size; diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index 9402c841e9..ffbf4bd540 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -75,9 +75,18 @@ static struct keymap map[0xE0] = { [0x2c] = {4,3}, /* z */ [0xc7] = {5,0}, /* Home */ [0x2a] = {5,1}, /* shift */ - [0x39] = {5,2}, /* space */ + /* + * There are two matrix positions which map to space, + * but QEMU can only use one of them for the reverse + * mapping, so simply use the second one. + */ + /* [0x39] = {5,2}, space */ [0x39] = {5,3}, /* space */ - [0x1c] = {5,5}, /* enter */ + /* + * Matrix position {5,4} and other keys are missing here. + * TODO: Compare with Linux code and test real hardware. + */ + [0x1c] = {5,5}, /* enter (TODO: might be wrong) */ [0xc8] = {6,0}, /* up */ [0xd0] = {6,1}, /* down */ [0xcb] = {6,2}, /* left */ diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 023e8756e2..a8d0086354 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -92,8 +92,6 @@ #define MP_ETH_CRDP3 0x4AC #define MP_ETH_CTDP0 0x4E0 #define MP_ETH_CTDP1 0x4E4 -#define MP_ETH_CTDP2 0x4E8 -#define MP_ETH_CTDP3 0x4EC /* MII PHY access */ #define MP_ETH_SMIR_DATA 0x0000FFFF @@ -308,7 +306,7 @@ static uint64_t mv88w8618_eth_read(void *opaque, hwaddr offset, case MP_ETH_CRDP0 ... MP_ETH_CRDP3: return s->rx_queue[(offset - MP_ETH_CRDP0)/4]; - case MP_ETH_CTDP0 ... MP_ETH_CTDP3: + case MP_ETH_CTDP0 ... MP_ETH_CTDP1: return s->tx_queue[(offset - MP_ETH_CTDP0)/4]; default: @@ -362,7 +360,7 @@ static void mv88w8618_eth_write(void *opaque, hwaddr offset, s->cur_rx[(offset - MP_ETH_CRDP0)/4] = value; break; - case MP_ETH_CTDP0 ... MP_ETH_CTDP3: + case MP_ETH_CTDP0 ... MP_ETH_CTDP1: s->tx_queue[(offset - MP_ETH_CTDP0)/4] = value; break; } diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 47511d2cae..e97eacd365 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -172,7 +172,7 @@ static void omap_timer_clk_update(void *opaque, int line, int on) static void omap_timer_clk_setup(struct omap_mpu_timer_s *timer) { omap_clk_adduser(timer->clk, - qemu_allocate_irqs(omap_timer_clk_update, timer, 1)[0]); + qemu_allocate_irq(omap_timer_clk_update, timer, 0)); timer->rate = omap_clk_getrate(timer->clk); } @@ -2094,7 +2094,7 @@ static struct omap_mpuio_s *omap_mpuio_init(MemoryRegion *memory, "omap-mpuio", 0x800); memory_region_add_subregion(memory, base, &s->iomem); - omap_clk_adduser(clk, qemu_allocate_irqs(omap_mpuio_onoff, s, 1)[0]); + omap_clk_adduser(clk, qemu_allocate_irq(omap_mpuio_onoff, s, 0)); return s; } @@ -2397,7 +2397,7 @@ static struct omap_pwl_s *omap_pwl_init(MemoryRegion *system_memory, "omap-pwl", 0x800); memory_region_add_subregion(system_memory, base, &s->iomem); - omap_clk_adduser(clk, qemu_allocate_irqs(omap_pwl_clk_update, s, 1)[0]); + omap_clk_adduser(clk, qemu_allocate_irq(omap_pwl_clk_update, s, 0)); return s; } @@ -3481,8 +3481,8 @@ static void omap_mcbsp_i2s_start(void *opaque, int line, int level) void omap_mcbsp_i2s_attach(struct omap_mcbsp_s *s, I2SCodec *slave) { s->codec = slave; - slave->rx_swallow = qemu_allocate_irqs(omap_mcbsp_i2s_swallow, s, 1)[0]; - slave->tx_start = qemu_allocate_irqs(omap_mcbsp_i2s_start, s, 1)[0]; + slave->rx_swallow = qemu_allocate_irq(omap_mcbsp_i2s_swallow, s, 0); + slave->tx_start = qemu_allocate_irq(omap_mcbsp_i2s_start, s, 0); } /* LED Pulse Generators */ @@ -3630,7 +3630,7 @@ static struct omap_lpg_s *omap_lpg_init(MemoryRegion *system_memory, memory_region_init_io(&s->iomem, NULL, &omap_lpg_ops, s, "omap-lpg", 0x800); memory_region_add_subregion(system_memory, base, &s->iomem); - omap_clk_adduser(clk, qemu_allocate_irqs(omap_lpg_clk_update, s, 1)[0]); + omap_clk_adduser(clk, qemu_allocate_irq(omap_lpg_clk_update, s, 0)); return s; } @@ -3844,7 +3844,7 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *system_memory, s->sdram_size = sdram_size; s->sram_size = OMAP15XX_SRAM_SIZE; - s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0]; + s->wakeup = qemu_allocate_irq(omap_mpu_wakeup, s, 0); /* Clocks */ omap_clk_init(s); diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 36efde0d64..dc53a7abba 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -2260,7 +2260,7 @@ struct omap_mpu_state_s *omap2420_mpu_init(MemoryRegion *sysmem, s->sdram_size = sdram_size; s->sram_size = OMAP242X_SRAM_SIZE; - s->wakeup = qemu_allocate_irqs(omap_mpu_wakeup, s, 1)[0]; + s->wakeup = qemu_allocate_irq(omap_mpu_wakeup, s, 0); /* Clocks */ omap_clk_init(s); diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index 02b7016a04..30cf71fb00 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -742,7 +742,7 @@ static void pxa2xx_ssp_save(QEMUFile *f, void *opaque) static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id) { PXA2xxSSPState *s = (PXA2xxSSPState *) opaque; - int i; + int i, v; s->enable = qemu_get_be32(f); @@ -756,7 +756,11 @@ static int pxa2xx_ssp_load(QEMUFile *f, void *opaque, int version_id) qemu_get_8s(f, &s->ssrsa); qemu_get_8s(f, &s->ssacd); - s->rx_level = qemu_get_byte(f); + v = qemu_get_byte(f); + if (v < 0 || v > ARRAY_SIZE(s->rx_fifo)) { + return -EINVAL; + } + s->rx_level = v; s->rx_start = 0; for (i = 0; i < s->rx_level; i ++) s->rx_fifo[i] = qemu_get_byte(f); @@ -2053,7 +2057,7 @@ PXA2xxState *pxa270_init(MemoryRegion *address_space, fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0]; + s->reset = qemu_allocate_irq(pxa2xx_reset, s, 0); /* SDRAM & Internal Memory Storage */ memory_region_init_ram(&s->sdram, NULL, "pxa270.sdram", sdram_size); @@ -2184,7 +2188,7 @@ PXA2xxState *pxa255_init(MemoryRegion *address_space, unsigned int sdram_size) fprintf(stderr, "Unable to find CPU definition\n"); exit(1); } - s->reset = qemu_allocate_irqs(pxa2xx_reset, s, 1)[0]; + s->reset = qemu_allocate_irq(pxa2xx_reset, s, 0); /* SDRAM & Internal Memory Storage */ memory_region_init_ram(&s->sdram, NULL, "pxa255.sdram", sdram_size); diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index ba172835eb..54f21668cb 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -743,7 +743,7 @@ static void spitz_i2c_setup(PXA2xxState *cpu) spitz_wm8750_addr(wm, 0, 0); qdev_connect_gpio_out(cpu->gpio, SPITZ_GPIO_WM, - qemu_allocate_irqs(spitz_wm8750_addr, wm, 1)[0]); + qemu_allocate_irq(spitz_wm8750_addr, wm, 0)); /* .. and to the sound interface. */ cpu->i2s->opaque = wm; cpu->i2s->codec_out = wm8750_dac_dat; @@ -849,7 +849,7 @@ static void spitz_gpio_setup(PXA2xxState *cpu, int slots) * wouldn't guarantee that a guest ever exits the loop. */ spitz_hsync = 0; - lcd_hsync = qemu_allocate_irqs(spitz_lcd_hsync_handler, cpu, 1)[0]; + lcd_hsync = qemu_allocate_irq(spitz_lcd_hsync_handler, cpu, 0); pxa2xx_gpio_read_notifier(cpu->gpio, lcd_hsync); pxa2xx_lcd_vsync_notifier(cpu->lcd, lcd_hsync); diff --git a/hw/arm/z2.c b/hw/arm/z2.c index d52c5019b3..d662130a2d 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -359,7 +359,7 @@ static void z2_init(QEMUMachineInitArgs *args) wm8750_data_req_set(wm, mpu->i2s->data_req, mpu->i2s); qdev_connect_gpio_out(mpu->gpio, Z2_GPIO_LCD_CS, - qemu_allocate_irqs(z2_lcd_cs, z2_lcd, 1)[0]); + qemu_allocate_irq(z2_lcd_cs, z2_lcd, 0)); z2_binfo.kernel_filename = kernel_filename; z2_binfo.kernel_cmdline = kernel_cmdline; diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index bd8e9d9815..e88d2dd845 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -347,8 +347,8 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT; s->mixbuf = g_malloc0 (s->samples << SHIFT); - adlib_portio_list[1].offset = s->port; - adlib_portio_list[2].offset = s->port + 8; + adlib_portio_list[0].offset = s->port; + adlib_portio_list[1].offset = s->port + 8; portio_list_init (port_list, OBJECT(s), adlib_portio_list, s, "adlib"); portio_list_add (port_list, isa_address_space_io(&s->parent_obj), 0); } diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 4327264394..6ab8c245d3 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -444,6 +444,7 @@ static bool intel_hda_xfer(HDACodecDevice *dev, uint32_t stnr, bool output, } } if (d->dp_lbase & 0x01) { + s = st - d->st; addr = intel_hda_addr(d->dp_lbase & ~0x01, d->dp_ubase); stl_le_pci_dma(&d->pci, addr + 8*s, st->lpib); } diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 13f6d8276e..7f0440f3a8 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -728,20 +728,18 @@ static int virtio_blk_device_init(VirtIODevice *vdev) return 0; } -static int virtio_blk_device_exit(DeviceState *dev) +static void virtio_blk_device_exit(VirtIODevice *vdev) { - VirtIODevice *vdev = VIRTIO_DEVICE(dev); - VirtIOBlock *s = VIRTIO_BLK(dev); + VirtIOBlock *s = VIRTIO_BLK(vdev); #ifdef CONFIG_VIRTIO_BLK_DATA_PLANE remove_migration_state_change_notifier(&s->migration_state_notifier); virtio_blk_data_plane_destroy(s->dataplane); s->dataplane = NULL; #endif qemu_del_vm_change_state_handler(s->change); - unregister_savevm(dev, "virtio-blk", s); + unregister_savevm(DEVICE(vdev), "virtio-blk", s); blockdev_mark_auto_del(s->bs); virtio_cleanup(vdev); - return 0; } static Property virtio_blk_properties[] = { @@ -753,10 +751,10 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = virtio_blk_device_exit; dc->props = virtio_blk_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->init = virtio_blk_device_init; + vdc->exit = virtio_blk_device_exit; vdc->get_config = virtio_blk_update_config; vdc->set_config = virtio_blk_set_config; vdc->get_features = virtio_blk_get_features; diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 703f026370..f3e496f5ac 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -670,6 +670,7 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) uint32_t max_nr_ports, nr_active_ports, ports_map; unsigned int i; int ret; + uint32_t tmp; if (version_id > 3) { return -EINVAL; @@ -685,17 +686,12 @@ static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) return 0; } - /* The config space */ - qemu_get_be16s(f, &s->config.cols); - qemu_get_be16s(f, &s->config.rows); - - qemu_get_be32s(f, &max_nr_ports); - tswap32s(&max_nr_ports); - if (max_nr_ports > tswap32(s->config.max_nr_ports)) { - /* Source could have had more ports than us. Fail migration. */ - return -EINVAL; - } + /* Unused */ + qemu_get_be16s(f, (uint16_t *) &tmp); + qemu_get_be16s(f, (uint16_t *) &tmp); + qemu_get_be32s(f, &tmp); + max_nr_ports = tswap32(s->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_get_be32s(f, &ports_map); @@ -987,12 +983,11 @@ static const TypeInfo virtio_serial_port_type_info = { .class_init = virtio_serial_port_class_init, }; -static int virtio_serial_device_exit(DeviceState *dev) +static void virtio_serial_device_exit(VirtIODevice *vdev) { - VirtIOSerial *vser = VIRTIO_SERIAL(dev); - VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtIOSerial *vser = VIRTIO_SERIAL(vdev); - unregister_savevm(dev, "virtio-console", vser); + unregister_savevm(DEVICE(vdev), "virtio-console", vser); g_free(vser->ivqs); g_free(vser->ovqs); @@ -1004,7 +999,6 @@ static int virtio_serial_device_exit(DeviceState *dev) g_free(vser->post_load); } virtio_cleanup(vdev); - return 0; } static Property virtio_serial_properties[] = { @@ -1016,10 +1010,10 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = virtio_serial_device_exit; dc->props = virtio_serial_properties; set_bit(DEVICE_CATEGORY_INPUT, dc->categories); vdc->init = virtio_serial_device_init; + vdc->exit = virtio_serial_device_exit; vdc->get_features = get_features; vdc->get_config = get_config; vdc->set_config = set_config; diff --git a/hw/core/irq.c b/hw/core/irq.c index 03c8cb31ea..3d284c6482 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -102,7 +102,7 @@ qemu_irq qemu_irq_invert(qemu_irq irq) { /* The default state for IRQs is low, so raise the output now. */ qemu_irq_raise(irq); - return qemu_allocate_irqs(qemu_notirq, irq, 1)[0]; + return qemu_allocate_irq(qemu_notirq, irq, 0); } static void qemu_splitirq(void *opaque, int line, int level) @@ -117,7 +117,7 @@ qemu_irq qemu_irq_split(qemu_irq irq1, qemu_irq irq2) qemu_irq *s = g_malloc0(2 * sizeof(qemu_irq)); s[0] = irq1; s[1] = irq2; - return qemu_allocate_irqs(qemu_splitirq, s, 1)[0]; + return qemu_allocate_irq(qemu_splitirq, s, 0); } static void proxy_irq_handler(void *opaque, int n, int level) diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index c3231c6116..773414c7ad 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -312,18 +312,42 @@ static int ssd0323_load(QEMUFile *f, void *opaque, int version_id) return -EINVAL; s->cmd_len = qemu_get_be32(f); + if (s->cmd_len < 0 || s->cmd_len > ARRAY_SIZE(s->cmd_data)) { + return -EINVAL; + } s->cmd = qemu_get_be32(f); for (i = 0; i < 8; i++) s->cmd_data[i] = qemu_get_be32(f); s->row = qemu_get_be32(f); + if (s->row < 0 || s->row >= 80) { + return -EINVAL; + } s->row_start = qemu_get_be32(f); + if (s->row_start < 0 || s->row_start >= 80) { + return -EINVAL; + } s->row_end = qemu_get_be32(f); + if (s->row_end < 0 || s->row_end >= 80) { + return -EINVAL; + } s->col = qemu_get_be32(f); + if (s->col < 0 || s->col >= 64) { + return -EINVAL; + } s->col_start = qemu_get_be32(f); + if (s->col_start < 0 || s->col_start >= 64) { + return -EINVAL; + } s->col_end = qemu_get_be32(f); + if (s->col_end < 0 || s->col_end >= 64) { + return -EINVAL; + } s->redraw = qemu_get_be32(f); s->remap = qemu_get_be32(f); s->mode = qemu_get_be32(f); + if (s->mode != SSD0323_CMD && s->mode != SSD0323_DATA) { + return -EINVAL; + } qemu_get_buffer(f, s->framebuffer, sizeof(s->framebuffer)); ss->cs = qemu_get_be32(f); diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 0e8cccd27f..bb02279372 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -1660,7 +1660,7 @@ struct soc_dma_s *omap_dma_init(hwaddr base, qemu_irq *irqs, } omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]); + omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); omap_dma_reset(s->dma); omap_dma_clk_update(s, 0, 1); @@ -2082,7 +2082,7 @@ struct soc_dma_s *omap_dma4_init(hwaddr base, qemu_irq *irqs, s->intr_update = omap_dma_interrupts_4_update; omap_dma_setcaps(s); - omap_clk_adduser(s->clk, qemu_allocate_irqs(omap_dma_clk_update, s, 1)[0]); + omap_clk_adduser(s->clk, qemu_allocate_irq(omap_dma_clk_update, s, 0)); omap_dma_reset(s->dma); omap_dma_clk_update(s, 0, !!s->dma->freq); diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index dc79a8baa6..8e2ce049de 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -203,6 +203,15 @@ static bool is_version_0 (void *opaque, int version_id) return version_id == 0; } +static bool vmstate_scoop_validate(void *opaque, int version_id) +{ + ScoopInfo *s = opaque; + + return !(s->prev_level & 0xffff0000) && + !(s->gpio_level & 0xffff0000) && + !(s->gpio_dir & 0xffff0000); +} + static const VMStateDescription vmstate_scoop_regs = { .name = "scoop", .version_id = 1, @@ -215,6 +224,7 @@ static const VMStateDescription vmstate_scoop_regs = { VMSTATE_UINT32(gpio_level, ScoopInfo), VMSTATE_UINT32(gpio_dir, ScoopInfo), VMSTATE_UINT32(prev_level, ScoopInfo), + VMSTATE_VALIDATE("irq levels are 16 bit", vmstate_scoop_validate), VMSTATE_UINT16(mcr, ScoopInfo), VMSTATE_UINT16(cdr, ScoopInfo), VMSTATE_UINT16(ccr, ScoopInfo), diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 1f22fb60a4..8988047ace 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1075,15 +1075,16 @@ void acpi_build(PcGuestInfo *guest_info, AcpiBuildTables *tables) /* ACPI tables pointed to by RSDT */ acpi_add_table(table_offsets, tables->table_data); build_fadt(tables->table_data, tables->linker, &pm, facs, dsdt); - acpi_add_table(table_offsets, tables->table_data); + acpi_add_table(table_offsets, tables->table_data); build_ssdt(tables->table_data, tables->linker, &cpu, &pm, &misc, &pci, guest_info); - acpi_add_table(table_offsets, tables->table_data); - build_madt(tables->table_data, tables->linker, &cpu, guest_info); acpi_add_table(table_offsets, tables->table_data); + build_madt(tables->table_data, tables->linker, &cpu, guest_info); + if (misc.has_hpet) { + acpi_add_table(table_offsets, tables->table_data); build_hpet(tables->table_data, tables->linker); } if (guest_info->numa_nodes) { diff --git a/hw/i386/bios-linker-loader.c b/hw/i386/bios-linker-loader.c index fd23611008..aa56184e9a 100644 --- a/hw/i386/bios-linker-loader.c +++ b/hw/i386/bios-linker-loader.c @@ -18,11 +18,10 @@ * with this program; if not, see . */ +#include "qemu-common.h" #include "bios-linker-loader.h" #include "hw/nvram/fw_cfg.h" -#include -#include #include "qemu/bswap.h" #define BIOS_LINKER_LOADER_FILESZ FW_CFG_MAX_FILE_PATH diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 968680104b..db70d6ea31 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -1257,6 +1257,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev) if (pos != 0 && kvm_device_msix_supported(kvm_state)) { int bar_nr; uint32_t msix_table_entry; + uint16_t msix_max; if (!check_irqchip_in_kernel()) { return -ENOTSUP; @@ -1268,9 +1269,10 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev) } pci_dev->msix_cap = pos; - pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS, - pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) & - PCI_MSIX_FLAGS_QSIZE); + msix_max = (pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) & + PCI_MSIX_FLAGS_QSIZE) + 1; + msix_max = MIN(msix_max, KVM_MAX_MSIX_PER_DEV); + pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS, msix_max - 1); /* Only enable and function mask bits are writable */ pci_set_word(pci_dev->wmask + pos + PCI_MSIX_FLAGS, @@ -1280,9 +1282,7 @@ static int assigned_device_pci_cap_init(PCIDevice *pci_dev) bar_nr = msix_table_entry & PCI_MSIX_FLAGS_BIRMASK; msix_table_entry &= ~PCI_MSIX_FLAGS_BIRMASK; dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry; - dev->msix_max = pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS); - dev->msix_max &= PCI_MSIX_FLAGS_QSIZE; - dev->msix_max += 1; + dev->msix_max = msix_max; } /* Minimal PM support, nothing writable, device appears to NAK changes */ diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 12c436e7f1..6c82ada3d4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1093,21 +1093,13 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, return guest_info; } -void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start, - uint64_t pci_hole64_size) +/* setup pci memory address space mapping into system address space */ +void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, + MemoryRegion *pci_address_space) { - if ((sizeof(hwaddr) == 4) || (!pci_hole64_size)) { - return; - } - /* - * BIOS does not set MTRR entries for the 64 bit window, so no need to - * align address to power of two. Align address at 1G, this makes sure - * it can be exactly covered with a PAT entry even when using huge - * pages. - */ - pci_info->w64.begin = ROUND_UP(pci_hole64_start, 0x1ULL << 30); - pci_info->w64.end = pci_info->w64.begin + pci_hole64_size; - assert(pci_info->w64.begin <= pci_info->w64.end); + /* Set to lower priority than RAM */ + memory_region_add_subregion_overlap(system_memory, 0x0, + pci_address_space, -1); } void pc_acpi_init(const char *default_dsdt) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 2111f0192c..cc9b273bc5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -150,7 +150,6 @@ static void pc_init1(QEMUMachineInitArgs *args, pci_bus = i440fx_init(&i440fx_state, &piix3_devfn, &isa_bus, gsi, system_memory, system_io, args->ram_size, below_4g_mem_size, - 0x100000000ULL - below_4g_mem_size, above_4g_mem_size, pci_memory, ram_memory); } else { diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index fbea9e8886..e3212741df 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -1290,7 +1290,7 @@ const VMStateDescription vmstate_ahci = { VMSTATE_UINT32(control_regs.impl, AHCIState), VMSTATE_UINT32(control_regs.version, AHCIState), VMSTATE_UINT32(idp_index, AHCIState), - VMSTATE_INT32(ports, AHCIState), + VMSTATE_INT32_EQUAL(ports, AHCIState), VMSTATE_END_OF_LIST() }, }; diff --git a/hw/ide/core.c b/hw/ide/core.c index d6479b9ecc..9dd9843e2b 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -1619,7 +1619,7 @@ static bool cmd_smart(IDEState *s, uint8_t cmd) case 2: /* extended self test */ s->smart_selftest_count++; if (s->smart_selftest_count > 21) { - s->smart_selftest_count = 0; + s->smart_selftest_count = 1; } n = 2 + (s->smart_selftest_count - 1) * 24; s->smart_selftest_data[n] = s->sector; diff --git a/hw/ide/microdrive.c b/hw/ide/microdrive.c index 21d6495817..c73c5a700f 100644 --- a/hw/ide/microdrive.c +++ b/hw/ide/microdrive.c @@ -594,7 +594,7 @@ static void microdrive_realize(DeviceState *dev, Error **errp) { MicroDriveState *md = MICRODRIVE(dev); - ide_init2(&md->bus, qemu_allocate_irqs(md_set_irq, md, 1)[0]); + ide_init2(&md->bus, qemu_allocate_irq(md_set_irq, md, 0)); } static void microdrive_init(Object *obj) diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index 485c9e5753..aa5b6886ea 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -1070,9 +1070,21 @@ static int tsc210x_load(QEMUFile *f, void *opaque, int version_id) s->enabled = qemu_get_byte(f); s->host_mode = qemu_get_byte(f); s->function = qemu_get_byte(f); + if (s->function < 0 || s->function >= ARRAY_SIZE(mode_regs)) { + return -EINVAL; + } s->nextfunction = qemu_get_byte(f); + if (s->nextfunction < 0 || s->nextfunction >= ARRAY_SIZE(mode_regs)) { + return -EINVAL; + } s->precision = qemu_get_byte(f); + if (s->precision < 0 || s->precision >= ARRAY_SIZE(resolution)) { + return -EINVAL; + } s->nextprecision = qemu_get_byte(f); + if (s->nextprecision < 0 || s->nextprecision >= ARRAY_SIZE(resolution)) { + return -EINVAL; + } s->filter = qemu_get_byte(f); s->pin_func = qemu_get_byte(f); s->ref = qemu_get_byte(f); diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index ef5e8eb22f..3287479456 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -418,7 +418,7 @@ static int exynos4210_combiner_init(SysBusDevice *sbd) qdev_init_gpio_in(dev, exynos4210_combiner_handler, IIC_NIRQ); /* Connect SysBusDev irqs to device specific irqs */ - for (i = 0; i < IIC_NIRQ; i++) { + for (i = 0; i < IIC_NGRP; i++) { sysbus_init_irq(sbd, &s->output_irq[i]); } diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 3989fd1bd5..25107cef39 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -41,7 +41,7 @@ #define GIC_SET_MODEL(irq) s->irq_state[irq].model = true #define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false #define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level = (cm) +#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level |= (cm) #define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) #define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) #define GIC_SET_TRIGGER(irq) s->irq_state[irq].trigger = true diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 7df72f44f0..ede1bc473f 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -41,6 +41,7 @@ #include "hw/sysbus.h" #include "hw/pci/msi.h" #include "qemu/bitops.h" +#include "qapi/qmp/qerror.h" //#define DEBUG_OPENPIC @@ -1416,7 +1417,7 @@ static void openpic_load_IRQ_queue(QEMUFile* f, IRQQueue *q) static int openpic_load(QEMUFile* f, void *opaque, int version_id) { OpenPICState *opp = (OpenPICState *)opaque; - unsigned int i; + unsigned int i, nb_cpus; if (version_id != 1) { return -EINVAL; @@ -1428,7 +1429,11 @@ static int openpic_load(QEMUFile* f, void *opaque, int version_id) qemu_get_be32s(f, &opp->spve); qemu_get_be32s(f, &opp->tfrr); - qemu_get_be32s(f, &opp->nb_cpus); + qemu_get_be32s(f, &nb_cpus); + if (opp->nb_cpus != nb_cpus) { + return -EINVAL; + } + assert(nb_cpus > 0 && nb_cpus <= MAX_CPU); for (i = 0; i < opp->nb_cpus; i++) { qemu_get_sbe32s(f, &opp->dst[i].ctpr); @@ -1567,6 +1572,13 @@ static void openpic_realize(DeviceState *dev, Error **errp) {NULL} }; + if (opp->nb_cpus > MAX_CPU) { + error_set(errp, QERR_PROPERTY_VALUE_OUT_OF_RANGE, + TYPE_OPENPIC, "nb_cpus", (uint64_t)opp->nb_cpus, + (uint64_t)0, (uint64_t)MAX_CPU); + return; + } + switch (opp->model) { case OPENPIC_MODEL_FSL_MPIC_20: default: diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 0fc26d29a5..3fad6f86de 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -276,7 +276,7 @@ static bool vexpress_cfgctrl_read(arm_sysctl_state *s, unsigned int dcc, } break; case SYS_CFG_OSC: - if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) { + if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) { /* motherboard clock */ *val = s->mb_clock[device]; return true; @@ -324,7 +324,7 @@ static bool vexpress_cfgctrl_write(arm_sysctl_state *s, unsigned int dcc, switch (function) { case SYS_CFG_OSC: - if (site == SYS_CFG_SITE_MB && device < sizeof(s->mb_clock)) { + if (site == SYS_CFG_SITE_MB && device < ARRAY_SIZE(s->mb_clock)) { /* motherboard clock */ s->mb_clock[device] = val; return true; diff --git a/hw/misc/cbus.c b/hw/misc/cbus.c index 29b467b61f..495d5078fe 100644 --- a/hw/misc/cbus.c +++ b/hw/misc/cbus.c @@ -135,9 +135,9 @@ CBus *cbus_init(qemu_irq dat) CBusPriv *s = (CBusPriv *) g_malloc0(sizeof(*s)); s->dat_out = dat; - s->cbus.clk = qemu_allocate_irqs(cbus_clk, s, 1)[0]; - s->cbus.dat = qemu_allocate_irqs(cbus_dat, s, 1)[0]; - s->cbus.sel = qemu_allocate_irqs(cbus_sel, s, 1)[0]; + s->cbus.clk = qemu_allocate_irq(cbus_clk, s, 0); + s->cbus.dat = qemu_allocate_irq(cbus_dat, s, 0); + s->cbus.sel = qemu_allocate_irq(cbus_sel, s, 0); s->sel = 1; s->clk = 0; diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c index f7f8a19ee8..355b0189c8 100644 --- a/hw/misc/vfio.c +++ b/hw/misc/vfio.c @@ -878,8 +878,20 @@ static void vfio_disable_msi_common(VFIODevice *vdev) static void vfio_disable_msix(VFIODevice *vdev) { + int i; + msix_unset_vector_notifiers(&vdev->pdev); + /* + * MSI-X will only release vectors if MSI-X is still enabled on the + * device, check through the rest and release it ourselves if necessary. + */ + for (i = 0; i < vdev->nr_vectors; i++) { + if (vdev->msi_vectors[i].use) { + vfio_msix_vector_release(&vdev->pdev, i); + } + } + if (vdev->nr_vectors) { vfio_disable_irqindex(vdev, VFIO_PCI_MSIX_IRQ_INDEX); } diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 9dd77f7571..0574d100b7 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -8,6 +8,7 @@ */ #include "hw/sysbus.h" #include "net/net.h" +#include "migration/migration.h" #include //#define DEBUG_STELLARIS_ENET 1 @@ -75,6 +76,7 @@ typedef struct { NICConf conf; qemu_irq irq; MemoryRegion mmio; + Error *migration_blocker; } stellaris_enet_state; static void stellaris_enet_update(stellaris_enet_state *s) @@ -252,17 +254,19 @@ static void stellaris_enet_write(void *opaque, hwaddr offset, s->tx_fifo[s->tx_fifo_len++] = value >> 24; } } else { - s->tx_fifo[s->tx_fifo_len++] = value; - s->tx_fifo[s->tx_fifo_len++] = value >> 8; - s->tx_fifo[s->tx_fifo_len++] = value >> 16; - s->tx_fifo[s->tx_fifo_len++] = value >> 24; + if (s->tx_fifo_len + 4 <= ARRAY_SIZE(s->tx_fifo)) { + s->tx_fifo[s->tx_fifo_len++] = value; + s->tx_fifo[s->tx_fifo_len++] = value >> 8; + s->tx_fifo[s->tx_fifo_len++] = value >> 16; + s->tx_fifo[s->tx_fifo_len++] = value >> 24; + } if (s->tx_fifo_len >= s->tx_frame_len) { /* We don't implement explicit CRC, so just chop it off. */ if ((s->tctl & SE_TCTL_CRC) == 0) s->tx_frame_len -= 4; if ((s->tctl & SE_TCTL_PADEN) && s->tx_frame_len < 60) { memset(&s->tx_fifo[s->tx_frame_len], 0, 60 - s->tx_frame_len); - s->tx_fifo_len = 60; + s->tx_frame_len = 60; } qemu_send_packet(qemu_get_queue(s->nic), s->tx_fifo, s->tx_frame_len); @@ -359,7 +363,7 @@ static int stellaris_enet_load(QEMUFile *f, void *opaque, int version_id) stellaris_enet_state *s = (stellaris_enet_state *)opaque; int i; - if (version_id != 1) + if (1) return -EINVAL; s->ris = qemu_get_be32(f); @@ -420,6 +424,10 @@ static int stellaris_enet_init(SysBusDevice *sbd) stellaris_enet_reset(s); register_savevm(dev, "stellaris_enet", -1, 1, stellaris_enet_save, stellaris_enet_load, s); + + error_setg(&s->migration_blocker, + "stellaris_enet does not support migration"); + migrate_add_blocker(s->migration_blocker); return 0; } @@ -427,6 +435,9 @@ static void stellaris_enet_unrealize(DeviceState *dev, Error **errp) { stellaris_enet_state *s = STELLARIS_ENET(dev); + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + unregister_savevm(DEVICE(s), "stellaris_enet", s); memory_region_destroy(&s->mmio); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index b75c753305..6246725614 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -515,6 +515,12 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) } vhost_net_ack_features(tap_get_vhost_net(nc->peer), features); } + + if ((1 << VIRTIO_NET_F_CTRL_VLAN) & features) { + memset(n->vlans, 0, MAX_VLAN >> 3); + } else { + memset(n->vlans, 0xff, MAX_VLAN >> 3); + } } static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, @@ -837,6 +843,14 @@ static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) return 1; } +static void virtio_net_hdr_swap(struct virtio_net_hdr *hdr) +{ + tswap16s(&hdr->hdr_len); + tswap16s(&hdr->gso_size); + tswap16s(&hdr->csum_start); + tswap16s(&hdr->csum_offset); +} + /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so * it never finds out that the packets don't have valid checksums. This * causes dhclient to get upset. Fedora's carried a patch for ages to @@ -872,6 +886,7 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); + virtio_net_hdr_swap(wbuf); iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1080,6 +1095,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) exit(1); } + if (n->has_vnet_hdr) { + if (out_sg[0].iov_len < n->guest_hdr_len) { + error_report("virtio-net header incorrect"); + exit(1); + } + virtio_net_hdr_swap((void *) out_sg[0].iov_base); + } + /* * If host wants to see the guest header as is, we can * pass it on unchanged. Otherwise, copy just the parts @@ -1336,10 +1359,17 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { qemu_get_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); - } else if (n->mac_table.in_use) { - uint8_t *buf = g_malloc0(n->mac_table.in_use); - qemu_get_buffer(f, buf, n->mac_table.in_use * ETH_ALEN); - g_free(buf); + } else { + int64_t i; + + /* Overflow detected - can happen if source has a larger MAC table. + * We simply set overflow flag so there's no need to maintain the + * table of addresses, discard them all. + * Note: 64 bit math to avoid integer overflow. + */ + for (i = 0; i < (int64_t)n->mac_table.in_use * ETH_ALEN; ++i) { + qemu_get_byte(f); + } n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; n->mac_table.in_use = 0; } @@ -1381,6 +1411,11 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) } n->curr_queues = qemu_get_be16(f); + if (n->curr_queues > n->max_queues) { + error_report("virtio-net: curr_queues %x > max_queues %x", + n->curr_queues, n->max_queues); + return -1; + } for (i = 1; i < n->curr_queues; i++) { n->vqs[i].tx_waiting = qemu_get_be32(f); } @@ -1570,16 +1605,15 @@ static int virtio_net_device_init(VirtIODevice *vdev) return 0; } -static int virtio_net_device_exit(DeviceState *qdev) +static void virtio_net_device_exit(VirtIODevice *vdev) { - VirtIONet *n = VIRTIO_NET(qdev); - VirtIODevice *vdev = VIRTIO_DEVICE(qdev); + VirtIONet *n = VIRTIO_NET(vdev); int i; /* This will stop vhost backend if appropriate. */ virtio_net_set_status(vdev, 0); - unregister_savevm(qdev, "virtio-net", n); + unregister_savevm(DEVICE(vdev), "virtio-net", n); if (n->netclient_name) { g_free(n->netclient_name); @@ -1610,8 +1644,6 @@ static int virtio_net_device_exit(DeviceState *qdev) g_free(n->vqs); qemu_del_nic(n->nic); virtio_cleanup(vdev); - - return 0; } static void virtio_net_instance_init(Object *obj) @@ -1638,10 +1670,10 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = virtio_net_device_exit; dc->props = virtio_net_properties; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); vdc->init = virtio_net_device_init; + vdc->exit = virtio_net_device_exit; vdc->get_config = virtio_net_get_config; vdc->set_config = virtio_net_set_config; vdc->get_features = virtio_net_get_features; diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 19687aa03c..bbb4fe526f 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -52,6 +52,9 @@ #define VMXNET3_DEVICE_VERSION 0x1 #define VMXNET3_DEVICE_REVISION 0x1 +/* Number of interrupt vectors for non-MSIx modes */ +#define VMXNET3_MAX_NMSIX_INTRS (1) + /* Macros for rings descriptors access */ #define VMXNET3_READ_TX_QUEUE_DESCR8(dpa, field) \ (vmw_shmem_ld8(dpa + offsetof(struct Vmxnet3_TxQueueDesc, field))) @@ -1305,6 +1308,51 @@ static bool vmxnet3_verify_intx(VMXNET3State *s, int intx) (pci_get_byte(s->parent_obj.config + PCI_INTERRUPT_PIN) - 1)); } +static void vmxnet3_validate_interrupt_idx(bool is_msix, int idx) +{ + int max_ints = is_msix ? VMXNET3_MAX_INTRS : VMXNET3_MAX_NMSIX_INTRS; + if (idx >= max_ints) { + hw_error("Bad interrupt index: %d\n", idx); + } +} + +static void vmxnet3_validate_interrupts(VMXNET3State *s) +{ + int i; + + VMW_CFPRN("Verifying event interrupt index (%d)", s->event_int_idx); + vmxnet3_validate_interrupt_idx(s->msix_used, s->event_int_idx); + + for (i = 0; i < s->txq_num; i++) { + int idx = s->txq_descr[i].intr_idx; + VMW_CFPRN("Verifying TX queue %d interrupt index (%d)", i, idx); + vmxnet3_validate_interrupt_idx(s->msix_used, idx); + } + + for (i = 0; i < s->rxq_num; i++) { + int idx = s->rxq_descr[i].intr_idx; + VMW_CFPRN("Verifying RX queue %d interrupt index (%d)", i, idx); + vmxnet3_validate_interrupt_idx(s->msix_used, idx); + } +} + +static void vmxnet3_validate_queues(VMXNET3State *s) +{ + /* + * txq_num and rxq_num are total number of queues + * configured by guest. These numbers must not + * exceed corresponding maximal values. + */ + + if (s->txq_num > VMXNET3_DEVICE_MAX_TX_QUEUES) { + hw_error("Bad TX queues number: %d\n", s->txq_num); + } + + if (s->rxq_num > VMXNET3_DEVICE_MAX_RX_QUEUES) { + hw_error("Bad RX queues number: %d\n", s->rxq_num); + } +} + static void vmxnet3_activate_device(VMXNET3State *s) { int i; @@ -1351,7 +1399,7 @@ static void vmxnet3_activate_device(VMXNET3State *s) VMXNET3_READ_DRV_SHARED8(s->drv_shmem, devRead.misc.numRxQueues); VMW_CFPRN("Number of TX/RX queues %u/%u", s->txq_num, s->rxq_num); - assert(s->txq_num <= VMXNET3_DEVICE_MAX_TX_QUEUES); + vmxnet3_validate_queues(s); qdescr_table_pa = VMXNET3_READ_DRV_SHARED64(s->drv_shmem, devRead.misc.queueDescPA); @@ -1447,6 +1495,8 @@ static void vmxnet3_activate_device(VMXNET3State *s) sizeof(s->rxq_descr[i].rxq_stats)); } + vmxnet3_validate_interrupts(s); + /* Make sure everything is in place before device activation */ smp_wmb(); @@ -2007,7 +2057,6 @@ vmxnet3_cleanup_msix(VMXNET3State *s) } } -#define VMXNET3_MSI_NUM_VECTORS (1) #define VMXNET3_MSI_OFFSET (0x50) #define VMXNET3_USE_64BIT (true) #define VMXNET3_PER_VECTOR_MASK (false) @@ -2018,7 +2067,7 @@ vmxnet3_init_msi(VMXNET3State *s) PCIDevice *d = PCI_DEVICE(s); int res; - res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MSI_NUM_VECTORS, + res = msi_init(d, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS, VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK); if (0 > res) { VMW_WRPRN("Failed to initialize MSI, error %d", res); @@ -2344,6 +2393,9 @@ static int vmxnet3_post_load(void *opaque, int version_id) } } + vmxnet3_validate_queues(s); + vmxnet3_validate_interrupts(s); + return 0; } diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index edc974ece3..4229d09acf 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -103,8 +103,6 @@ struct PCII440FXState { MemoryRegion *system_memory; MemoryRegion *pci_address_space; MemoryRegion *ram_memory; - MemoryRegion pci_hole; - MemoryRegion pci_hole_64bit; PAMMemoryRegion pam_regions[13]; MemoryRegion smram_region; uint8_t smm_enabled; @@ -313,8 +311,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, ram_addr_t ram_size, - hwaddr pci_hole_start, - hwaddr pci_hole_size, + ram_addr_t below_4g_mem_size, ram_addr_t above_4g_mem_size, MemoryRegion *pci_address_space, MemoryRegion *ram_memory) @@ -327,7 +324,6 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, PCII440FXState *f; unsigned i; I440FXState *i440fx; - uint64_t pci_hole64_size; dev = qdev_create(NULL, TYPE_I440FX_PCI_HOST_BRIDGE); s = PCI_HOST_BRIDGE(dev); @@ -345,33 +341,12 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, f->ram_memory = ram_memory; i440fx = I440FX_PCI_HOST_BRIDGE(dev); - /* Set PCI window size the way seabios has always done it. */ - /* Power of 2 so bios can cover it with a single MTRR */ - if (ram_size <= 0x80000000) { - i440fx->pci_info.w32.begin = 0x80000000; - } else if (ram_size <= 0xc0000000) { - i440fx->pci_info.w32.begin = 0xc0000000; - } else { - i440fx->pci_info.w32.begin = 0xe0000000; - } + i440fx->pci_info.w32.begin = below_4g_mem_size; - memory_region_init_alias(&f->pci_hole, OBJECT(d), "pci-hole", f->pci_address_space, - pci_hole_start, pci_hole_size); - memory_region_add_subregion(f->system_memory, pci_hole_start, &f->pci_hole); + /* setup pci memory mapping */ + pc_pci_as_mapping_init(OBJECT(f), f->system_memory, + f->pci_address_space); - pci_hole64_size = pci_host_get_hole64_size(i440fx->pci_hole64_size); - - pc_init_pci64_hole(&i440fx->pci_info, 0x100000000ULL + above_4g_mem_size, - pci_hole64_size); - memory_region_init_alias(&f->pci_hole_64bit, OBJECT(d), "pci-hole64", - f->pci_address_space, - i440fx->pci_info.w64.begin, - pci_hole64_size); - if (pci_hole64_size) { - memory_region_add_subregion(f->system_memory, - i440fx->pci_info.w64.begin, - &f->pci_hole_64bit); - } memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region", f->pci_address_space, 0xa0000, 0x20000); memory_region_add_subregion_overlap(f->system_memory, 0xa0000, diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index c043998e32..81c82404d6 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -356,28 +356,11 @@ static int mch_init(PCIDevice *d) { int i; MCHPCIState *mch = MCH_PCI_DEVICE(d); - uint64_t pci_hole64_size; - /* setup pci memory regions */ - memory_region_init_alias(&mch->pci_hole, OBJECT(mch), "pci-hole", - mch->pci_address_space, - mch->below_4g_mem_size, - 0x100000000ULL - mch->below_4g_mem_size); - memory_region_add_subregion(mch->system_memory, mch->below_4g_mem_size, - &mch->pci_hole); + /* setup pci memory mapping */ + pc_pci_as_mapping_init(OBJECT(mch), mch->system_memory, + mch->pci_address_space); - pci_hole64_size = pci_host_get_hole64_size(mch->pci_hole64_size); - pc_init_pci64_hole(&mch->pci_info, 0x100000000ULL + mch->above_4g_mem_size, - pci_hole64_size); - memory_region_init_alias(&mch->pci_hole_64bit, OBJECT(mch), "pci-hole64", - mch->pci_address_space, - mch->pci_info.w64.begin, - pci_hole64_size); - if (pci_hole64_size) { - memory_region_add_subregion(mch->system_memory, - mch->pci_info.w64.begin, - &mch->pci_hole_64bit); - } /* smram */ cpu_smm_register(&mch_set_smm, mch); memory_region_init_alias(&mch->smram_region, OBJECT(mch), "smram-region", diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 49eca955aa..e1bc1e3f9f 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -474,7 +474,7 @@ const VMStateDescription vmstate_pci_device = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_INT32_LE(version_id, PCIDevice), + VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, vmstate_info_pci_config, PCI_CONFIG_SPACE_SIZE), @@ -491,7 +491,7 @@ const VMStateDescription vmstate_pcie_device = { .minimum_version_id = 1, .minimum_version_id_old = 1, .fields = (VMStateField []) { - VMSTATE_INT32_LE(version_id, PCIDevice), + VMSTATE_INT32_POSITIVE_LE(version_id, PCIDevice), VMSTATE_BUFFER_UNSAFE_INFO(config, PCIDevice, 0, vmstate_info_pci_config, PCIE_CONFIG_SPACE_SIZE), @@ -820,6 +820,7 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, } pci_dev->bus = bus; + pci_dev->devfn = devfn; dma_as = pci_device_iommu_address_space(pci_dev); memory_region_init_alias(&pci_dev->bus_master_enable_region, @@ -829,7 +830,6 @@ static PCIDevice *do_pci_register_device(PCIDevice *pci_dev, PCIBus *bus, address_space_init(&pci_dev->bus_master_as, &pci_dev->bus_master_enable_region, name); - pci_dev->devfn = devfn; pstrcpy(pci_dev->name, sizeof(pci_dev->name), name); pci_dev->irq_state = 0; pci_config_alloc(pci_dev); diff --git a/hw/pci/pcie_aer.c b/hw/pci/pcie_aer.c index 991502e517..535be2c08a 100644 --- a/hw/pci/pcie_aer.c +++ b/hw/pci/pcie_aer.c @@ -795,6 +795,13 @@ static const VMStateDescription vmstate_pcie_aer_err = { } }; +static bool pcie_aer_state_log_num_valid(void *opaque, int version_id) +{ + PCIEAERLog *s = opaque; + + return s->log_num <= s->log_max; +} + const VMStateDescription vmstate_pcie_aer_log = { .name = "PCIE_AER_ERROR_LOG", .version_id = 1, @@ -802,7 +809,8 @@ const VMStateDescription vmstate_pcie_aer_log = { .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_UINT16(log_num, PCIEAERLog), - VMSTATE_UINT16(log_max, PCIEAERLog), + VMSTATE_UINT16_EQUAL(log_max, PCIEAERLog), + VMSTATE_VALIDATE("log_num <= log_max", pcie_aer_state_log_num_valid), VMSTATE_STRUCT_VARRAY_POINTER_UINT16(log, PCIEAERLog, log_num, vmstate_pcie_aer_err, PCIEAERErr), VMSTATE_END_OF_LIST() diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c index 8f17596cc3..4a126b3e04 100644 --- a/hw/pcmcia/pxa2xx.c +++ b/hw/pcmcia/pxa2xx.c @@ -195,7 +195,7 @@ static void pxa2xx_pcmcia_initfn(Object *obj) memory_region_add_subregion(&s->container_mem, 0x0c000000, &s->common_iomem); - s->slot.irq = qemu_allocate_irqs(pxa2xx_pcmcia_set_irq, s, 1)[0]; + s->slot.irq = qemu_allocate_irq(pxa2xx_pcmcia_set_irq, s, 0); object_property_add_link(obj, "card", TYPE_PCMCIA_CARD, (Object **)&s->card, NULL); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index edb4cb0413..0079d31195 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -342,6 +342,22 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPREnvironment *spapr, /* There is no cached config, allocate MSIs */ if (!phb->msi_table[ndev].nvec) { + int max_irqs = 0; + if (ret_intr_type == RTAS_TYPE_MSI) { + max_irqs = msi_nr_vectors_allocated(pdev); + } else if (ret_intr_type == RTAS_TYPE_MSIX) { + max_irqs = pdev->msix_entries_nr; + } + if (!max_irqs) { + fprintf(stderr, + "Requested interrupt type %d is not enabled for device#%d\n", + ret_intr_type, ndev); + rtas_st(rets, 0, -1); /* Hardware error */ + return; + } + if (req_num > max_irqs) { + req_num = max_irqs; + } irq = spapr_allocate_irq_block(req_num, false, ret_intr_type == RTAS_TYPE_MSI); if (irq < 0) { diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 101da63d04..ebe44ae6ec 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -722,9 +722,11 @@ out: return ret; } -static void copy_irb_to_guest(IRB *dest, const IRB *src) +static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw) { int i; + uint16_t stctl = src->scsw.ctrl & SCSW_CTRL_MASK_STCTL; + uint16_t actl = src->scsw.ctrl & SCSW_CTRL_MASK_ACTL; copy_scsw_to_guest(&dest->scsw, &src->scsw); @@ -734,8 +736,22 @@ static void copy_irb_to_guest(IRB *dest, const IRB *src) for (i = 0; i < ARRAY_SIZE(dest->ecw); i++) { dest->ecw[i] = cpu_to_be32(src->ecw[i]); } - for (i = 0; i < ARRAY_SIZE(dest->emw); i++) { - dest->emw[i] = cpu_to_be32(src->emw[i]); + /* extended measurements enabled? */ + if ((src->scsw.flags & SCSW_FLAGS_MASK_ESWF) || + !(pmcw->flags & PMCW_FLAGS_MASK_TF) || + !(pmcw->chars & PMCW_CHARS_MASK_XMWME)) { + return; + } + /* extended measurements pending? */ + if (!(stctl & SCSW_STCTL_STATUS_PEND)) { + return; + } + if ((stctl & SCSW_STCTL_PRIMARY) || + (stctl == SCSW_STCTL_SECONDARY) || + ((stctl & SCSW_STCTL_INTERMEDIATE) && (actl & SCSW_ACTL_SUSP))) { + for (i = 0; i < ARRAY_SIZE(dest->emw); i++) { + dest->emw[i] = cpu_to_be32(src->emw[i]); + } } } @@ -781,7 +797,7 @@ int css_do_tsch(SubchDev *sch, IRB *target_irb) } } /* Store the irb to the guest. */ - copy_irb_to_guest(target_irb, &irb); + copy_irb_to_guest(target_irb, &irb, p); /* Clear conditions on subchannel, if applicable. */ if (stctl & SCSW_STCTL_STATUS_PEND) { diff --git a/hw/s390x/s390-virtio-hcall.c b/hw/s390x/s390-virtio-hcall.c index ee626493c6..0e328d806d 100644 --- a/hw/s390x/s390-virtio-hcall.c +++ b/hw/s390x/s390-virtio-hcall.c @@ -26,11 +26,14 @@ void s390_register_virtio_hypercall(uint64_t code, s390_virtio_fn fn) int s390_virtio_hypercall(CPUS390XState *env) { - s390_virtio_fn fn = s390_diag500_table[env->regs[1]]; + s390_virtio_fn fn; - if (!fn) { - return -EINVAL; + if (env->regs[1] < MAX_DIAG_SUBCODES) { + fn = s390_diag500_table[env->regs[1]]; + if (fn) { + return fn(&env->regs[2]); + } } - return fn(&env->regs[2]); + return -EINVAL; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index f93a81c7cd..ecc80ecaf7 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -57,9 +57,10 @@ static const TypeInfo virtual_css_bus_info = { VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) { VirtIODevice *vdev = NULL; + VirtioCcwDevice *dev = sch->driver_data; - if (sch->driver_data) { - vdev = ((VirtioCcwDevice *)sch->driver_data)->vdev; + if (dev) { + vdev = virtio_bus_get_device(&dev->bus); } return vdev; } @@ -67,7 +68,8 @@ VirtIODevice *virtio_ccw_get_vdev(SubchDev *sch) static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n, bool assign, bool set_handler) { - VirtQueue *vq = virtio_get_queue(dev->vdev, n); + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + VirtQueue *vq = virtio_get_queue(vdev, n); EventNotifier *notifier = virtio_queue_get_host_notifier(vq); int r = 0; SubchDev *sch = dev->sch; @@ -97,6 +99,7 @@ static int virtio_ccw_set_guest2host_notifier(VirtioCcwDevice *dev, int n, static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) { + VirtIODevice *vdev; int n, r; if (!(dev->flags & VIRTIO_CCW_FLAG_USE_IOEVENTFD) || @@ -104,8 +107,9 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) dev->ioeventfd_started) { return; } + vdev = virtio_bus_get_device(&dev->bus); for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(dev->vdev, n)) { + if (!virtio_queue_get_num(vdev, n)) { continue; } r = virtio_ccw_set_guest2host_notifier(dev, n, true, true); @@ -118,7 +122,7 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) assign_error: while (--n >= 0) { - if (!virtio_queue_get_num(dev->vdev, n)) { + if (!virtio_queue_get_num(vdev, n)) { continue; } r = virtio_ccw_set_guest2host_notifier(dev, n, false, false); @@ -132,13 +136,15 @@ static void virtio_ccw_start_ioeventfd(VirtioCcwDevice *dev) static void virtio_ccw_stop_ioeventfd(VirtioCcwDevice *dev) { + VirtIODevice *vdev; int n, r; if (!dev->ioeventfd_started) { return; } + vdev = virtio_bus_get_device(&dev->bus); for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(dev->vdev, n)) { + if (!virtio_queue_get_num(vdev, n)) { continue; } r = virtio_ccw_set_guest2host_notifier(dev, n, false, false); @@ -189,7 +195,7 @@ typedef struct VirtioFeatDesc { static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, uint16_t index, uint16_t num) { - VirtioCcwDevice *dev = sch->driver_data; + VirtIODevice *vdev = virtio_ccw_get_vdev(sch); if (index > VIRTIO_PCI_QUEUE_MAX) { return -EINVAL; @@ -200,23 +206,23 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return -EINVAL; } - if (!dev) { + if (!vdev) { return -EINVAL; } - virtio_queue_set_addr(dev->vdev, index, addr); + virtio_queue_set_addr(vdev, index, addr); if (!addr) { - virtio_queue_set_vector(dev->vdev, index, 0); + virtio_queue_set_vector(vdev, index, 0); } else { /* Fail if we don't have a big enough queue. */ /* TODO: Add interface to handle vring.num changing */ - if (virtio_queue_get_num(dev->vdev, index) > num) { + if (virtio_queue_get_num(vdev, index) > num) { return -EINVAL; } - virtio_queue_set_vector(dev->vdev, index, index); + virtio_queue_set_vector(vdev, index, index); } /* tell notify handler in case of config change */ - dev->vdev->config_vector = VIRTIO_PCI_QUEUE_MAX; + vdev->config_vector = VIRTIO_PCI_QUEUE_MAX; return 0; } @@ -230,6 +236,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) hwaddr indicators; VqConfigBlock vq_config; VirtioCcwDevice *dev = sch->driver_data; + VirtIODevice *vdev = virtio_ccw_get_vdev(sch); bool check_len; int len; hwaddr hw_len; @@ -272,7 +279,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) break; case CCW_CMD_VDEV_RESET: virtio_ccw_stop_ioeventfd(dev); - virtio_reset(dev->vdev); + virtio_reset(vdev); ret = 0; break; case CCW_CMD_READ_FEAT: @@ -319,7 +326,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) features.features = ldl_le_phys(ccw.cda); if (features.index < ARRAY_SIZE(dev->host_features)) { virtio_bus_set_vdev_features(&dev->bus, features.features); - dev->vdev->guest_features = features.features; + vdev->guest_features = features.features; } else { /* * If the guest supports more feature bits, assert that it @@ -337,30 +344,30 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) break; case CCW_CMD_READ_CONF: if (check_len) { - if (ccw.count > dev->vdev->config_len) { + if (ccw.count > vdev->config_len) { ret = -EINVAL; break; } } - len = MIN(ccw.count, dev->vdev->config_len); + len = MIN(ccw.count, vdev->config_len); if (!ccw.cda) { ret = -EFAULT; } else { - virtio_bus_get_vdev_config(&dev->bus, dev->vdev->config); + virtio_bus_get_vdev_config(&dev->bus, vdev->config); /* XXX config space endianness */ - cpu_physical_memory_write(ccw.cda, dev->vdev->config, len); + cpu_physical_memory_write(ccw.cda, vdev->config, len); sch->curr_status.scsw.count = ccw.count - len; ret = 0; } break; case CCW_CMD_WRITE_CONF: if (check_len) { - if (ccw.count > dev->vdev->config_len) { + if (ccw.count > vdev->config_len) { ret = -EINVAL; break; } } - len = MIN(ccw.count, dev->vdev->config_len); + len = MIN(ccw.count, vdev->config_len); hw_len = len; if (!ccw.cda) { ret = -EFAULT; @@ -371,9 +378,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } else { len = hw_len; /* XXX config space endianness */ - memcpy(dev->vdev->config, config, len); + memcpy(vdev->config, config, len); cpu_physical_memory_unmap(config, hw_len, 0, hw_len); - virtio_bus_set_vdev_config(&dev->bus, dev->vdev->config); + virtio_bus_set_vdev_config(&dev->bus, vdev->config); sch->curr_status.scsw.count = ccw.count - len; ret = 0; } @@ -397,9 +404,9 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { virtio_ccw_stop_ioeventfd(dev); } - virtio_set_status(dev->vdev, status); - if (dev->vdev->status == 0) { - virtio_reset(dev->vdev); + virtio_set_status(vdev, status); + if (vdev->status == 0) { + virtio_reset(vdev); } if (status & VIRTIO_CONFIG_S_DRIVER_OK) { virtio_ccw_start_ioeventfd(dev); @@ -463,7 +470,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EFAULT; } else { vq_config.index = lduw_phys(ccw.cda); - vq_config.num_max = virtio_queue_get_num(dev->vdev, + vq_config.num_max = virtio_queue_get_num(vdev, vq_config.index); stw_phys(ccw.cda + sizeof(vq_config.index), vq_config.num_max); sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); @@ -495,7 +502,6 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->driver_data = dev; dev->sch = sch; - dev->vdev = vdev; dev->indicators = 0; /* Initialize subchannel structure. */ @@ -608,7 +614,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) memset(&sch->id, 0, sizeof(SenseId)); sch->id.reserved = 0xff; sch->id.cu_type = VIRTIO_CCW_CU_TYPE; - sch->id.cu_model = dev->vdev->device_id; + sch->id.cu_model = vdev->device_id; /* Only the first 32 feature bits are used. */ dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus, @@ -631,7 +637,6 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) { SubchDev *sch = dev->sch; - virtio_ccw_stop_ioeventfd(dev); if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); g_free(sch); @@ -892,9 +897,10 @@ static unsigned virtio_ccw_get_features(DeviceState *d) static void virtio_ccw_reset(DeviceState *d) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); virtio_ccw_stop_ioeventfd(dev); - virtio_reset(dev->vdev); + virtio_reset(vdev); css_reset_sch(dev->sch); dev->indicators = 0; dev->indicators2 = 0; @@ -934,9 +940,10 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign) static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, bool assign, bool with_irqfd) { - VirtQueue *vq = virtio_get_queue(dev->vdev, n); + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + VirtQueue *vq = virtio_get_queue(vdev, n); EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(dev->vdev); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); if (assign) { int r = event_notifier_init(notifier, 0); @@ -952,16 +959,16 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, * land in qemu (and only the irq fd) in this code. */ if (k->guest_notifier_mask) { - k->guest_notifier_mask(dev->vdev, n, false); + k->guest_notifier_mask(vdev, n, false); } /* get lost events and re-inject */ if (k->guest_notifier_pending && - k->guest_notifier_pending(dev->vdev, n)) { + k->guest_notifier_pending(vdev, n)) { event_notifier_set(notifier); } } else { if (k->guest_notifier_mask) { - k->guest_notifier_mask(dev->vdev, n, true); + k->guest_notifier_mask(vdev, n, true); } virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); event_notifier_cleanup(notifier); @@ -973,7 +980,7 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, bool assigned) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - VirtIODevice *vdev = dev->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); int r, n; for (n = 0; n < nvqs; n++) { @@ -1228,6 +1235,8 @@ static int virtio_ccw_busdev_unplug(DeviceState *dev) VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; SubchDev *sch = _dev->sch; + virtio_ccw_stop_ioeventfd(_dev); + /* * We should arrive here only for device_del, since we don't support * direct hot(un)plug of channels, but only through virtio. diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 96d6f5d5b7..00932c746d 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -77,7 +77,6 @@ typedef struct VirtIOCCWDeviceClass { struct VirtioCcwDevice { DeviceState parent_obj; SubchDev *sch; - VirtIODevice *vdev; char *bus_id; uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; VirtioBusState bus; diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 7c5a1a2b3a..dc09de365f 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -1101,6 +1101,21 @@ static int megasas_dcmd_ld_get_list(MegasasState *s, MegasasCmd *cmd) return MFI_STAT_OK; } +static int megasas_dcmd_ld_list_query(MegasasState *s, MegasasCmd *cmd) +{ + uint16_t flags; + + /* mbox0 contains flags */ + flags = le16_to_cpu(cmd->frame->dcmd.mbox[0]); + trace_megasas_dcmd_ld_list_query(cmd->index, flags); + if (flags == MR_LD_QUERY_TYPE_ALL || + flags == MR_LD_QUERY_TYPE_EXPOSED_TO_HOST) { + return megasas_dcmd_ld_get_list(s, cmd); + } + + return MFI_STAT_OK; +} + static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, MegasasCmd *cmd) { @@ -1404,6 +1419,8 @@ static const struct dcmd_cmd_tbl_t { megasas_dcmd_dummy }, { MFI_DCMD_LD_GET_LIST, "LD_GET_LIST", megasas_dcmd_ld_get_list}, + { MFI_DCMD_LD_LIST_QUERY, "LD_LIST_QUERY", + megasas_dcmd_ld_list_query }, { MFI_DCMD_LD_GET_INFO, "LD_GET_INFO", megasas_dcmd_ld_get_info }, { MFI_DCMD_LD_GET_PROP, "LD_GET_PROP", diff --git a/hw/scsi/mfi.h b/hw/scsi/mfi.h index cd8355badf..a3034f6239 100644 --- a/hw/scsi/mfi.h +++ b/hw/scsi/mfi.h @@ -164,6 +164,7 @@ typedef enum { MFI_DCMD_PD_BLINK = 0x02070100, MFI_DCMD_PD_UNBLINK = 0x02070200, MFI_DCMD_LD_GET_LIST = 0x03010000, + MFI_DCMD_LD_LIST_QUERY = 0x03010100, MFI_DCMD_LD_GET_INFO = 0x03020000, MFI_DCMD_LD_GET_PROP = 0x03030000, MFI_DCMD_LD_SET_PROP = 0x03040000, @@ -411,6 +412,14 @@ typedef enum { MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, /*query for system drives */ } mfi_pd_query_type; +typedef enum { + MR_LD_QUERY_TYPE_ALL = 0, + MR_LD_QUERY_TYPE_EXPOSED_TO_HOST = 1, + MR_LD_QUERY_TYPE_USED_TGT_IDS = 2, + MR_LD_QUERY_TYPE_CLUSTER_ACCESS = 3, + MR_LD_QUERY_TYPE_CLUSTER_LOCALE = 4, +} mfi_ld_query_type; + /* * Other propertities and definitions */ diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index ea916d1466..48286ef415 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -469,6 +469,8 @@ static int32_t scsi_target_send_command(SCSIRequest *req, uint8_t *buf) r->req.dev->sense_is_ua = false; } break; + case TEST_UNIT_READY: + break; default: scsi_req_build_sense(req, SENSE_CODE(LUN_NOT_SUPPORTED)); scsi_req_complete(req, CHECK_CONDITION); @@ -886,7 +888,6 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case RELEASE: case ERASE: case ALLOW_MEDIUM_REMOVAL: - case VERIFY_10: case SEEK_10: case SYNCHRONIZE_CACHE: case SYNCHRONIZE_CACHE_16: @@ -903,6 +904,16 @@ static int scsi_req_length(SCSICommand *cmd, SCSIDevice *dev, uint8_t *buf) case ALLOW_OVERWRITE: cmd->xfer = 0; break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + if ((buf[1] & 2) == 0) { + cmd->xfer = 0; + } else if ((buf[1] & 4) != 0) { + cmd->xfer = 1; + } + cmd->xfer *= dev->blocksize; + break; case MODE_SENSE: break; case WRITE_SAME_10: @@ -1100,6 +1111,9 @@ static void scsi_cmd_xfer_mode(SCSICommand *cmd) case WRITE_VERIFY_12: case WRITE_16: case WRITE_VERIFY_16: + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: case COPY: case COPY_VERIFY: case COMPARE: diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 74e6a14c29..06c31e9ec1 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -1597,6 +1597,14 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) scsi_disk_emulate_unmap(r, r->iov.iov_base); break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + if (r->req.status == -1) { + scsi_check_condition(r, SENSE_CODE(INVALID_FIELD)); + } + break; + default: abort(); } @@ -1837,6 +1845,14 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case UNMAP: DPRINTF("Unmap (len %lu)\n", (long)r->req.cmd.xfer); break; + case VERIFY_10: + case VERIFY_12: + case VERIFY_16: + DPRINTF("Verify (bytchk %lu)\n", (r->req.buf[1] >> 1) & 3); + if (req->cmd.buf[1] & 6) { + goto illegal_request; + } + break; case WRITE_SAME_10: case WRITE_SAME_16: nb_sectors = scsi_data_cdb_length(r->req.cmd.buf); @@ -1936,10 +1952,6 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); return 0; } - /* fallthrough */ - case VERIFY_10: - case VERIFY_12: - case VERIFY_16: DPRINTF("Write %s(sector %" PRId64 ", count %u)\n", (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); @@ -2169,6 +2181,7 @@ static const SCSIReqOps scsi_disk_emulate_reqops = { .send_command = scsi_disk_emulate_command, .read_data = scsi_disk_emulate_read_data, .write_data = scsi_disk_emulate_write_data, + .cancel_io = scsi_cancel_io, .get_buf = scsi_get_buf, }; @@ -2207,14 +2220,14 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [UNMAP] = &scsi_disk_emulate_reqops, [WRITE_SAME_10] = &scsi_disk_emulate_reqops, [WRITE_SAME_16] = &scsi_disk_emulate_reqops, + [VERIFY_10] = &scsi_disk_emulate_reqops, + [VERIFY_12] = &scsi_disk_emulate_reqops, + [VERIFY_16] = &scsi_disk_emulate_reqops, [READ_6] = &scsi_disk_dma_reqops, [READ_10] = &scsi_disk_dma_reqops, [READ_12] = &scsi_disk_dma_reqops, [READ_16] = &scsi_disk_dma_reqops, - [VERIFY_10] = &scsi_disk_dma_reqops, - [VERIFY_12] = &scsi_disk_dma_reqops, - [VERIFY_16] = &scsi_disk_dma_reqops, [WRITE_6] = &scsi_disk_dma_reqops, [WRITE_10] = &scsi_disk_dma_reqops, [WRITE_12] = &scsi_disk_dma_reqops, @@ -2359,7 +2372,7 @@ static SCSIRequest *scsi_block_new_request(SCSIDevice *d, uint32_t tag, * ones (such as WRITE SAME or EXTENDED COPY, etc.). So, without * O_DIRECT everything must go through SG_IO. */ - if (bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE) { + if (!(bdrv_get_flags(s->qdev.conf.bs) & BDRV_O_NOCACHE)) { break; } diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 8f195bec00..4967e477d8 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -37,8 +37,6 @@ do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) #include #include "block/scsi.h" -#define SCSI_SENSE_BUF_SIZE 96 - #define SG_ERR_DRIVER_TIMEOUT 0x06 #define SG_ERR_DRIVER_SENSE 0x08 diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index c0c46d7f7c..e8bca390dd 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -60,7 +60,6 @@ #define VSCSI_MAX_SECTORS 4096 #define VSCSI_REQ_LIMIT 24 -#define SCSI_SENSE_BUF_SIZE 96 #define SRP_RSP_SENSE_DATA_LEN 18 typedef union vscsi_crq { diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 9e770fba98..5e3cc614c9 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -240,11 +240,10 @@ static int vhost_scsi_init(VirtIODevice *vdev) return 0; } -static int vhost_scsi_exit(DeviceState *qdev) +static void vhost_scsi_exit(VirtIODevice *vdev) { - VirtIODevice *vdev = VIRTIO_DEVICE(qdev); - VHostSCSI *s = VHOST_SCSI(qdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev); + VHostSCSI *s = VHOST_SCSI(vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); migrate_del_blocker(s->migration_blocker); error_free(s->migration_blocker); @@ -253,7 +252,7 @@ static int vhost_scsi_exit(DeviceState *qdev) vhost_scsi_set_status(vdev, 0); g_free(s->dev.vqs); - return virtio_scsi_common_exit(vs); + virtio_scsi_common_exit(vs); } static Property vhost_scsi_properties[] = { @@ -265,10 +264,10 @@ static void vhost_scsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = vhost_scsi_exit; dc->props = vhost_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->init = vhost_scsi_init; + vdc->exit = vhost_scsi_exit; vdc->get_features = vhost_scsi_get_features; vdc->set_config = vhost_scsi_set_config; vdc->set_status = vhost_scsi_set_status; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 26d95a14ec..4f238c280b 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -147,6 +147,15 @@ static void *virtio_scsi_load_request(QEMUFile *f, SCSIRequest *sreq) qemu_get_be32s(f, &n); assert(n < vs->conf.num_queues); qemu_get_buffer(f, (unsigned char *)&req->elem, sizeof(req->elem)); + /* TODO: add a way for SCSIBusInfo's load_request to fail, + * and fail migration instead of asserting here. + * When we do, we might be able to re-enable NDEBUG below. + */ +#ifdef NDEBUG +#error building with NDEBUG is not supported +#endif + assert(req->elem.in_num <= ARRAY_SIZE(req->elem.in_sg)); + assert(req->elem.out_num <= ARRAY_SIZE(req->elem.out_sg)); virtio_scsi_parse_req(s, vs->cmd_vqs[n], req); scsi_req_ref(sreq); @@ -306,6 +315,10 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, VirtIOSCSIReq *req = r->hba_private; uint32_t sense_len; + if (r->io_canceled) { + return; + } + req->resp.cmd->response = VIRTIO_SCSI_S_OK; req->resp.cmd->status = status; if (req->resp.cmd->status == GOOD) { @@ -483,7 +496,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, uint32_t event, uint32_t reason) { VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); - VirtIOSCSIReq *req = virtio_scsi_pop_req(s, vs->event_vq); + VirtIOSCSIReq *req; VirtIOSCSIEvent *evt; VirtIODevice *vdev = VIRTIO_DEVICE(s); int in_size; @@ -492,6 +505,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, return; } + req = virtio_scsi_pop_req(s, vs->event_vq); if (!req) { s->events_dropped = true; return; @@ -516,7 +530,7 @@ static void virtio_scsi_push_event(VirtIOSCSI *s, SCSIDevice *dev, evt->event = event; evt->reason = reason; if (!dev) { - assert(event == VIRTIO_SCSI_T_NO_EVENT); + assert(event == VIRTIO_SCSI_T_EVENTS_MISSED); } else { evt->lun[0] = 1; evt->lun[1] = dev->id; @@ -644,22 +658,21 @@ static int virtio_scsi_device_init(VirtIODevice *vdev) return 0; } -int virtio_scsi_common_exit(VirtIOSCSICommon *vs) +void virtio_scsi_common_exit(VirtIOSCSICommon *vs) { VirtIODevice *vdev = VIRTIO_DEVICE(vs); g_free(vs->cmd_vqs); virtio_cleanup(vdev); - return 0; } -static int virtio_scsi_device_exit(DeviceState *qdev) +static void virtio_scsi_device_exit(VirtIODevice *vdev) { - VirtIOSCSI *s = VIRTIO_SCSI(qdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(qdev); + VirtIOSCSI *s = VIRTIO_SCSI(vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - unregister_savevm(qdev, "virtio-scsi", s); - return virtio_scsi_common_exit(vs); + unregister_savevm(DEVICE(vdev), "virtio-scsi", s); + virtio_scsi_common_exit(vs); } static Property virtio_scsi_properties[] = { @@ -680,10 +693,10 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = virtio_scsi_device_exit; dc->props = virtio_scsi_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->init = virtio_scsi_device_init; + vdc->exit = virtio_scsi_device_exit; vdc->set_config = virtio_scsi_set_config; vdc->get_features = virtio_scsi_get_features; vdc->reset = virtio_scsi_reset; diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index 937a47869a..6c92149c04 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -625,7 +625,7 @@ struct omap_mmc_s *omap2_mmc_init(struct omap_target_agent_s *ta, exit(1); } - s->cdet = qemu_allocate_irqs(omap_mmc_cover_cb, s, 1)[0]; + s->cdet = qemu_allocate_irq(omap_mmc_cover_cb, s, 0); sd_set_cb(s->card, NULL, s->cdet); return s; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 0906a1d62b..79a2b1d2ae 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1169,8 +1169,8 @@ static void sdhci_initfn(Object *obj) if (s->card == NULL) { exit(1); } - s->eject_cb = qemu_allocate_irqs(sdhci_insert_eject_cb, s, 1)[0]; - s->ro_cb = qemu_allocate_irqs(sdhci_card_readonly_cb, s, 1)[0]; + s->eject_cb = qemu_allocate_irq(sdhci_insert_eject_cb, s, 0); + s->ro_cb = qemu_allocate_irq(sdhci_card_readonly_cb, s, 0); sd_set_cb(s->card, s->ro_cb, s->eject_cb); s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s); @@ -1185,8 +1185,8 @@ static void sdhci_uninitfn(Object *obj) timer_free(s->insert_timer); timer_del(s->transfer_timer); timer_free(s->transfer_timer); - qemu_free_irqs(&s->eject_cb); - qemu_free_irqs(&s->ro_cb); + qemu_free_irq(s->eject_cb); + qemu_free_irq(s->ro_cb); if (s->fifo_buffer) { g_free(s->fifo_buffer); diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 1bb56c4d54..90ff07bf72 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -230,8 +230,17 @@ static int ssi_sd_load(QEMUFile *f, void *opaque, int version_id) for (i = 0; i < 5; i++) s->response[i] = qemu_get_be32(f); s->arglen = qemu_get_be32(f); + if (s->mode == SSI_SD_CMDARG && + (s->arglen < 0 || s->arglen >= ARRAY_SIZE(s->cmdarg))) { + return -EINVAL; + } s->response_pos = qemu_get_be32(f); s->stopping = qemu_get_be32(f); + if (s->mode == SSI_SD_RESPONSE && + (s->response_pos < 0 || s->response_pos >= ARRAY_SIZE(s->response) || + (!s->stopping && s->arglen > ARRAY_SIZE(s->response)))) { + return -EINVAL; + } ss->cs = qemu_get_be32(f); diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 1439ba44e5..6ad66c9f59 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -838,6 +838,5 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) qemu_irq sh7750_irl(SH7750State *s) { sh_intc_toggle_source(sh_intc_source(&s->intc, IRL), 1, 0); /* enable */ - return qemu_allocate_irqs(sh_intc_set_irl, sh_intc_source(&s->intc, IRL), - 1)[0]; + return qemu_allocate_irq(sh_intc_set_irl, sh_intc_source(&s->intc, IRL), 0); } diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index fd479effb9..b19bc7174a 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -240,11 +240,25 @@ static const MemoryRegionOps pl022_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static int pl022_post_load(void *opaque, int version_id) +{ + PL022State *s = opaque; + + if (s->tx_fifo_head < 0 || + s->tx_fifo_head >= ARRAY_SIZE(s->tx_fifo) || + s->rx_fifo_head < 0 || + s->rx_fifo_head >= ARRAY_SIZE(s->rx_fifo)) { + return -1; + } + return 0; +} + static const VMStateDescription vmstate_pl022 = { .name = "pl022_ssp", .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, + .post_load = pl022_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(cr0, PL022State), VMSTATE_UINT32(cr1, PL022State), diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index a47afde23a..fb0a45c889 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -320,6 +320,7 @@ static uint64_t icp_pit_read(void *opaque, hwaddr offset, n = offset >> 8; if (n > 2) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n); + return 0; } return arm_timer_read(s->timer[n], offset & 0xff); @@ -334,6 +335,7 @@ static void icp_pit_write(void *opaque, hwaddr offset, n = offset >> 8; if (n > 2) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad timer %d\n", __func__, n); + return; } arm_timer_write(s->timer[n], offset & 0xff, value); diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 2eb75ea945..60892dac8c 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -42,7 +42,6 @@ #define HPET_MSI_SUPPORT 0 -#define TYPE_HPET "hpet" #define HPET(obj) OBJECT_CHECK(HPETState, (obj), TYPE_HPET) struct HPETState; @@ -228,6 +227,18 @@ static int hpet_pre_load(void *opaque) return 0; } +static bool hpet_validate_num_timers(void *opaque, int version_id) +{ + HPETState *s = opaque; + + if (s->num_timers < HPET_MIN_TIMERS) { + return false; + } else if (s->num_timers > HPET_MAX_TIMERS) { + return false; + } + return true; +} + static int hpet_post_load(void *opaque, int version_id) { HPETState *s = opaque; @@ -296,6 +307,7 @@ static const VMStateDescription vmstate_hpet = { VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), VMSTATE_UINT8_V(num_timers, HPETState, 2), + VMSTATE_VALIDATE("num_timers in range", hpet_validate_num_timers), VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() @@ -757,11 +769,6 @@ static void hpet_device_class_init(ObjectClass *klass, void *data) dc->props = hpet_device_properties; } -bool hpet_find(void) -{ - return object_resolve_path_type("", TYPE_HPET, NULL); -} - static const TypeInfo hpet_device_info = { .name = TYPE_HPET, .parent = TYPE_SYS_BUS_DEVICE, diff --git a/hw/timer/omap_gptimer.c b/hw/timer/omap_gptimer.c index 016207f626..b7f3d49ca6 100644 --- a/hw/timer/omap_gptimer.c +++ b/hw/timer/omap_gptimer.c @@ -227,7 +227,7 @@ static void omap_gp_timer_clk_update(void *opaque, int line, int on) static void omap_gp_timer_clk_setup(struct omap_gp_timer_s *timer) { omap_clk_adduser(timer->clk, - qemu_allocate_irqs(omap_gp_timer_clk_update, timer, 1)[0]); + qemu_allocate_irq(omap_gp_timer_clk_update, timer, 0)); timer->rate = omap_clk_getrate(timer->clk); } @@ -476,7 +476,7 @@ struct omap_gp_timer_s *omap_gp_timer_init(struct omap_target_agent_s *ta, s->clk = fclk; s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_tick, s); s->match = timer_new_ns(QEMU_CLOCK_VIRTUAL, omap_gp_timer_match, s); - s->in = qemu_allocate_irqs(omap_gp_timer_input, s, 1)[0]; + s->in = qemu_allocate_irq(omap_gp_timer_input, s, 0); omap_gp_timer_reset(s); omap_gp_timer_clk_setup(s); diff --git a/hw/usb/bus.c b/hw/usb/bus.c index ca329bef29..53c85fe6ff 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -47,7 +47,9 @@ static int usb_device_post_load(void *opaque, int version_id) } else { dev->attached = 1; } - if (dev->setup_index >= sizeof(dev->data_buf) || + if (dev->setup_index < 0 || + dev->setup_len < 0 || + dev->setup_index >= sizeof(dev->data_buf) || dev->setup_len >= sizeof(dev->data_buf)) { return -EINVAL; } diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index 7f292b1ae6..43a9a6d514 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -19,6 +19,7 @@ */ #include "qemu-common.h" +#include "qemu/error-report.h" #include "hw/usb.h" #include "hw/usb/desc.h" #include "sysemu/bt.h" @@ -506,6 +507,14 @@ static int usb_bt_initfn(USBDevice *dev) usb_desc_create_serial(dev); usb_desc_init(dev); + s->dev.opaque = s; + if (!s->hci) { + s->hci = bt_new_hci(qemu_find_bt_vlan(0)); + } + s->hci->opaque = s; + s->hci->evt_recv = usb_bt_out_hci_packet_event; + s->hci->acl_recv = usb_bt_out_hci_packet_acl; + usb_bt_handle_reset(&s->dev); s->intr = usb_ep_get(dev, USB_TOKEN_IN, USB_EVT_EP); return 0; @@ -516,6 +525,7 @@ static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) USBDevice *dev; struct USBBtState *s; HCIInfo *hci; + const char *name = "usb-bt-dongle"; if (*cmdline) { hci = hci_init(cmdline); @@ -525,19 +535,17 @@ static USBDevice *usb_bt_init(USBBus *bus, const char *cmdline) if (!hci) return NULL; - dev = usb_create_simple(bus, "usb-bt-dongle"); + dev = usb_create(bus, name); if (!dev) { + error_report("Failed to create USB device '%s'", name); return NULL; } s = DO_UPCAST(struct USBBtState, dev, dev); - s->dev.opaque = s; - s->hci = hci; - s->hci->opaque = s; - s->hci->evt_recv = usb_bt_out_hci_packet_event; - s->hci->acl_recv = usb_bt_out_hci_packet_acl; - - usb_bt_handle_reset(&s->dev); + if (qdev_init(&dev->qdev) < 0) { + error_report("Failed to initialize USB device '%s'", name); + return NULL; + } return dev; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 9e336ad81e..1d349e0c72 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -309,7 +309,9 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, uint64_t size) { int i; - for (i = 0; i < dev->nvqs; ++i) { + int r = 0; + + for (i = 0; !r && i < dev->nvqs; ++i) { struct vhost_virtqueue *vq = dev->vqs + i; hwaddr l; void *p; @@ -321,15 +323,15 @@ static int vhost_verify_ring_mappings(struct vhost_dev *dev, p = cpu_physical_memory_map(vq->ring_phys, &l, 1); if (!p || l != vq->ring_size) { fprintf(stderr, "Unable to map ring buffer for ring %d\n", i); - return -ENOMEM; + r = -ENOMEM; } if (p != vq->ring) { fprintf(stderr, "Ring buffer relocated for ring %d\n", i); - return -EBUSY; + r = -EBUSY; } cpu_physical_memory_unmap(p, l, 0, 0); } - return 0; + return r; } static struct vhost_memory_region *vhost_dev_find_reg(struct vhost_dev *dev, diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 9504877120..d7a392db1d 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -370,16 +370,14 @@ static int virtio_balloon_device_init(VirtIODevice *vdev) return 0; } -static int virtio_balloon_device_exit(DeviceState *qdev) +static void virtio_balloon_device_exit(VirtIODevice *vdev) { - VirtIOBalloon *s = VIRTIO_BALLOON(qdev); - VirtIODevice *vdev = VIRTIO_DEVICE(qdev); + VirtIOBalloon *s = VIRTIO_BALLOON(vdev); balloon_stats_destroy_timer(s); qemu_remove_balloon_handler(s); - unregister_savevm(qdev, "virtio-balloon", s); + unregister_savevm(DEVICE(vdev), "virtio-balloon", s); virtio_cleanup(vdev); - return 0; } static Property virtio_balloon_properties[] = { @@ -390,10 +388,10 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = virtio_balloon_device_exit; dc->props = virtio_balloon_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->init = virtio_balloon_device_init; + vdc->exit = virtio_balloon_device_exit; vdc->get_config = virtio_balloon_get_config; vdc->set_config = virtio_balloon_set_config; vdc->get_features = virtio_balloon_get_features; diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index e6b103c991..eb77019267 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -37,8 +37,8 @@ do { printf("virtio_bus: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do { } while (0) #endif -/* Plug the VirtIODevice */ -int virtio_bus_plug_device(VirtIODevice *vdev) +/* A VirtIODevice is being plugged */ +int virtio_bus_device_plugged(VirtIODevice *vdev) { DeviceState *qdev = DEVICE(vdev); BusState *qbus = BUS(qdev_get_parent_bus(qdev)); @@ -46,8 +46,6 @@ int virtio_bus_plug_device(VirtIODevice *vdev) VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); DPRINTF("%s: plug device.\n", qbus->name); - bus->vdev = vdev; - if (klass->device_plugged != NULL) { klass->device_plugged(qbus->parent); } @@ -58,73 +56,83 @@ int virtio_bus_plug_device(VirtIODevice *vdev) /* Reset the virtio_bus */ void virtio_bus_reset(VirtioBusState *bus) { + VirtIODevice *vdev = virtio_bus_get_device(bus); + DPRINTF("%s: reset device.\n", qbus->name); - if (bus->vdev != NULL) { - virtio_reset(bus->vdev); + if (vdev != NULL) { + virtio_reset(vdev); } } -/* Destroy the VirtIODevice */ -void virtio_bus_destroy_device(VirtioBusState *bus) +/* A VirtIODevice is being unplugged */ +void virtio_bus_device_unplugged(VirtIODevice *vdev) { - BusState *qbus = BUS(bus); - VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(bus); + DeviceState *qdev = DEVICE(vdev); + BusState *qbus = BUS(qdev_get_parent_bus(qdev)); + VirtioBusClass *klass = VIRTIO_BUS_GET_CLASS(qbus); + DPRINTF("%s: remove device.\n", qbus->name); - if (bus->vdev != NULL) { - if (klass->device_unplug != NULL) { - klass->device_unplug(qbus->parent); + if (vdev != NULL) { + if (klass->device_unplugged != NULL) { + klass->device_unplugged(qbus->parent); } - object_unparent(OBJECT(bus->vdev)); - bus->vdev = NULL; } } /* Get the device id of the plugged device. */ uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus) { - assert(bus->vdev != NULL); - return bus->vdev->device_id; + VirtIODevice *vdev = virtio_bus_get_device(bus); + assert(vdev != NULL); + return vdev->device_id; } /* Get the config_len field of the plugged device. */ size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) { - assert(bus->vdev != NULL); - return bus->vdev->config_len; + VirtIODevice *vdev = virtio_bus_get_device(bus); + assert(vdev != NULL); + return vdev->config_len; } /* Get the features of the plugged device. */ uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, uint32_t requested_features) { + VirtIODevice *vdev = virtio_bus_get_device(bus); VirtioDeviceClass *k; - assert(bus->vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + + assert(vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(vdev); assert(k->get_features != NULL); - return k->get_features(bus->vdev, requested_features); + return k->get_features(vdev, requested_features); } /* Set the features of the plugged device. */ void virtio_bus_set_vdev_features(VirtioBusState *bus, uint32_t requested_features) { + VirtIODevice *vdev = virtio_bus_get_device(bus); VirtioDeviceClass *k; - assert(bus->vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + + assert(vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(vdev); if (k->set_features != NULL) { - k->set_features(bus->vdev, requested_features); + k->set_features(vdev, requested_features); } } /* Get bad features of the plugged device. */ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) { + VirtIODevice *vdev = virtio_bus_get_device(bus); VirtioDeviceClass *k; - assert(bus->vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + + assert(vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(vdev); if (k->bad_features != NULL) { - return k->bad_features(bus->vdev); + return k->bad_features(vdev); } else { return 0; } @@ -133,22 +141,26 @@ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) /* Get config of the plugged device. */ void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config) { + VirtIODevice *vdev = virtio_bus_get_device(bus); VirtioDeviceClass *k; - assert(bus->vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + + assert(vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(vdev); if (k->get_config != NULL) { - k->get_config(bus->vdev, config); + k->get_config(vdev, config); } } /* Set config of the plugged device. */ void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config) { + VirtIODevice *vdev = virtio_bus_get_device(bus); VirtioDeviceClass *k; - assert(bus->vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(bus->vdev); + + assert(vdev != NULL); + k = VIRTIO_DEVICE_GET_CLASS(vdev); if (k->set_config != NULL) { - k->set_config(bus->vdev, config); + k->set_config(vdev, config); } } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 29cf284d12..8829eb0e26 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -95,7 +95,7 @@ static void virtio_mmio_bus_new(VirtioBusState *bus, size_t bus_size, static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size) { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; - VirtIODevice *vdev = proxy->bus.vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset); @@ -185,7 +185,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque; - VirtIODevice *vdev = proxy->bus.vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n", (int)offset, value); @@ -298,12 +298,13 @@ static const MemoryRegionOps virtio_mem_ops = { static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector) { VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); int level; - if (!proxy->bus.vdev) { + if (!vdev) { return; } - level = (proxy->bus.vdev->isr != 0); + level = (vdev->isr != 0); DPRINTF("virtio_mmio setting IRQ %d\n", level); qemu_set_irq(proxy->irq, level); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 7647be8a3c..30c9f2b698 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -113,31 +113,40 @@ static inline VirtIOPCIProxy *to_virtio_pci_proxy_fast(DeviceState *d) static void virtio_pci_notify(DeviceState *d, uint16_t vector) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy_fast(d); + if (msix_enabled(&proxy->pci_dev)) msix_notify(&proxy->pci_dev, vector); - else - pci_set_irq(&proxy->pci_dev, proxy->vdev->isr & 1); + else { + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + pci_set_irq(&proxy->pci_dev, vdev->isr & 1); + } } static void virtio_pci_save_config(DeviceState *d, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + pci_device_save(&proxy->pci_dev, f); msix_save(&proxy->pci_dev, f); if (msix_present(&proxy->pci_dev)) - qemu_put_be16(f, proxy->vdev->config_vector); + qemu_put_be16(f, vdev->config_vector); } static void virtio_pci_save_queue(DeviceState *d, int n, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + if (msix_present(&proxy->pci_dev)) - qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n)); + qemu_put_be16(f, virtio_queue_vector(vdev, n)); } static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + int ret; ret = pci_device_load(&proxy->pci_dev, f); if (ret) { @@ -146,12 +155,12 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) msix_unuse_all_vectors(&proxy->pci_dev); msix_load(&proxy->pci_dev, f); if (msix_present(&proxy->pci_dev)) { - qemu_get_be16s(f, &proxy->vdev->config_vector); + qemu_get_be16s(f, &vdev->config_vector); } else { - proxy->vdev->config_vector = VIRTIO_NO_VECTOR; + vdev->config_vector = VIRTIO_NO_VECTOR; } - if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector); + if (vdev->config_vector != VIRTIO_NO_VECTOR) { + return msix_vector_use(&proxy->pci_dev, vdev->config_vector); } return 0; } @@ -159,13 +168,15 @@ static int virtio_pci_load_config(DeviceState *d, QEMUFile *f) static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + uint16_t vector; if (msix_present(&proxy->pci_dev)) { qemu_get_be16s(f, &vector); } else { vector = VIRTIO_NO_VECTOR; } - virtio_queue_set_vector(proxy->vdev, n, vector); + virtio_queue_set_vector(vdev, n, vector); if (vector != VIRTIO_NO_VECTOR) { return msix_vector_use(&proxy->pci_dev, vector); } @@ -175,7 +186,8 @@ static int virtio_pci_load_queue(DeviceState *d, int n, QEMUFile *f) static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, int n, bool assign, bool set_handler) { - VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtQueue *vq = virtio_get_queue(vdev, n); EventNotifier *notifier = virtio_queue_get_host_notifier(vq); int r = 0; @@ -200,6 +212,7 @@ static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) { + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); int n, r; if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || @@ -209,7 +222,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) } for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(proxy->vdev, n)) { + if (!virtio_queue_get_num(vdev, n)) { continue; } @@ -223,7 +236,7 @@ static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) assign_error: while (--n >= 0) { - if (!virtio_queue_get_num(proxy->vdev, n)) { + if (!virtio_queue_get_num(vdev, n)) { continue; } @@ -236,6 +249,7 @@ assign_error: static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) { + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); int r; int n; @@ -244,7 +258,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) } for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(proxy->vdev, n)) { + if (!virtio_queue_get_num(vdev, n)) { continue; } @@ -257,7 +271,7 @@ static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) { VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); hwaddr pa; switch (addr) { @@ -272,7 +286,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; if (pa == 0) { virtio_pci_stop_ioeventfd(proxy); - virtio_reset(proxy->vdev); + virtio_reset(vdev); msix_unuse_all_vectors(&proxy->pci_dev); } else @@ -299,7 +313,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) } if (vdev->status == 0) { - virtio_reset(proxy->vdev); + virtio_reset(vdev); msix_unuse_all_vectors(&proxy->pci_dev); } @@ -335,7 +349,7 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) { - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); uint32_t ret = 0xFFFFFFFF; switch (addr) { @@ -381,6 +395,7 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, unsigned size) { VirtIOPCIProxy *proxy = opaque; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); uint64_t val = 0; if (addr < config) { @@ -390,16 +405,16 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, switch (size) { case 1: - val = virtio_config_readb(proxy->vdev, addr); + val = virtio_config_readb(vdev, addr); break; case 2: - val = virtio_config_readw(proxy->vdev, addr); + val = virtio_config_readw(vdev, addr); if (virtio_is_big_endian()) { val = bswap16(val); } break; case 4: - val = virtio_config_readl(proxy->vdev, addr); + val = virtio_config_readl(vdev, addr); if (virtio_is_big_endian()) { val = bswap32(val); } @@ -413,6 +428,7 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr, { VirtIOPCIProxy *proxy = opaque; uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); if (addr < config) { virtio_ioport_write(proxy, addr, val); return; @@ -424,19 +440,19 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr, */ switch (size) { case 1: - virtio_config_writeb(proxy->vdev, addr, val); + virtio_config_writeb(vdev, addr, val); break; case 2: if (virtio_is_big_endian()) { val = bswap16(val); } - virtio_config_writew(proxy->vdev, addr, val); + virtio_config_writew(vdev, addr, val); break; case 4: if (virtio_is_big_endian()) { val = bswap32(val); } - virtio_config_writel(proxy->vdev, addr, val); + virtio_config_writel(vdev, addr, val); break; } } @@ -455,6 +471,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, uint32_t val, int len) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); pci_default_write_config(pci_dev, address, val, len); @@ -462,8 +479,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) && !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(proxy->vdev, - proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); + virtio_set_status(vdev, vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); } } @@ -506,7 +522,8 @@ static int kvm_virtio_pci_irqfd_use(VirtIOPCIProxy *proxy, unsigned int vector) { VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtQueue *vq = virtio_get_queue(vdev, queue_no); EventNotifier *n = virtio_queue_get_guest_notifier(vq); int ret; ret = kvm_irqchip_add_irqfd_notifier(kvm_state, n, NULL, irqfd->virq); @@ -517,7 +534,8 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, unsigned int queue_no, unsigned int vector) { - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtQueue *vq = virtio_get_queue(vdev, queue_no); EventNotifier *n = virtio_queue_get_guest_notifier(vq); VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; int ret; @@ -529,7 +547,7 @@ static void kvm_virtio_pci_irqfd_release(VirtIOPCIProxy *proxy, static int kvm_virtio_pci_vector_use(VirtIOPCIProxy *proxy, int nvqs) { PCIDevice *dev = &proxy->pci_dev; - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); unsigned int vector; int ret, queue_no; @@ -578,7 +596,7 @@ undo: static void kvm_virtio_pci_vector_release(VirtIOPCIProxy *proxy, int nvqs) { PCIDevice *dev = &proxy->pci_dev; - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); unsigned int vector; int queue_no; VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); @@ -606,8 +624,9 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, unsigned int vector, MSIMessage msg) { - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev); - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + VirtQueue *vq = virtio_get_queue(vdev, queue_no); EventNotifier *n = virtio_queue_get_guest_notifier(vq); VirtIOIRQFD *irqfd; int ret = 0; @@ -626,10 +645,10 @@ static int virtio_pci_vq_vector_unmask(VirtIOPCIProxy *proxy, * Otherwise, set it up now. */ if (k->guest_notifier_mask) { - k->guest_notifier_mask(proxy->vdev, queue_no, false); + k->guest_notifier_mask(vdev, queue_no, false); /* Test after unmasking to avoid losing events. */ if (k->guest_notifier_pending && - k->guest_notifier_pending(proxy->vdev, queue_no)) { + k->guest_notifier_pending(vdev, queue_no)) { event_notifier_set(n); } } else { @@ -642,13 +661,14 @@ static void virtio_pci_vq_vector_mask(VirtIOPCIProxy *proxy, unsigned int queue_no, unsigned int vector) { - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(proxy->vdev); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); /* If guest supports masking, keep irqfd but mask it. * Otherwise, clean it up now. */ if (k->guest_notifier_mask) { - k->guest_notifier_mask(proxy->vdev, queue_no, true); + k->guest_notifier_mask(vdev, queue_no, true); } else { kvm_virtio_pci_irqfd_release(proxy, queue_no, vector); } @@ -658,7 +678,7 @@ static int virtio_pci_vector_unmask(PCIDevice *dev, unsigned vector, MSIMessage msg) { VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); int ret, queue_no; for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { @@ -688,7 +708,7 @@ undo: static void virtio_pci_vector_mask(PCIDevice *dev, unsigned vector) { VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); int queue_no; for (queue_no = 0; queue_no < proxy->nvqs_with_notifiers; queue_no++) { @@ -707,7 +727,7 @@ static void virtio_pci_vector_poll(PCIDevice *dev, unsigned int vector_end) { VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); int queue_no; unsigned int vector; @@ -739,8 +759,9 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, bool with_irqfd) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(proxy->vdev); - VirtQueue *vq = virtio_get_queue(proxy->vdev, n); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); + VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + VirtQueue *vq = virtio_get_queue(vdev, n); EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); if (assign) { @@ -755,7 +776,7 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, } if (!msix_enabled(&proxy->pci_dev) && vdc->guest_notifier_mask) { - vdc->guest_notifier_mask(proxy->vdev, n, !assign); + vdc->guest_notifier_mask(vdev, n, !assign); } return 0; @@ -770,7 +791,7 @@ static bool virtio_pci_query_guest_notifiers(DeviceState *d) static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - VirtIODevice *vdev = proxy->vdev; + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); int r, n; bool with_irqfd = msix_enabled(&proxy->pci_dev) && @@ -864,11 +885,12 @@ static int virtio_pci_set_host_notifier(DeviceState *d, int n, bool assign) static void virtio_pci_vmstate_change(DeviceState *d, bool running) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); if (running) { /* Try to find out if the guest has bus master disabled, but is in ready state. Then we have a buggy guest OS. */ - if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && + if ((vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; } @@ -943,8 +965,6 @@ static void virtio_pci_device_plugged(DeviceState *d) uint8_t *config; uint32_t size; - proxy->vdev = bus->vdev; - config = proxy->pci_dev.config; if (proxy->class_code) { pci_config_set_class(config, proxy->class_code); @@ -982,6 +1002,15 @@ static void virtio_pci_device_plugged(DeviceState *d) proxy->host_features); } +static void virtio_pci_device_unplugged(DeviceState *d) +{ + PCIDevice *pci_dev = PCI_DEVICE(d); + VirtIOPCIProxy *proxy = VIRTIO_PCI(d); + + virtio_pci_stop_ioeventfd(proxy); + msix_uninit_exclusive_bar(pci_dev); +} + static int virtio_pci_init(PCIDevice *pci_dev) { VirtIOPCIProxy *dev = VIRTIO_PCI(pci_dev); @@ -996,9 +1025,7 @@ static int virtio_pci_init(PCIDevice *pci_dev) static void virtio_pci_exit(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); - virtio_pci_stop_ioeventfd(proxy); memory_region_destroy(&proxy->bar); - msix_uninit_exclusive_bar(pci_dev); } static void virtio_pci_reset(DeviceState *qdev) @@ -1533,6 +1560,7 @@ static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) k->set_guest_notifiers = virtio_pci_set_guest_notifiers; k->vmstate_change = virtio_pci_vmstate_change; k->device_plugged = virtio_pci_device_plugged; + k->device_unplugged = virtio_pci_device_unplugged; } static const TypeInfo virtio_pci_bus_info = { diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 917bcc5348..dc332ae774 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -82,7 +82,6 @@ typedef struct VirtioPCIClass { struct VirtIOPCIProxy { PCIDevice pci_dev; - VirtIODevice *vdev; MemoryRegion bar; uint32_t flags; uint32_t class_code; diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index b22ccf1008..42ca56843b 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -190,16 +190,14 @@ static int virtio_rng_device_init(VirtIODevice *vdev) return 0; } -static int virtio_rng_device_exit(DeviceState *qdev) +static void virtio_rng_device_exit(VirtIODevice *vdev) { - VirtIORNG *vrng = VIRTIO_RNG(qdev); - VirtIODevice *vdev = VIRTIO_DEVICE(qdev); + VirtIORNG *vrng = VIRTIO_RNG(vdev); timer_del(vrng->rate_limit_timer); timer_free(vrng->rate_limit_timer); - unregister_savevm(qdev, "virtio-rng", vrng); + unregister_savevm(DEVICE(vdev), "virtio-rng", vrng); virtio_cleanup(vdev); - return 0; } static Property virtio_rng_properties[] = { @@ -211,10 +209,10 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); - dc->exit = virtio_rng_device_exit; dc->props = virtio_rng_properties; set_bit(DEVICE_CATEGORY_MISC, dc->categories); vdc->init = virtio_rng_device_init; + vdc->exit = virtio_rng_device_exit; vdc->get_features = get_features; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2f1e73bc75..3fe91b74de 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -427,6 +427,12 @@ void virtqueue_map_sg(struct iovec *sg, hwaddr *addr, unsigned int i; hwaddr len; + if (num_sg > VIRTQUEUE_MAX_SIZE) { + error_report("virtio: map attempt out of bounds: %zd > %d", + num_sg, VIRTQUEUE_MAX_SIZE); + exit(1); + } + for (i = 0; i < num_sg; i++) { len = sg[i].iov_len; sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); @@ -888,7 +894,9 @@ int virtio_set_features(VirtIODevice *vdev, uint32_t val) int virtio_load(VirtIODevice *vdev, QEMUFile *f) { - int num, i, ret; + int i, ret; + int32_t config_len; + uint32_t num; uint32_t features; uint32_t supported_features; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -903,6 +911,9 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) qemu_get_8s(f, &vdev->status); qemu_get_8s(f, &vdev->isr); qemu_get_be16s(f, &vdev->queue_sel); + if (vdev->queue_sel >= VIRTIO_PCI_QUEUE_MAX) { + return -1; + } qemu_get_be32s(f, &features); if (virtio_set_features(vdev, features) < 0) { @@ -911,11 +922,27 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) features, supported_features); return -1; } - vdev->config_len = qemu_get_be32(f); - qemu_get_buffer(f, vdev->config, vdev->config_len); + config_len = qemu_get_be32(f); + + /* + * There are cases where the incoming config can be bigger or smaller + * than what we have; so load what we have space for, and skip + * any excess that's in the stream. + */ + qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len)); + + while (config_len > vdev->config_len) { + qemu_get_byte(f); + config_len--; + } num = qemu_get_be32(f); + if (num > VIRTIO_PCI_QUEUE_MAX) { + error_report("Invalid number of PCI queues: 0x%x", num); + return -1; + } + for (i = 0; i < num; i++) { vdev->vq[i].vring.num = qemu_get_be32(f); if (k->has_variable_vring_alignment) { @@ -1158,14 +1185,19 @@ static int virtio_device_init(DeviceState *qdev) if (k->init(vdev) < 0) { return -1; } - virtio_bus_plug_device(vdev); + virtio_bus_device_plugged(vdev); return 0; } static int virtio_device_exit(DeviceState *qdev) { VirtIODevice *vdev = VIRTIO_DEVICE(qdev); + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(qdev); + virtio_bus_device_unplugged(vdev); + if (k->exit) { + k->exit(vdev); + } if (vdev->bus_name) { g_free(vdev->bus_name); vdev->bus_name = NULL; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 09652fb22c..2a4a0947e6 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -128,17 +128,9 @@ PcGuestInfo *pc_guest_info_init(ram_addr_t below_4g_mem_size, #define PCI_HOST_PROP_PCI_HOLE64_SIZE "pci-hole64-size" #define DEFAULT_PCI_HOLE64_SIZE (~0x0ULL) -static inline uint64_t pci_host_get_hole64_size(uint64_t pci_hole64_size) -{ - if (pci_hole64_size == DEFAULT_PCI_HOLE64_SIZE) { - return 1ULL << 62; - } else { - return pci_hole64_size; - } -} -void pc_init_pci64_hole(PcPciInfo *pci_info, uint64_t pci_hole64_start, - uint64_t pci_hole64_size); +void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, + MemoryRegion *pci_address_space); FWCfgState *pc_memory_init(MemoryRegion *system_memory, const char *kernel_filename, @@ -187,8 +179,7 @@ PCIBus *i440fx_init(PCII440FXState **pi440fx_state, int *piix_devfn, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, ram_addr_t ram_size, - hwaddr pci_hole_start, - hwaddr pci_hole_size, + ram_addr_t below_4g_mem_size, ram_addr_t above_4g_mem_size, MemoryRegion *pci_memory, MemoryRegion *ram_memory); diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 309065fa41..d0355b712b 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -53,8 +53,6 @@ typedef struct MCHPCIState { MemoryRegion *address_space_io; PAMMemoryRegion pam_regions[13]; MemoryRegion smram_region; - MemoryRegion pci_hole; - MemoryRegion pci_hole_64bit; PcPciInfo pci_info; uint8_t smm_enabled; ram_addr_t below_4g_mem_size; diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index 76f6ac24a7..2e0554cf49 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -31,7 +31,7 @@ typedef struct SCSISense { uint8_t ascq; } SCSISense; -#define SCSI_SENSE_BUF_SIZE 96 +#define SCSI_SENSE_BUF_SIZE 252 struct SCSICommand { uint8_t buf[SCSI_CMD_BUF_SIZE]; diff --git a/include/hw/timer/hpet.h b/include/hw/timer/hpet.h index ab44bd31fd..773953be75 100644 --- a/include/hw/timer/hpet.h +++ b/include/hw/timer/hpet.h @@ -13,6 +13,8 @@ #ifndef QEMU_HPET_EMUL_H #define QEMU_HPET_EMUL_H +#include "qom/object.h" + #define HPET_BASE 0xfed00000 #define HPET_CLK_PERIOD 10000000ULL /* 10000000 femtoseconds == 10ns*/ @@ -72,5 +74,11 @@ struct hpet_fw_config extern struct hpet_fw_config hpet_cfg; -bool hpet_find(void); +#define TYPE_HPET "hpet" + +static inline bool hpet_find(void) +{ + return object_resolve_path_type("", TYPE_HPET, NULL); +} + #endif diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 9217f85abc..0756545d4d 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -61,7 +61,7 @@ typedef struct VirtioBusClass { * transport independent exit function. * This is called by virtio-bus just before the device is unplugged. */ - void (*device_unplug)(DeviceState *d); + void (*device_unplugged)(DeviceState *d); /* * Does the transport have variable vring alignment? * (ie can it ever call virtio_queue_set_align()?) @@ -72,15 +72,11 @@ typedef struct VirtioBusClass { struct VirtioBusState { BusState parent_obj; - /* - * Only one VirtIODevice can be plugged on the bus. - */ - VirtIODevice *vdev; }; -int virtio_bus_plug_device(VirtIODevice *vdev); +int virtio_bus_device_plugged(VirtIODevice *vdev); void virtio_bus_reset(VirtioBusState *bus); -void virtio_bus_destroy_device(VirtioBusState *bus); +void virtio_bus_device_unplugged(VirtIODevice *bus); /* Get the device id of the plugged device. */ uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus); /* Get the config_len field of the plugged device. */ @@ -98,4 +94,16 @@ void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config); /* Set config of the plugged device. */ void virtio_bus_set_vdev_config(VirtioBusState *bus, uint8_t *config); +static inline VirtIODevice *virtio_bus_get_device(VirtioBusState *bus) +{ + BusState *qbus = &bus->parent_obj; + BusChild *kid = QTAILQ_FIRST(&qbus->children); + DeviceState *qdev = kid ? kid->child : NULL; + + /* This is used on the data path, the cast is guaranteed + * to succeed by the qdev machinery. + */ + return (VirtIODevice *)qdev; +} + #endif /* VIRTIO_BUS_H */ diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index df60f16a3e..4b32440837 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -176,8 +176,8 @@ typedef struct VirtIONet { uint8_t nobcast; uint8_t vhost_started; struct { - int in_use; - int first_multi; + uint32_t in_use; + uint32_t first_multi; uint8_t multi_overflow; uint8_t uni_overflow; uint8_t *macs; diff --git a/include/hw/virtio/virtio-scsi.h b/include/hw/virtio/virtio-scsi.h index 9a985403c2..206c61dbfd 100644 --- a/include/hw/virtio/virtio-scsi.h +++ b/include/hw/virtio/virtio-scsi.h @@ -187,6 +187,6 @@ typedef struct { VIRTIO_SCSI_F_CHANGE, true) int virtio_scsi_common_init(VirtIOSCSICommon *vs); -int virtio_scsi_common_exit(VirtIOSCSICommon *vs); +void virtio_scsi_common_exit(VirtIOSCSICommon *vs); #endif /* _QEMU_VIRTIO_SCSI_H */ diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index a90522d6d6..59756c29b9 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -127,6 +127,7 @@ typedef struct VirtioDeviceClass { /* This is what a VirtioDevice must implement */ DeviceClass parent; int (*init)(VirtIODevice *vdev); + void (*exit)(VirtIODevice *vdev); uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); uint32_t (*bad_features)(VirtIODevice *vdev); void (*set_features)(VirtIODevice *vdev, uint32_t val); diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 9d09e60419..1325fa5343 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -100,6 +100,7 @@ enum VMStateFlags { VMS_MULTIPLY = 0x200, /* multiply "size" field by field_size */ VMS_VARRAY_UINT8 = 0x400, /* Array with size in uint8_t field*/ VMS_VARRAY_UINT32 = 0x800, /* Array with size in uint32_t field*/ + VMS_MUST_EXIST = 0x1000, /* Field must exist in input */ }; typedef struct { @@ -203,6 +204,14 @@ extern const VMStateInfo vmstate_info_bitmap; .offset = vmstate_offset_value(_state, _field, _type), \ } +/* Validate state using a boolean predicate. */ +#define VMSTATE_VALIDATE(_name, _test) { \ + .name = (_name), \ + .field_exists = (_test), \ + .flags = VMS_ARRAY | VMS_MUST_EXIST, \ + .num = 0, /* 0 elements: no data, only run _test */ \ +} + #define VMSTATE_POINTER(_field, _state, _version, _info, _type) { \ .name = (stringify(_field)), \ .version_id = (_version), \ @@ -582,7 +591,7 @@ extern const VMStateInfo vmstate_info_bitmap; #define VMSTATE_UINT64_EQUAL(_f, _s) \ VMSTATE_UINT64_EQUAL_V(_f, _s, 0) -#define VMSTATE_INT32_LE(_f, _s) \ +#define VMSTATE_INT32_POSITIVE_LE(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t) #define VMSTATE_UINT8_TEST(_f, _s, _t) \ diff --git a/include/qom/object.h b/include/qom/object.h index a275db2092..5f78847d68 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -358,7 +358,8 @@ struct ObjectClass Type type; GSList *interfaces; - const char *cast_cache[OBJECT_CLASS_CAST_CACHE]; + const char *object_cast_cache[OBJECT_CLASS_CAST_CACHE]; + const char *class_cast_cache[OBJECT_CLASS_CAST_CACHE]; ObjectUnparent *unparent; }; diff --git a/kvm-all.c b/kvm-all.c index 4478969ed2..7e05f08c2e 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -965,7 +965,7 @@ void kvm_init_irq_routing(KVMState *s) { int gsi_count, i; - gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING); + gsi_count = kvm_check_extension(s, KVM_CAP_IRQ_ROUTING) - 1; if (gsi_count > 0) { unsigned int gsi_bits, i; @@ -1431,16 +1431,22 @@ int kvm_init(void) nc++; } - s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0); - if (s->vmfd < 0) { + do { + ret = kvm_ioctl(s, KVM_CREATE_VM, 0); + } while (ret == -EINTR); + + if (ret < 0) { + fprintf(stderr, "ioctl(KVM_CREATE_VM) failed: %d %s\n", -ret, + strerror(-ret)); + #ifdef TARGET_S390X fprintf(stderr, "Please add the 'switch_amode' kernel parameter to " "your host kernel command line\n"); #endif - ret = s->vmfd; goto err; } + s->vmfd = ret; missing_cap = kvm_check_extension_list(s, kvm_required_capabilites); if (!missing_cap) { missing_cap = diff --git a/linux-user/aarch64/target_structs.h b/linux-user/aarch64/target_structs.h new file mode 100644 index 0000000000..21c1f2c074 --- /dev/null +++ b/linux-user/aarch64/target_structs.h @@ -0,0 +1,58 @@ +/* + * ARM AArch64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/alpha/target_structs.h b/linux-user/alpha/target_structs.h new file mode 100644 index 0000000000..50e7708ffd --- /dev/null +++ b/linux-user/alpha/target_structs.h @@ -0,0 +1,48 @@ +/* + * Alpha specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad1; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/arm/target_structs.h b/linux-user/arm/target_structs.h new file mode 100644 index 0000000000..f3c85d4e1f --- /dev/null +++ b/linux-user/arm/target_structs.h @@ -0,0 +1,52 @@ +/* + * ARM specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong __unused1; + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong __unused2; + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_ulong __unused3; + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/cris/target_structs.h b/linux-user/cris/target_structs.h new file mode 100644 index 0000000000..e4a1ffb3c1 --- /dev/null +++ b/linux-user/cris/target_structs.h @@ -0,0 +1,58 @@ +/* + * CRIS specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8dd424dadd..c9147b3ede 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -267,17 +267,15 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #ifdef TARGET_ARM +#ifndef TARGET_AARCH64 +/* 32 bit ARM definitions */ + #define ELF_START_MMAP 0x80000000 #define elf_check_arch(x) ((x) == ELF_MACHINE) #define ELF_ARCH ELF_MACHINE - -#ifdef TARGET_AARCH64 -#define ELF_CLASS ELFCLASS64 -#else #define ELF_CLASS ELFCLASS32 -#endif static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) @@ -285,10 +283,6 @@ static inline void init_thread(struct target_pt_regs *regs, abi_long stack = infop->start_stack; memset(regs, 0, sizeof(*regs)); -#ifdef TARGET_AARCH64 - regs->pc = infop->entry & ~0x3ULL; - regs->sp = stack; -#else regs->ARM_cpsr = 0x10; if (infop->entry & 1) regs->ARM_cpsr |= CPSR_T; @@ -302,7 +296,6 @@ static inline void init_thread(struct target_pt_regs *regs, /* For uClinux PIC binaries. */ /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ regs->ARM_r10 = infop->start_data; -#endif } #define ELF_NREG 18 @@ -346,10 +339,18 @@ enum ARM_HWCAP_ARM_EDSP = 1 << 7, ARM_HWCAP_ARM_JAVA = 1 << 8, ARM_HWCAP_ARM_IWMMXT = 1 << 9, - ARM_HWCAP_ARM_THUMBEE = 1 << 10, - ARM_HWCAP_ARM_NEON = 1 << 11, - ARM_HWCAP_ARM_VFPv3 = 1 << 12, - ARM_HWCAP_ARM_VFPv3D16 = 1 << 13, + ARM_HWCAP_ARM_CRUNCH = 1 << 10, + ARM_HWCAP_ARM_THUMBEE = 1 << 11, + ARM_HWCAP_ARM_NEON = 1 << 12, + ARM_HWCAP_ARM_VFPv3 = 1 << 13, + ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, + ARM_HWCAP_ARM_TLS = 1 << 15, + ARM_HWCAP_ARM_VFPv4 = 1 << 16, + ARM_HWCAP_ARM_IDIVA = 1 << 17, + ARM_HWCAP_ARM_IDIVT = 1 << 18, + ARM_HWCAP_ARM_VFPD32 = 1 << 19, + ARM_HWCAP_ARM_LPAE = 1 << 20, + ARM_HWCAP_ARM_EVTSTRM = 1 << 21, }; #define TARGET_HAS_VALIDATE_GUEST_SPACE @@ -424,23 +425,103 @@ static uint32_t get_elf_hwcap(void) hwcaps |= ARM_HWCAP_ARM_HALF; hwcaps |= ARM_HWCAP_ARM_THUMB; hwcaps |= ARM_HWCAP_ARM_FAST_MULT; - hwcaps |= ARM_HWCAP_ARM_FPA; /* probe for the extra features */ #define GET_FEATURE(feat, hwcap) \ do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) + /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ + GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); GET_FEATURE(ARM_FEATURE_VFP, ARM_HWCAP_ARM_VFP); GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPv3); - GET_FEATURE(ARM_FEATURE_VFP_FP16, ARM_HWCAP_ARM_VFPv3D16); + GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); + GET_FEATURE(ARM_FEATURE_VFP4, ARM_HWCAP_ARM_VFPv4); + GET_FEATURE(ARM_FEATURE_ARM_DIV, ARM_HWCAP_ARM_IDIVA); + GET_FEATURE(ARM_FEATURE_THUMB_DIV, ARM_HWCAP_ARM_IDIVT); + /* All QEMU's VFPv3 CPUs have 32 registers, see VFP_DREG in translate.c. + * Note that the ARM_HWCAP_ARM_VFPv3D16 bit is always the inverse of + * ARM_HWCAP_ARM_VFPD32 (and so always clear for QEMU); it is unrelated + * to our VFP_FP16 feature bit. + */ + GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPD32); + GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); #undef GET_FEATURE return hwcaps; } -#endif +#else +/* 64 bit ARM definitions */ +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ((x) == ELF_MACHINE) + +#define ELF_ARCH ELF_MACHINE +#define ELF_CLASS ELFCLASS64 +#define ELF_PLATFORM "aarch64" + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + abi_long stack = infop->start_stack; + memset(regs, 0, sizeof(*regs)); + + regs->pc = infop->entry & ~0x3ULL; + regs->sp = stack; +} + +#define ELF_NREG 34 +typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; + +static void elf_core_copy_regs(target_elf_gregset_t *regs, + const CPUARMState *env) +{ + int i; + + for (i = 0; i < 32; i++) { + (*regs)[i] = tswapreg(env->xregs[i]); + } + (*regs)[32] = tswapreg(env->pc); + (*regs)[33] = tswapreg(pstate_read((CPUARMState *)env)); +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +enum { + ARM_HWCAP_A64_FP = 1 << 0, + ARM_HWCAP_A64_ASIMD = 1 << 1, + ARM_HWCAP_A64_EVTSTRM = 1 << 2, + ARM_HWCAP_A64_AES = 1 << 3, + ARM_HWCAP_A64_PMULL = 1 << 4, + ARM_HWCAP_A64_SHA1 = 1 << 5, + ARM_HWCAP_A64_SHA2 = 1 << 6, + ARM_HWCAP_A64_CRC32 = 1 << 7, +}; + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + uint32_t hwcaps = 0; + + hwcaps |= ARM_HWCAP_A64_FP; + hwcaps |= ARM_HWCAP_A64_ASIMD; + + /* probe for the extra features */ +#define GET_FEATURE(feat, hwcap) \ + do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) + GET_FEATURE(ARM_FEATURE_V8_AES, ARM_HWCAP_A64_PMULL); +#undef GET_FEATURE + + return hwcaps; +} + +#endif /* not TARGET_AARCH64 */ +#endif /* TARGET_ARM */ #ifdef TARGET_UNICORE32 diff --git a/linux-user/i386/target_structs.h b/linux-user/i386/target_structs.h new file mode 100644 index 0000000000..65f535e16b --- /dev/null +++ b/linux-user/i386/target_structs.h @@ -0,0 +1,58 @@ +/* + * i386 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/m68k/target_structs.h b/linux-user/m68k/target_structs.h new file mode 100644 index 0000000000..de257c97de --- /dev/null +++ b/linux-user/m68k/target_structs.h @@ -0,0 +1,58 @@ +/* + * m68k specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/microblaze/target_structs.h b/linux-user/microblaze/target_structs.h new file mode 100644 index 0000000000..325e2f6d4d --- /dev/null +++ b/linux-user/microblaze/target_structs.h @@ -0,0 +1,58 @@ +/* + * MicroBlaze specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/mips/target_structs.h b/linux-user/mips/target_structs.h new file mode 100644 index 0000000000..16021e8a94 --- /dev/null +++ b/linux-user/mips/target_structs.h @@ -0,0 +1,48 @@ +/* + * MIPS specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad1; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/mips64/target_cpu.h b/linux-user/mips64/target_cpu.h index fa36407c68..f16991b4ef 100644 --- a/linux-user/mips64/target_cpu.h +++ b/linux-user/mips64/target_cpu.h @@ -1 +1,19 @@ +/* + * MIPS64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ #include "../mips/target_cpu.h" diff --git a/linux-user/mips64/target_structs.h b/linux-user/mips64/target_structs.h new file mode 100644 index 0000000000..a4f619e732 --- /dev/null +++ b/linux-user/mips64/target_structs.h @@ -0,0 +1,2 @@ +#include "../mips/target_structs.h" + diff --git a/linux-user/openrisc/target_structs.h b/linux-user/openrisc/target_structs.h new file mode 100644 index 0000000000..f4d560f575 --- /dev/null +++ b/linux-user/openrisc/target_structs.h @@ -0,0 +1,58 @@ +/* + * OpenRISC specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/ppc/target_structs.h b/linux-user/ppc/target_structs.h new file mode 100644 index 0000000000..2b87613104 --- /dev/null +++ b/linux-user/ppc/target_structs.h @@ -0,0 +1,60 @@ +/* + * PowerPC specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_uint mode; /* Read/write permission. */ + uint32_t __seq; /* Sequence number. */ + uint32_t __pad1; + uint64_t __unused1; + uint64_t __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused1; +#endif + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused2; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused3; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_uint __unused4; +#endif + abi_long shm_segsz; /* size of segment in bytes */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused5; + abi_ulong __unused6; +}; + +#endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index da64e877c7..e2717e0775 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -452,5 +452,6 @@ static inline void *lock_user_string(abi_ulong guest_addr) */ #include "target_cpu.h" #include "target_signal.h" +#include "target_structs.h" #endif /* QEMU_H */ diff --git a/linux-user/s390x/target_structs.h b/linux-user/s390x/target_structs.h new file mode 100644 index 0000000000..6b6f5b5212 --- /dev/null +++ b/linux-user/s390x/target_structs.h @@ -0,0 +1,63 @@ +/* + * S/390 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ +#if TARGET_ABI_BITS == 64 + abi_uint mode; /* Read/write permission. */ +#else + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; +#endif + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/sh4/target_structs.h b/linux-user/sh4/target_structs.h new file mode 100644 index 0000000000..32b235e0b3 --- /dev/null +++ b/linux-user/sh4/target_structs.h @@ -0,0 +1,58 @@ +/* + * SH4 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/signal.c b/linux-user/signal.c index 7751c47ef1..544e77eac2 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -3653,7 +3653,7 @@ struct target_sigcontext { struct target_signal_frame { struct target_sigcontext sc; uint32_t extramask[TARGET_NSIG_WORDS - 1]; - uint8_t retcode[8]; /* Trampoline code. */ + uint16_t retcode[4]; /* Trampoline code. */ }; struct rt_signal_frame { @@ -3661,7 +3661,7 @@ struct rt_signal_frame { void *puc; siginfo_t info; struct ucontext uc; - uint8_t retcode[8]; /* Trampoline code. */ + uint16_t retcode[4]; /* Trampoline code. */ }; static void setup_sigcontext(struct target_sigcontext *sc, CPUCRISState *env) @@ -3739,8 +3739,8 @@ static void setup_frame(int sig, struct target_sigaction *ka, */ err |= __put_user(0x9c5f, frame->retcode+0); err |= __put_user(TARGET_NR_sigreturn, - frame->retcode+2); - err |= __put_user(0xe93d, frame->retcode+4); + frame->retcode + 1); + err |= __put_user(0xe93d, frame->retcode + 2); /* Save the mask. */ err |= __put_user(set->sig[0], &frame->sc.oldmask); diff --git a/linux-user/sparc/target_structs.h b/linux-user/sparc/target_structs.h new file mode 100644 index 0000000000..c139e09a61 --- /dev/null +++ b/linux-user/sparc/target_structs.h @@ -0,0 +1,63 @@ +/* + * SPARC specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ +#if TARGET_ABI_BITS == 32 + abi_ushort __pad1; + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad2; +#else + abi_ushort mode; + abi_ushort __pad1; +#endif + abi_ushort __seq; /* Sequence number. */ + uint64_t __unused1; + uint64_t __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ +#if TARGET_ABI_BITS == 32 + abi_uint __pad1; +#endif + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_uint __pad2; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_uint __pad3; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_cpid; /* pid of creator */ + abi_ulong shm_lpid; /* pid of last shmop */ + abi_long shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/sparc64/target_structs.h b/linux-user/sparc64/target_structs.h new file mode 100644 index 0000000000..fc1729007d --- /dev/null +++ b/linux-user/sparc64/target_structs.h @@ -0,0 +1,58 @@ +/* + * SPARC64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/syscall.c b/linux-user/syscall.c index eaaf00ddd0..de8918d629 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2417,21 +2417,6 @@ static struct shm_region { abi_ulong size; } shm_regions[N_SHM_REGIONS]; -struct target_ipc_perm -{ - abi_long __key; - abi_ulong uid; - abi_ulong gid; - abi_ulong cuid; - abi_ulong cgid; - unsigned short int mode; - unsigned short int __pad1; - unsigned short int __seq; - unsigned short int __pad2; - abi_ulong __unused1; - abi_ulong __unused2; -}; - struct target_semid_ds { struct target_ipc_perm sem_perm; @@ -2453,12 +2438,21 @@ static inline abi_long target_to_host_ipc_perm(struct ipc_perm *host_ip, if (!lock_user_struct(VERIFY_READ, target_sd, target_addr, 1)) return -TARGET_EFAULT; target_ip = &(target_sd->sem_perm); - host_ip->__key = tswapal(target_ip->__key); - host_ip->uid = tswapal(target_ip->uid); - host_ip->gid = tswapal(target_ip->gid); - host_ip->cuid = tswapal(target_ip->cuid); - host_ip->cgid = tswapal(target_ip->cgid); + host_ip->__key = tswap32(target_ip->__key); + host_ip->uid = tswap32(target_ip->uid); + host_ip->gid = tswap32(target_ip->gid); + host_ip->cuid = tswap32(target_ip->cuid); + host_ip->cgid = tswap32(target_ip->cgid); +#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_PPC) + host_ip->mode = tswap32(target_ip->mode); +#else host_ip->mode = tswap16(target_ip->mode); +#endif +#if defined(TARGET_PPC) + host_ip->__seq = tswap32(target_ip->__seq); +#else + host_ip->__seq = tswap16(target_ip->__seq); +#endif unlock_user_struct(target_sd, target_addr, 0); return 0; } @@ -2472,12 +2466,21 @@ static inline abi_long host_to_target_ipc_perm(abi_ulong target_addr, if (!lock_user_struct(VERIFY_WRITE, target_sd, target_addr, 0)) return -TARGET_EFAULT; target_ip = &(target_sd->sem_perm); - target_ip->__key = tswapal(host_ip->__key); - target_ip->uid = tswapal(host_ip->uid); - target_ip->gid = tswapal(host_ip->gid); - target_ip->cuid = tswapal(host_ip->cuid); - target_ip->cgid = tswapal(host_ip->cgid); + target_ip->__key = tswap32(host_ip->__key); + target_ip->uid = tswap32(host_ip->uid); + target_ip->gid = tswap32(host_ip->gid); + target_ip->cuid = tswap32(host_ip->cuid); + target_ip->cgid = tswap32(host_ip->cgid); +#if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_PPC) + target_ip->mode = tswap32(host_ip->mode); +#else target_ip->mode = tswap16(host_ip->mode); +#endif +#if defined(TARGET_PPC) + target_ip->__seq = tswap32(host_ip->__seq); +#else + target_ip->__seq = tswap16(host_ip->__seq); +#endif unlock_user_struct(target_sd, target_addr, 1); return 0; } @@ -2908,29 +2911,6 @@ end: return ret; } -struct target_shmid_ds -{ - struct target_ipc_perm shm_perm; - abi_ulong shm_segsz; - abi_ulong shm_atime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused1; -#endif - abi_ulong shm_dtime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused2; -#endif - abi_ulong shm_ctime; -#if TARGET_ABI_BITS == 32 - abi_ulong __unused3; -#endif - int shm_cpid; - int shm_lpid; - abi_ulong shm_nattch; - unsigned long int __unused4; - unsigned long int __unused5; -}; - static inline abi_long target_to_host_shmid_ds(struct shmid_ds *host_sd, abi_ulong target_addr) { @@ -3216,7 +3196,7 @@ static abi_long do_ipc(unsigned int call, int first, /* IPC_* and SHM_* command values are the same on all linux platforms */ case IPCOP_shmctl: - ret = do_shmctl(first, second, third); + ret = do_shmctl(first, second, ptr); break; default: gemu_log("Unsupported ipc call: %d (version %d)\n", call, version); @@ -7499,6 +7479,22 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(sys_sched_getaffinity(arg1, mask_size, mask)); if (!is_error(ret)) { + if (ret > arg2) { + /* More data returned than the caller's buffer will fit. + * This only happens if sizeof(abi_long) < sizeof(long) + * and the caller passed us a buffer holding an odd number + * of abi_longs. If the host kernel is actually using the + * extra 4 bytes then fail EINVAL; otherwise we can just + * ignore them and only copy the interesting part. + */ + int numcpus = sysconf(_SC_NPROCESSORS_CONF); + if (numcpus > arg2 * 8) { + ret = -TARGET_EINVAL; + break; + } + ret = arg2; + } + if (copy_to_user(arg3, mask, ret)) { goto efault; } diff --git a/linux-user/unicore32/target_structs.h b/linux-user/unicore32/target_structs.h new file mode 100644 index 0000000000..789369503b --- /dev/null +++ b/linux-user/unicore32/target_structs.h @@ -0,0 +1,58 @@ +/* + * UniCore32 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/linux-user/x86_64/target_structs.h b/linux-user/x86_64/target_structs.h new file mode 100644 index 0000000000..d934056149 --- /dev/null +++ b/linux-user/x86_64/target_structs.h @@ -0,0 +1,58 @@ +/* + * X86-64 specific structures for linux-user + * + * Copyright (c) 2013 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TARGET_STRUCTS_H +#define TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad1; + abi_ushort __seq; /* Sequence number. */ + abi_ushort __pad2; + abi_ulong __unused1; + abi_ulong __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_long shm_segsz; /* size of segment in bytes */ + abi_ulong shm_atime; /* time of last shmat() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused1; +#endif + abi_ulong shm_dtime; /* time of last shmdt() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused2; +#endif + abi_ulong shm_ctime; /* time of last change by shmctl() */ +#if TARGET_ABI_BITS == 32 + abi_ulong __unused3; +#endif + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused4; + abi_ulong __unused5; +}; + +#endif diff --git a/memory.c b/memory.c index 6690c48004..6d479721b5 100644 --- a/memory.c +++ b/memory.c @@ -1632,6 +1632,7 @@ MemoryRegionSection memory_region_find(MemoryRegion *mr, view = address_space_get_flatview(as); fr = flatview_lookup(view, range); if (!fr) { + flatview_unref(view); return ret; } diff --git a/migration-rdma.c b/migration-rdma.c index f94f3b4e3a..29351a6056 100644 --- a/migration-rdma.c +++ b/migration-rdma.c @@ -1589,13 +1589,11 @@ static int qemu_rdma_post_send_control(RDMAContext *rdma, uint8_t *buf, } - if (ibv_post_send(rdma->qp, &send_wr, &bad_wr)) { - return -1; - } + ret = ibv_post_send(rdma->qp, &send_wr, &bad_wr); - if (ret < 0) { + if (ret > 0) { fprintf(stderr, "Failed to use post IB SEND for control!\n"); - return ret; + return -ret; } ret = qemu_rdma_block_for_wrid(rdma, RDMA_WRID_SEND_CONTROL, NULL); @@ -2237,10 +2235,6 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) } } - if (rdma->qp) { - rdma_destroy_qp(rdma->cm_id); - rdma->qp = NULL; - } if (rdma->cq) { ibv_destroy_cq(rdma->cq); rdma->cq = NULL; @@ -2258,6 +2252,10 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma->listen_id = NULL; } if (rdma->cm_id) { + if (rdma->qp) { + rdma_destroy_qp(rdma->cm_id); + rdma->qp = NULL; + } rdma_destroy_id(rdma->cm_id); rdma->cm_id = NULL; } @@ -2512,8 +2510,10 @@ static void *qemu_rdma_data_init(const char *host_port, Error **errp) } else { ERROR(errp, "bad RDMA migration address '%s'", host_port); g_free(rdma); - return NULL; + rdma = NULL; } + + qapi_free_InetSocketAddress(addr); } return rdma; diff --git a/migration.c b/migration.c index 2b1ab20c54..22a1399290 100644 --- a/migration.c +++ b/migration.c @@ -105,7 +105,7 @@ static void process_incoming_migration_co(void *opaque) ret = qemu_loadvm_state(f); qemu_fclose(f); if (ret < 0) { - fprintf(stderr, "load of migration failed\n"); + error_report("load of migration failed: %s", strerror(-ret)); exit(EXIT_FAILURE); } qemu_announce_self(); @@ -437,6 +437,7 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, #endif } else { error_set(errp, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); + s->state = MIG_STATE_ERROR; return; } @@ -583,7 +584,7 @@ static void *migration_thread(void *opaque) ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); if (ret >= 0) { - qemu_file_set_rate_limit(s->file, INT_MAX); + qemu_file_set_rate_limit(s->file, INT64_MAX); qemu_savevm_state_complete(s->file); } qemu_mutex_unlock_iothread(); diff --git a/nbd.c b/nbd.c index f847940f3e..d0974579ae 100644 --- a/nbd.c +++ b/nbd.c @@ -1069,6 +1069,7 @@ static void nbd_trip(void *opaque) struct nbd_request request; struct nbd_reply reply; ssize_t ret; + uint32_t command; TRACE("Reading request."); if (client->closing) { @@ -1091,8 +1092,8 @@ static void nbd_trip(void *opaque) reply.error = -ret; goto error_reply; } - - if ((request.from + request.len) > exp->size) { + command = request.type & NBD_CMD_MASK_COMMAND; + if (command != NBD_CMD_DISC && (request.from + request.len) > exp->size) { LOG("From: %" PRIu64 ", Len: %u, Size: %" PRIu64 ", Offset: %" PRIu64 "\n", request.from, request.len, @@ -1101,7 +1102,7 @@ static void nbd_trip(void *opaque) goto invalid_request; } - switch (request.type & NBD_CMD_MASK_COMMAND) { + switch (command) { case NBD_CMD_READ: TRACE("Request type is READ"); diff --git a/net/tap.c b/net/tap.c index 39c1cda3e4..6b87a73960 100644 --- a/net/tap.c +++ b/net/tap.c @@ -190,7 +190,7 @@ static void tap_send(void *opaque) TAPState *s = opaque; int size; - do { + while (qemu_can_send_packet(&s->nc)) { uint8_t *buf = s->buf; size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); @@ -206,8 +206,11 @@ static void tap_send(void *opaque) size = qemu_send_packet_async(&s->nc, buf, size, tap_send_completed); if (size == 0) { tap_read_poll(s, false); + break; + } else if (size < 0) { + break; } - } while (size > 0 && qemu_can_send_packet(&s->nc)); + } } bool tap_has_ufo(NetClientState *nc) diff --git a/pc-bios/QEMU,tcx.bin b/pc-bios/QEMU,tcx.bin index a8ddd70ef3..eed108f3f1 100644 Binary files a/pc-bios/QEMU,tcx.bin and b/pc-bios/QEMU,tcx.bin differ diff --git a/pc-bios/README b/pc-bios/README index a110125a97..f1900686dc 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -12,7 +12,7 @@ 1275-1994 (referred to as Open Firmware) compliant firmware. The included images for PowerPC (for 32 and 64 bit PPC CPUs), Sparc32 (including QEMU,tcx.bin) and Sparc64 are built from OpenBIOS SVN - revision 1229. + revision 1246. - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index 550273a5ef..f4a3a396c4 100644 Binary files a/pc-bios/openbios-ppc and b/pc-bios/openbios-ppc differ diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 01105fc904..bb7cdfb4ec 100644 Binary files a/pc-bios/openbios-sparc32 and b/pc-bios/openbios-sparc32 differ diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 62c9e77983..46b4fddd08 100644 Binary files a/pc-bios/openbios-sparc64 and b/pc-bios/openbios-sparc64 differ diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index 57d8bd0d6c..ce4852a4d5 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -12,6 +12,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/optionrom) CFLAGS := -Wall -Wstrict-prototypes -Werror -fomit-frame-pointer -fno-builtin CFLAGS += -I$(SRC_PATH) CFLAGS += $(call cc-option, $(CFLAGS), -fno-stack-protector) +CFLAGS += $(CFLAGS_NOPIE) QEMU_CFLAGS = $(CFLAGS) build-all: multiboot.bin linuxboot.bin kvmvapic.bin @@ -20,7 +21,7 @@ build-all: multiboot.bin linuxboot.bin kvmvapic.bin .SECONDARY: %.img: %.o - $(call quiet-command,$(LD) -Ttext 0 -e _start -s -o $@ $<," Building $(TARGET_DIR)$@") + $(call quiet-command,$(LD) $(LDFLAGS_NOPIE) -Ttext 0 -e _start -s -o $@ $<," Building $(TARGET_DIR)$@") %.raw: %.img $(call quiet-command,$(OBJCOPY) -O binary -j .text $< $@," Building $(TARGET_DIR)$@") diff --git a/po/Makefile b/po/Makefile index 705166e2d3..669f8654a6 100644 --- a/po/Makefile +++ b/po/Makefile @@ -37,8 +37,8 @@ install: $(OBJS) $(call quiet-command, msgfmt -o $@ $<, " GEN $@") $(PO_PATH)/messages.po: $(SRC_PATH)/ui/gtk.c - $(call quiet-command, cd $(SRC_PATH) && \ - (xgettext -o - --from-code=UTF-8 --foreign-user \ + $(call quiet-command, ( cd $(SRC_PATH) && \ + xgettext -o - --from-code=UTF-8 --foreign-user \ --package-name=QEMU --package-version=$(VERSION) \ --msgid-bugs-address=qemu-devel@nongnu.org -k_ -C ui/gtk.c | \ sed -e s/CHARSET/UTF-8/) >$@, " GEN $@") diff --git a/qdev-monitor.c b/qdev-monitor.c index dc37a43dd9..90a0cea308 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -477,7 +477,7 @@ DeviceState *qdev_device_add(QemuOpts *opts) } } - if (!oc) { + if (!object_class_dynamic_cast(oc, TYPE_DEVICE)) { qerror_report(QERR_INVALID_PARAMETER_VALUE, "driver", "device type"); return NULL; } diff --git a/qemu-char.c b/qemu-char.c index e00f84c8e9..1c0d6486f5 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -213,7 +213,7 @@ void qemu_chr_add_handlers(CharDriverState *s, s->chr_read = fd_read; s->chr_event = fd_event; s->handler_opaque = opaque; - if (s->chr_update_read_handler) + if (fe_open && s->chr_update_read_handler) s->chr_update_read_handler(s); if (!s->explicit_fe_open) { @@ -1136,13 +1136,14 @@ static void pty_chr_state(CharDriverState *chr, int connected) if (!s->connected) { s->connected = 1; qemu_chr_be_generic_open(chr); + } + if (!chr->fd_in_tag) { chr->fd_in_tag = io_add_watch_poll(s->fd, pty_chr_read_poll, pty_chr_read, chr); } } } - static void pty_chr_close(struct CharDriverState *chr) { PtyCharDriver *s = chr->opaque; @@ -2510,6 +2511,17 @@ static void tcp_chr_connect(void *opaque) qemu_chr_be_generic_open(chr); } +static void tcp_chr_update_read_handler(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + + remove_fd_in_watch(chr); + if (s->chan) { + chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, + tcp_chr_read, chr); + } +} + #define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c; static void tcp_chr_telnet_init(int fd) { @@ -2665,6 +2677,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, chr->get_msgfd = tcp_get_msgfd; chr->chr_add_client = tcp_chr_add_client; chr->chr_add_watch = tcp_chr_add_watch; + chr->chr_update_read_handler = tcp_chr_update_read_handler; /* be isn't opened until we get a connection */ chr->explicit_be_open = true; diff --git a/qemu-img.c b/qemu-img.c index b6b5644cb6..1d5caa3c8b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1355,7 +1355,7 @@ static int img_convert(int argc, char **argv) ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); - return -1; + goto out; } out_bs = bdrv_new_open(out_filename, out_fmt, flags, true, quiet); diff --git a/qemu-nbd.c b/qemu-nbd.c index c26c98ef1d..474966f468 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -299,8 +299,11 @@ static void nbd_accept(void *opaque) return; } - if (fd >= 0 && nbd_client_new(exp, fd, nbd_client_closed)) { + if (nbd_client_new(exp, fd, nbd_client_closed)) { nb_fds++; + } else { + shutdown(fd, 2); + close(fd); } } diff --git a/qemu-seccomp.c b/qemu-seccomp.c index 69cee443af..7c7b4742a6 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -230,6 +230,7 @@ int seccomp_start(void) ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { + rc = -1; goto seccomp_return; } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 8100bee67e..8763308cac 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -525,7 +525,7 @@ struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset, if (ret == -1) { error_setg_errno(err, errno, "failed to seek file"); } else { - seek_data = g_malloc0(sizeof(GuestFileRead)); + seek_data = g_new0(GuestFileSeek, 1); seek_data->position = ftell(fh); seek_data->eof = feof(fh); } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index a6a0af264b..c59e144f8d 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -31,7 +31,7 @@ static void acquire_privilege(const char *name, Error **err) { - HANDLE token; + HANDLE token = NULL; TOKEN_PRIVILEGES priv; Error *local_err = NULL; @@ -57,13 +57,15 @@ static void acquire_privilege(const char *name, Error **err) goto out; } - CloseHandle(token); } else { error_set(&local_err, QERR_QGA_COMMAND_FAILED, "failed to open privilege token"); } out: + if (token) { + CloseHandle(token); + } if (local_err) { error_propagate(err, local_err); } diff --git a/qga/vss-win32/provider.cpp b/qga/vss-win32/provider.cpp index bf42b5e95f..d5129f8f65 100644 --- a/qga/vss-win32/provider.cpp +++ b/qga/vss-win32/provider.cpp @@ -278,7 +278,9 @@ STDMETHODIMP CQGAVssProvider::DeleteSnapshots( VSS_ID SourceObjectId, VSS_OBJECT_TYPE eSourceObjectType, BOOL bForceDelete, LONG *plDeletedSnapshots, VSS_ID *pNondeletedSnapshotID) { - return E_NOTIMPL; + *plDeletedSnapshots = 0; + *pNondeletedSnapshotID = SourceObjectId; + return S_OK; } STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( @@ -291,8 +293,17 @@ STDMETHODIMP CQGAVssProvider::BeginPrepareSnapshot( STDMETHODIMP CQGAVssProvider::IsVolumeSupported( VSS_PWSZ pwszVolumeName, BOOL *pbSupportedByThisProvider) { - *pbSupportedByThisProvider = TRUE; + HANDLE hEventFrozen; + /* Check if a requester is qemu-ga by whether an event is created */ + hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); + if (!hEventFrozen) { + *pbSupportedByThisProvider = FALSE; + return S_OK; + } + CloseHandle(hEventFrozen); + + *pbSupportedByThisProvider = TRUE; return S_OK; } @@ -342,18 +353,18 @@ STDMETHODIMP CQGAVssProvider::CommitSnapshots(VSS_ID SnapshotSetId) HANDLE hEventFrozen, hEventThaw, hEventTimeout; hEventFrozen = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_FROZEN); - if (hEventFrozen == INVALID_HANDLE_VALUE) { + if (!hEventFrozen) { return E_FAIL; } hEventThaw = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_THAW); - if (hEventThaw == INVALID_HANDLE_VALUE) { + if (!hEventThaw) { CloseHandle(hEventFrozen); return E_FAIL; } hEventTimeout = OpenEvent(EVENT_ALL_ACCESS, FALSE, EVENT_NAME_TIMEOUT); - if (hEventTimeout == INVALID_HANDLE_VALUE) { + if (!hEventTimeout) { CloseHandle(hEventFrozen); CloseHandle(hEventThaw); return E_FAIL; diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 1e8dd3dfa8..922e74ddfc 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -50,10 +50,6 @@ static struct QGAVSSContext { STDAPI requester_init(void) { - vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; - vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; - vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; - COMInitializer initializer; /* to call CoInitializeSecurity */ HRESULT hr = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, @@ -94,17 +90,17 @@ STDAPI requester_init(void) static void requester_cleanup(void) { - if (vss_ctx.hEventFrozen != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventFrozen) { CloseHandle(vss_ctx.hEventFrozen); - vss_ctx.hEventFrozen = INVALID_HANDLE_VALUE; + vss_ctx.hEventFrozen = NULL; } - if (vss_ctx.hEventThaw != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventThaw) { CloseHandle(vss_ctx.hEventThaw); - vss_ctx.hEventThaw = INVALID_HANDLE_VALUE; + vss_ctx.hEventThaw = NULL; } - if (vss_ctx.hEventTimeout != INVALID_HANDLE_VALUE) { + if (vss_ctx.hEventTimeout) { CloseHandle(vss_ctx.hEventTimeout); - vss_ctx.hEventTimeout = INVALID_HANDLE_VALUE; + vss_ctx.hEventTimeout = NULL; } if (vss_ctx.pAsyncSnapshot) { vss_ctx.pAsyncSnapshot->Release(); @@ -256,6 +252,32 @@ void requester_freeze(int *num_vols, ErrorSet *errset) CoInitialize(NULL); + /* Allow unrestricted access to events */ + InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); + sa.nLength = sizeof(sa); + sa.lpSecurityDescriptor = &sd; + sa.bInheritHandle = FALSE; + + vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); + if (!vss_ctx.hEventFrozen) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_FROZEN); + goto out; + } + vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); + if (!vss_ctx.hEventThaw) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_THAW); + goto out; + } + vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); + if (!vss_ctx.hEventTimeout) { + err_set(errset, GetLastError(), "failed to create event %s", + EVENT_NAME_TIMEOUT); + goto out; + } + assert(pCreateVssBackupComponents != NULL); hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); if (FAILED(hr)) { @@ -366,32 +388,6 @@ void requester_freeze(int *num_vols, ErrorSet *errset) goto out; } - /* Allow unrestricted access to events */ - InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION); - SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE); - sa.nLength = sizeof(sa); - sa.lpSecurityDescriptor = &sd; - sa.bInheritHandle = FALSE; - - vss_ctx.hEventFrozen = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_FROZEN); - if (vss_ctx.hEventFrozen == INVALID_HANDLE_VALUE) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_FROZEN); - goto out; - } - vss_ctx.hEventThaw = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_THAW); - if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_THAW); - goto out; - } - vss_ctx.hEventTimeout = CreateEvent(&sa, TRUE, FALSE, EVENT_NAME_TIMEOUT); - if (vss_ctx.hEventTimeout == INVALID_HANDLE_VALUE) { - err_set(errset, GetLastError(), "failed to create event %s", - EVENT_NAME_TIMEOUT); - goto out; - } - /* * Start VSS quiescing operations. * CQGAVssProvider::CommitSnapshots will kick vss_ctx.hEventFrozen @@ -443,7 +439,7 @@ void requester_thaw(int *num_vols, ErrorSet *errset) { COMPointer pAsync; - if (vss_ctx.hEventThaw == INVALID_HANDLE_VALUE) { + if (!vss_ctx.hEventThaw) { /* * In this case, DoSnapshotSet is aborted or not started, * and no volumes must be frozen. We return without an error. diff --git a/qom/object.c b/qom/object.c index fc19cf676a..9156f91070 100644 --- a/qom/object.c +++ b/qom/object.c @@ -458,7 +458,7 @@ Object *object_dynamic_cast_assert(Object *obj, const char *typename, Object *inst; for (i = 0; obj && i < OBJECT_CLASS_CAST_CACHE; i++) { - if (obj->class->cast_cache[i] == typename) { + if (obj->class->object_cast_cache[i] == typename) { goto out; } } @@ -475,9 +475,10 @@ Object *object_dynamic_cast_assert(Object *obj, const char *typename, if (obj && obj == inst) { for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { - obj->class->cast_cache[i - 1] = obj->class->cast_cache[i]; + obj->class->object_cast_cache[i - 1] = + obj->class->object_cast_cache[i]; } - obj->class->cast_cache[i - 1] = typename; + obj->class->object_cast_cache[i - 1] = typename; } out: @@ -547,7 +548,7 @@ ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, int i; for (i = 0; class && i < OBJECT_CLASS_CAST_CACHE; i++) { - if (class->cast_cache[i] == typename) { + if (class->class_cast_cache[i] == typename) { ret = class; goto out; } @@ -568,9 +569,9 @@ ObjectClass *object_class_dynamic_cast_assert(ObjectClass *class, #ifdef CONFIG_QOM_CAST_DEBUG if (class && ret == class) { for (i = 1; i < OBJECT_CLASS_CAST_CACHE; i++) { - class->cast_cache[i - 1] = class->cast_cache[i]; + class->class_cast_cache[i - 1] = class->class_cast_cache[i]; } - class->cast_cache[i - 1] = typename; + class->class_cast_cache[i - 1] = typename; } out: #endif @@ -1272,6 +1273,7 @@ void object_property_add_str(Object *obj, const char *name, void (*set)(Object *, const char *, Error **), Error **errp) { + Error *local_err = NULL; StringProperty *prop = g_malloc0(sizeof(*prop)); prop->get = get; @@ -1281,7 +1283,11 @@ void object_property_add_str(Object *obj, const char *name, get ? property_get_str : NULL, set ? property_set_str : NULL, property_release_str, - prop, errp); + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } } typedef struct BoolProperty @@ -1328,6 +1334,7 @@ void object_property_add_bool(Object *obj, const char *name, void (*set)(Object *, bool, Error **), Error **errp) { + Error *local_err = NULL; BoolProperty *prop = g_malloc0(sizeof(*prop)); prop->get = get; @@ -1337,7 +1344,11 @@ void object_property_add_bool(Object *obj, const char *name, get ? property_get_bool : NULL, set ? property_set_bool : NULL, property_release_bool, - prop, errp); + prop, &local_err); + if (local_err) { + error_propagate(errp, local_err); + g_free(prop); + } } static char *qdev_get_type(Object *obj, Error **errp) diff --git a/roms/openbios b/roms/openbios index d363cf50c5..888126272f 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit d363cf50c50c268da7e6d0bf707adde1893d1ab9 +Subproject commit 888126272f92294b0da45158393f1b862742cf6b diff --git a/savevm.c b/savevm.c index 3f912ddcf9..a271c2b3ee 100644 --- a/savevm.c +++ b/savevm.c @@ -1111,22 +1111,25 @@ const VMStateInfo vmstate_info_int32_equal = { .put = put_int32, }; -/* 32 bit int. See that the received value is the less or the same - than the one in the field */ +/* 32 bit int. Check that the received value is non-negative + * and less than or equal to the one in the field. + */ static int get_int32_le(QEMUFile *f, void *pv, size_t size) { - int32_t *old = pv; - int32_t new; - qemu_get_sbe32s(f, &new); + int32_t *cur = pv; + int32_t loaded; + qemu_get_sbe32s(f, &loaded); - if (*old <= new) + if (loaded >= 0 && loaded <= *cur) { + *cur = loaded; return 0; + } return -EINVAL; } const VMStateInfo vmstate_info_int32_le = { - .name = "int32 equal", + .name = "int32 le", .get = get_int32_le, .put = put_int32, }; @@ -1673,6 +1676,50 @@ static void vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque); +static int vmstate_n_elems(void *opaque, VMStateField *field) +{ + int n_elems = 1; + + if (field->flags & VMS_ARRAY) { + n_elems = field->num; + } else if (field->flags & VMS_VARRAY_INT32) { + n_elems = *(int32_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT32) { + n_elems = *(uint32_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT16) { + n_elems = *(uint16_t *)(opaque+field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque+field->num_offset); + } + + return n_elems; +} + +static int vmstate_size(void *opaque, VMStateField *field) +{ + int size = field->size; + + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque+field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } + + return size; +} + +static void *vmstate_base_addr(void *opaque, VMStateField *field) +{ + void *base_addr = opaque + field->offset; + + if (field->flags & VMS_POINTER) { + base_addr = *(void **)base_addr + field->start; + } + + return base_addr; +} + int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id) { @@ -1682,11 +1729,12 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, if (version_id > vmsd->version_id) { return -EINVAL; } - if (version_id < vmsd->minimum_version_id_old) { - return -EINVAL; - } if (version_id < vmsd->minimum_version_id) { - return vmsd->load_state_old(f, opaque, version_id); + if (vmsd->load_state_old && + version_id >= vmsd->minimum_version_id_old) { + return vmsd->load_state_old(f, opaque, version_id); + } + return -EINVAL; } if (vmsd->pre_load) { int ret = vmsd->pre_load(opaque); @@ -1698,30 +1746,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, field->field_exists(opaque, version_id)) || (!field->field_exists && field->version_id <= version_id)) { - void *base_addr = opaque + field->offset; - int i, n_elems = 1; - int size = field->size; + void *base_addr = vmstate_base_addr(opaque, field); + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); - if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); - if (field->flags & VMS_MULTIPLY) { - size *= field->size; - } - } - if (field->flags & VMS_ARRAY) { - n_elems = field->num; - } else if (field->flags & VMS_VARRAY_INT32) { - n_elems = *(int32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT32) { - n_elems = *(uint32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT16) { - n_elems = *(uint16_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT8) { - n_elems = *(uint8_t *)(opaque+field->num_offset); - } - if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr + field->start; - } for (i = 0; i < n_elems; i++) { void *addr = base_addr + size * i; @@ -1738,6 +1766,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, return ret; } } + } else if (field->flags & VMS_MUST_EXIST) { + fprintf(stderr, "Input validation failed: %s/%s\n", + vmsd->name, field->name); + return -1; } field++; } @@ -1762,30 +1794,10 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, while(field->name) { if (!field->field_exists || field->field_exists(opaque, vmsd->version_id)) { - void *base_addr = opaque + field->offset; - int i, n_elems = 1; - int size = field->size; + void *base_addr = vmstate_base_addr(opaque, field); + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); - if (field->flags & VMS_VBUFFER) { - size = *(int32_t *)(opaque+field->size_offset); - if (field->flags & VMS_MULTIPLY) { - size *= field->size; - } - } - if (field->flags & VMS_ARRAY) { - n_elems = field->num; - } else if (field->flags & VMS_VARRAY_INT32) { - n_elems = *(int32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT32) { - n_elems = *(uint32_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT16) { - n_elems = *(uint16_t *)(opaque+field->num_offset); - } else if (field->flags & VMS_VARRAY_UINT8) { - n_elems = *(uint8_t *)(opaque+field->num_offset); - } - if (field->flags & VMS_POINTER) { - base_addr = *(void **)base_addr + field->start; - } for (i = 0; i < n_elems; i++) { void *addr = base_addr + size * i; @@ -1798,6 +1810,12 @@ void vmstate_save_state(QEMUFile *f, const VMStateDescription *vmsd, field->info->put(f, addr, size); } } + } else { + if (field->flags & VMS_MUST_EXIST) { + fprintf(stderr, "Output state validation failed: %s/%s\n", + vmsd->name, field->name); + assert(!(field->flags & VMS_MUST_EXIST)); + } } field++; } diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index b12b6964ef..b9c41fb605 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -119,7 +119,7 @@ bool has_%(argname)s = false; argname=c_var(argname), argtype=c_type(argtype)) else: ret += mcgen(''' -%(argtype)s %(argname)s; +%(argtype)s %(argname)s = {0}; ''', argname=c_var(argname), argtype=c_type(argtype)) diff --git a/target-arm/helper.c b/target-arm/helper.c index 3445813465..6e67317811 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -546,7 +546,7 @@ static int pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, static int vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - env->cp15.c12_vbar = value & ~0x1Ful; + env->cp15.c12_vbar = value & ~0x1FULL; return 0; } @@ -859,16 +859,16 @@ static int gt_ctl_write(CPUARMState *env, const ARMCPRegInfo *ri, int timeridx = ri->crm & 1; uint32_t oldval = env->cp15.c14_timer[timeridx].ctl; - env->cp15.c14_timer[timeridx].ctl = value & 3; + env->cp15.c14_timer[timeridx].ctl = deposit64(oldval, 0, 2, value); if ((oldval ^ value) & 1) { /* Enable toggled */ gt_recalc_timer(cpu, timeridx); - } else if ((oldval & value) & 2) { + } else if ((oldval ^ value) & 2) { /* IMASK toggled: don't need to recalculate, * just set the interrupt line based on ISTATUS */ qemu_set_irq(cpu->gt_timer_outputs[timeridx], - (oldval & 4) && (value & 2)); + (oldval & 4) && !(value & 2)); } return 0; } diff --git a/target-arm/machine.c b/target-arm/machine.c index 74f010f637..286e853cd9 100644 --- a/target-arm/machine.c +++ b/target-arm/machine.c @@ -246,7 +246,7 @@ const VMStateDescription vmstate_arm_cpu = { /* The length-check must come before the arrays to avoid * incoming data possibly overflowing the array. */ - VMSTATE_INT32_LE(cpreg_vmstate_array_len, ARMCPU), + VMSTATE_INT32_POSITIVE_LE(cpreg_vmstate_array_len, ARMCPU), VMSTATE_VARRAY_INT32(cpreg_vmstate_indexes, ARMCPU, cpreg_vmstate_array_len, 0, vmstate_info_uint64, uint64_t), diff --git a/target-arm/translate.c b/target-arm/translate.c index 5f003e785e..e0c3eaa3bb 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7732,27 +7732,39 @@ static void disas_arm_insn(CPUARMState * env, DisasContext *s) if (insn & (1 << 5)) gen_swap_half(tmp2); gen_smul_dual(tmp, tmp2); - if (insn & (1 << 6)) { - /* This subtraction cannot overflow. */ - tcg_gen_sub_i32(tmp, tmp, tmp2); - } else { - /* This addition cannot overflow 32 bits; - * however it may overflow considered as a signed - * operation, in which case we must set the Q flag. - */ - gen_helper_add_setq(tmp, cpu_env, tmp, tmp2); - } - tcg_temp_free_i32(tmp2); if (insn & (1 << 22)) { /* smlald, smlsld */ + TCGv_i64 tmp64_2; + tmp64 = tcg_temp_new_i64(); + tmp64_2 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); + tcg_gen_ext_i32_i64(tmp64_2, tmp2); tcg_temp_free_i32(tmp); + tcg_temp_free_i32(tmp2); + if (insn & (1 << 6)) { + tcg_gen_sub_i64(tmp64, tmp64, tmp64_2); + } else { + tcg_gen_add_i64(tmp64, tmp64, tmp64_2); + } + tcg_temp_free_i64(tmp64_2); gen_addq(s, tmp64, rd, rn); gen_storeq_reg(s, rd, rn, tmp64); tcg_temp_free_i64(tmp64); } else { /* smuad, smusd, smlad, smlsd */ + if (insn & (1 << 6)) { + /* This subtraction cannot overflow. */ + tcg_gen_sub_i32(tmp, tmp, tmp2); + } else { + /* This addition cannot overflow 32 bits; + * however it may overflow considered as a + * signed operation, in which case we must set + * the Q flag. + */ + gen_helper_add_setq(tmp, cpu_env, tmp, tmp2); + } + tcg_temp_free_i32(tmp2); if (rd != 15) { tmp2 = load_reg(s, rd); diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index ee04092b4e..05dd12b5a7 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -103,7 +103,7 @@ target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, case CC_OP_EFLAGS: return src1; case CC_OP_CLR: - return CC_Z; + return CC_Z | CC_P; case CC_OP_MULB: return compute_all_mulb(dst, src1); diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 47af9a8816..d6bc332156 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -539,7 +539,7 @@ typedef struct x86_def_t { #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A) #define TCG_SVM_FEATURES 0 -#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP \ +#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX) /* missing: CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, @@ -2446,6 +2446,8 @@ static void x86_cpu_reset(CPUState *s) cpu_breakpoint_remove_all(env, BP_CPU); cpu_watchpoint_remove_all(env, BP_CPU); + env->xcr0 = 1; + #if !defined(CONFIG_USER_ONLY) /* We hard-wire the BSP to the first CPU. */ if (s->cpu_index == 0) { @@ -2560,6 +2562,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (!kvm_enabled()) { env->features[FEAT_1_EDX] &= TCG_FEATURES; env->features[FEAT_1_ECX] &= TCG_EXT_FEATURES; + env->features[FEAT_7_0_EBX] &= TCG_7_0_EBX_FEATURES; env->features[FEAT_8000_0001_EDX] &= (TCG_EXT2_FEATURES #ifdef TARGET_X86_64 | CPUID_EXT2_SYSCALL | CPUID_EXT2_LM diff --git a/target-i386/cpu.h b/target-i386/cpu.h index ea373e82dc..199f4079dc 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -801,6 +801,8 @@ typedef struct CPUX86State { XMMReg xmm_t0; MMXReg mmx_t0; + XMMReg ymmh_regs[CPU_NB_REGS]; + /* sysenter registers */ uint32_t sysenter_cs; target_ulong sysenter_esp; @@ -909,9 +911,7 @@ typedef struct CPUX86State { uint16_t fpus_vmstate; uint16_t fptag_vmstate; uint16_t fpregs_format_vmstate; - uint64_t xstate_bv; - XMMReg ymmh_regs[CPU_NB_REGS]; uint64_t xcr0; diff --git a/target-i386/helper.c b/target-i386/helper.c index 7c196ffc42..ed965d634d 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -531,6 +531,12 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, if (!(env->cr[0] & CR0_PG_MASK)) { pte = addr; +#ifdef TARGET_X86_64 + if (!(env->hflags & HF_LMA_MASK)) { + /* Without long mode we can only address 32bits in real mode */ + pte = (uint32_t)pte; + } +#endif virt_addr = addr & TARGET_PAGE_MASK; prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; page_size = 4096; diff --git a/target-i386/translate.c b/target-i386/translate.c index 7916e5b1f6..4a25486e03 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -915,7 +915,7 @@ static void gen_compute_eflags(DisasContext *s) return; } if (s->cc_op == CC_OP_CLR) { - tcg_gen_movi_tl(cpu_cc_src, CC_Z); + tcg_gen_movi_tl(cpu_cc_src, CC_Z | CC_P); set_cc_op(s, CC_OP_EFLAGS); return; } @@ -4565,21 +4565,47 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (is_xmm) { op1_offset = offsetof(CPUX86State,xmm_regs[reg]); if (mod != 3) { + int sz = 4; + gen_lea_modrm(env, s, modrm, ®_addr, &offset_addr); op2_offset = offsetof(CPUX86State,xmm_t0); - if (b1 >= 2 && ((b >= 0x50 && b <= 0x5f && b != 0x5b) || - b == 0xc2)) { - /* specific case for SSE single instructions */ + + switch (b) { + case 0x50 ... 0x5a: + case 0x5c ... 0x5f: + case 0xc2: + /* Most sse scalar operations. */ if (b1 == 2) { - /* 32 bit access */ - gen_op_ld_T0_A0(OT_LONG + s->mem_index); - tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); - } else { - /* 64 bit access */ - gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_D(0))); + sz = 2; + } else if (b1 == 3) { + sz = 3; } - } else { + break; + + case 0x2e: /* ucomis[sd] */ + case 0x2f: /* comis[sd] */ + if (b1 == 0) { + sz = 2; + } else { + sz = 3; + } + break; + } + + switch (sz) { + case 2: + /* 32 bit access */ + gen_op_ld_T0_A0(OT_LONG + s->mem_index); + tcg_gen_st32_tl(cpu_T[0], cpu_env, offsetof(CPUX86State,xmm_t0.XMM_L(0))); + break; + case 3: + /* 64 bit access */ + gen_ldq_env_A0(s->mem_index, offsetof(CPUX86State,xmm_t0.XMM_D(0))); + break; + default: + /* 128 bit access */ gen_ldo_env_A0(s->mem_index, op2_offset); + break; } } else { rm = (modrm & 7) | REX_B(s); diff --git a/target-mips/translate.c b/target-mips/translate.c index 67f326b205..e30273438a 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -15983,10 +15983,13 @@ void cpu_state_reset(CPUMIPSState *env) if (env->CP0_Config3 & (1 << CP0C3_DSPP)) { env->CP0_Status |= (1 << CP0St_MX); } - /* Enable 64-bit FPU if the target cpu supports it. */ - if (env->active_fpu.fcr0 & (1 << FCR0_F64)) { +# if defined(TARGET_MIPS64) + /* For MIPS64, init FR bit to 1 if FPU unit is there and bit is writable. */ + if ((env->CP0_Config1 & (1 << CP0C1_FP)) && + (env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) { env->CP0_Status |= (1 << CP0St_FR); } +# endif #else if (env->hflags & MIPS_HFLAG_BMASK) { /* If the exception was raised from a delay slot, diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h index a2c077bdcd..8c0d00a2ed 100644 --- a/target-s390x/cpu.h +++ b/target-s390x/cpu.h @@ -274,6 +274,9 @@ typedef struct CPUS390XState { #define FLAG_MASK_64 (PSW_MASK_64 >> 32) #define FLAG_MASK_32 0x00001000 +/* Control register 0 bits */ +#define CR0_EDAT 0x0000000000800000ULL + static inline int cpu_mmu_index (CPUS390XState *env) { if (env->psw.mask & PSW_MASK_PSTATE) { @@ -352,6 +355,9 @@ static inline hwaddr decode_basedisp_s(CPUS390XState *env, uint32_t ipb) return addr; } +/* Base/displacement are at the same locations. */ +#define decode_basedisp_rs decode_basedisp_s + void s390x_tod_timer(void *opaque); void s390x_cpu_timer(void *opaque); @@ -929,6 +935,7 @@ struct sysib_322 { #define _REGION_ENTRY_LENGTH 0x03 /* region third length */ #define _SEGMENT_ENTRY_ORIGIN ~0x7ffULL /* segment table origin */ +#define _SEGMENT_ENTRY_FC 0x400 /* format control */ #define _SEGMENT_ENTRY_RO 0x200 /* page protection bit */ #define _SEGMENT_ENTRY_INV 0x20 /* invalid segment table entry */ diff --git a/target-s390x/helper.c b/target-s390x/helper.c index da33b38009..e8e92efa8d 100644 --- a/target-s390x/helper.c +++ b/target-s390x/helper.c @@ -164,6 +164,50 @@ static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr, trigger_pgm_exception(env, type, ilen); } +/* Decode page table entry (normal 4KB page) */ +static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, uint64_t asce, + target_ulong *raddr, int *flags, int rw) +{ + if (asce & _PAGE_INVALID) { + DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, asce); + trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw); + return -1; + } + + if (asce & _PAGE_RO) { + *flags &= ~PAGE_WRITE; + } + + *raddr = asce & _ASCE_ORIGIN; + + PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, asce); + + return 0; +} + +/* Decode EDAT1 segment frame absolute address (1MB page) */ +static int mmu_translate_sfaa(CPUS390XState *env, target_ulong vaddr, + uint64_t asc, uint64_t asce, target_ulong *raddr, + int *flags, int rw) +{ + if (asce & _SEGMENT_ENTRY_INV) { + DPRINTF("%s: SEG=0x%" PRIx64 " invalid\n", __func__, asce); + trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw); + return -1; + } + + if (asce & _SEGMENT_ENTRY_RO) { + *flags &= ~PAGE_WRITE; + } + + *raddr = (asce & 0xfffffffffff00000ULL) | (vaddr & 0xfffff); + + PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, asce); + + return 0; +} + static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, uint64_t asc, uint64_t asce, int level, target_ulong *raddr, int *flags, int rw) @@ -222,28 +266,18 @@ static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr, PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n", __func__, origin, offs, new_asce); - if (level != _ASCE_TYPE_SEGMENT) { + if (level == _ASCE_TYPE_SEGMENT) { + /* 4KB page */ + return mmu_translate_pte(env, vaddr, asc, new_asce, raddr, flags, rw); + } else if (level - 4 == _ASCE_TYPE_SEGMENT && + (new_asce & _SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) { + /* 1MB page */ + return mmu_translate_sfaa(env, vaddr, asc, new_asce, raddr, flags, rw); + } else { /* yet another region */ return mmu_translate_asce(env, vaddr, asc, new_asce, level - 4, raddr, flags, rw); } - - /* PTE */ - if (new_asce & _PAGE_INVALID) { - DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, new_asce); - trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw); - return -1; - } - - if (new_asce & _PAGE_RO) { - *flags &= ~PAGE_WRITE; - } - - *raddr = new_asce & _ASCE_ORIGIN; - - PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, new_asce); - - return 0; } static int mmu_translate_asc(CPUS390XState *env, target_ulong vaddr, diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 02ac4ba995..ef4d5cca12 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -362,6 +362,26 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) return 0; } +int kvm_arch_insert_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + return -ENOSYS; +} + +int kvm_arch_remove_hw_breakpoint(target_ulong addr, + target_ulong len, int type) +{ + return -ENOSYS; +} + +void kvm_arch_remove_all_hw_breakpoints(void) +{ +} + +void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg) +{ +} + void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) { } @@ -562,11 +582,19 @@ static void kvm_handle_diag_308(S390CPU *cpu, struct kvm_run *run) handle_diag_308(&cpu->env, r1, r3); } -static int handle_diag(S390CPU *cpu, struct kvm_run *run, int ipb_code) +#define DIAG_KVM_CODE_MASK 0x000000000000ffff + +static int handle_diag(S390CPU *cpu, struct kvm_run *run, uint32_t ipb) { int r = 0; + uint16_t func_code; - switch (ipb_code) { + /* + * For any diagnose call we support, bits 48-63 of the resulting + * address specify the function code; the remainder is ignored. + */ + func_code = decode_basedisp_rs(&cpu->env, ipb) & DIAG_KVM_CODE_MASK; + switch (func_code) { case DIAG_IPL: kvm_handle_diag_308(cpu, run); break; @@ -577,7 +605,7 @@ static int handle_diag(S390CPU *cpu, struct kvm_run *run, int ipb_code) sleep(10); break; default: - DPRINTF("KVM: unknown DIAG: 0x%x\n", ipb_code); + DPRINTF("KVM: unknown DIAG: 0x%x\n", func_code); r = -1; break; } @@ -684,7 +712,6 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run) { unsigned int ipa0 = (run->s390_sieic.ipa & 0xff00); uint8_t ipa1 = run->s390_sieic.ipa & 0x00ff; - int ipb_code = (run->s390_sieic.ipb & 0x0fff0000) >> 16; int r = -1; DPRINTF("handle_instruction 0x%x 0x%x\n", @@ -696,7 +723,7 @@ static void handle_instruction(S390CPU *cpu, struct kvm_run *run) r = handle_priv(cpu, run, ipa0 >> 8, ipa1); break; case IPA0_DIAG: - r = handle_diag(cpu, run, ipb_code); + r = handle_diag(cpu, run, run->s390_sieic.ipb); break; case IPA0_SIGP: r = handle_sigp(cpu, run, ipa1); @@ -805,6 +832,11 @@ static int handle_tsch(S390CPU *cpu) return ret; } +static int kvm_arch_handle_debug_exit(S390CPU *cpu) +{ + return -ENOSYS; +} + int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) { S390CPU *cpu = S390_CPU(cs); @@ -820,6 +852,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_S390_TSCH: ret = handle_tsch(cpu); break; + case KVM_EXIT_DEBUG: + ret = kvm_arch_handle_debug_exit(cpu); + break; default: fprintf(stderr, "Unknown KVM exit: %d\n", run->exit_reason); break; diff --git a/target-xtensa/translate.c b/target-xtensa/translate.c index 2d2df33115..7d34326417 100644 --- a/target-xtensa/translate.c +++ b/target-xtensa/translate.c @@ -414,7 +414,7 @@ static void gen_jump(DisasContext *dc, TCGv dest) static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); - if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + if (((dc->tb->pc ^ dest) & TARGET_PAGE_MASK) != 0) { slot = -1; } gen_jump_slot(dc, tmp, slot); @@ -442,7 +442,7 @@ static void gen_callw(DisasContext *dc, int callinc, TCGv_i32 dest) static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); - if (((dc->pc ^ dest) & TARGET_PAGE_MASK) != 0) { + if (((dc->tb->pc ^ dest) & TARGET_PAGE_MASK) != 0) { slot = -1; } gen_callw_slot(dc, callinc, tmp, slot); diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index e93a4a237b..5d4bbe7fb0 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -1868,7 +1868,7 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : SHIFT_IMM_LSL(0)); } else { - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_TMP, args[1], 0x20); + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_TMP, args[2], 0x20); tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], SHIFT_REG_ROR(TCG_REG_TMP)); } diff --git a/tcg/i386/tcg-target.c b/tcg/i386/tcg-target.c index 7ac8e45485..60d3325c97 100644 --- a/tcg/i386/tcg-target.c +++ b/tcg/i386/tcg-target.c @@ -1306,7 +1306,8 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) } else { retaddr = TCG_REG_RAX; tcg_out_movi(s, TCG_TYPE_PTR, retaddr, (uintptr_t)l->raddr); - tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, 0); + tcg_out_st(s, TCG_TYPE_PTR, retaddr, TCG_REG_ESP, + TCG_TARGET_CALL_STACK_OFFSET); } } diff --git a/tcg/optimize.c b/tcg/optimize.c index 89e2d6a3b3..c5cdde2160 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -726,16 +726,25 @@ static TCGArg *tcg_constant_folding(TCGContext *s, uint16_t *tcg_opc_ptr, mask = temps[args[1]].mask & mask; break; - CASE_OP_32_64(sar): + case INDEX_op_sar_i32: if (temps[args[2]].state == TCG_TEMP_CONST) { - mask = ((tcg_target_long)temps[args[1]].mask - >> temps[args[2]].val); + mask = (int32_t)temps[args[1]].mask >> temps[args[2]].val; + } + break; + case INDEX_op_sar_i64: + if (temps[args[2]].state == TCG_TEMP_CONST) { + mask = (int64_t)temps[args[1]].mask >> temps[args[2]].val; } break; - CASE_OP_32_64(shr): + case INDEX_op_shr_i32: if (temps[args[2]].state == TCG_TEMP_CONST) { - mask = temps[args[1]].mask >> temps[args[2]].val; + mask = (uint32_t)temps[args[1]].mask >> temps[args[2]].val; + } + break; + case INDEX_op_shr_i64: + if (temps[args[2]].state == TCG_TEMP_CONST) { + mask = (uint64_t)temps[args[1]].mask >> temps[args[2]].val; } break; diff --git a/tests/qemu-iotests/026.out b/tests/qemu-iotests/026.out index 15045799a2..f7c78e712a 100644 --- a/tests/qemu-iotests/026.out +++ b/tests/qemu-iotests/026.out @@ -475,7 +475,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_blocks; errno: 28; imm: off; once: off; write write failed: No space left on device -10 leaked clusters were found on the image. +11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -499,7 +499,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.write_table; errno: 28; imm: off; once: off; write write failed: No space left on device -10 leaked clusters were found on the image. +11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 @@ -523,7 +523,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 Event: refblock_alloc.switch_table; errno: 28; imm: off; once: off; write write failed: No space left on device -10 leaked clusters were found on the image. +11 leaked clusters were found on the image. This means waste of disk space, but no harm to data. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1073741824 diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index b424726fc4..fa46ace67b 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -1,7 +1,6 @@ #!/bin/bash # -# Test loading internal snapshots where the L1 table of the snapshot -# is smaller than the current L1 table. +# qcow2 internal snapshots/VM state tests # # Copyright (C) 2011 Red Hat, Inc. # @@ -31,7 +30,8 @@ status=1 # failure is the default! _cleanup() { - _cleanup_test_img + rm -f $TEST_IMG.snap + _cleanup_test_img } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -45,6 +45,14 @@ _supported_fmt qcow2 _supported_proto generic _supported_os Linux +offset_size=24 +offset_l1_size=36 + +echo +echo Test loading internal snapshots where the L1 table of the snapshot +echo is smaller than the current L1 table. +echo + CLUSTER_SIZE=65536 _make_test_img 64M $QEMU_IMG snapshot -c foo "$TEST_IMG" @@ -59,6 +67,32 @@ $QEMU_IO -c 'write -b 0 4M' "$TEST_IMG" | _filter_qemu_io $QEMU_IMG snapshot -a foo "$TEST_IMG" _check_test_img + +echo +echo Try using a huge VM state +echo + +CLUSTER_SIZE=65536 +_make_test_img 64M +{ $QEMU_IO -c "write -b -P 0x11 1T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -a foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "read -b -P 0x11 1T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +_check_test_img + + +echo +echo "qcow2_snapshot_load_tmp() should take the L1 size from the snapshot" +echo + +CLUSTER_SIZE=512 +_make_test_img 64M +{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_size" "\x00\x00\x00\x00\x00\x00\x02\x00" +poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01" +{ $QEMU_IMG convert -s foo $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_qemu_io | _filter_testdir + + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/029.out b/tests/qemu-iotests/029.out index 0eedb3a3ab..ce0e64d24a 100644 --- a/tests/qemu-iotests/029.out +++ b/tests/qemu-iotests/029.out @@ -1,4 +1,8 @@ QA output created by 029 + +Test loading internal snapshots where the L1 table of the snapshot +is smaller than the current L1 table. + Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) @@ -7,4 +11,17 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=16777216 wrote 4194304/4194304 bytes at offset 0 4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) No errors were found on the image. + +Try using a huge VM state + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 4096/4096 bytes at offset 1099511627776 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 1099511627776 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +qcow2_snapshot_load_tmp() should take the L1 size from the snapshot + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 *** done diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 8bade92a80..cc4fad8743 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -130,6 +130,26 @@ ulimit -c "$old_ulimit" ./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features _check_test_img +echo +echo "== Committing to a backing file with lazy_refcounts=on ==" + +IMGOPTS="compat=1.1,lazy_refcounts=on" +TEST_IMG="$TEST_IMG".base _make_test_img $size + +IMGOPTS="compat=1.1,lazy_refcounts=on,backing_file=$TEST_IMG.base" +_make_test_img $size + +$QEMU_IO -c "write 0 512" "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG commit "$TEST_IMG" + +# The dirty bit must not be set +./qcow2.py "$TEST_IMG" dump-header | grep incompatible_features +./qcow2.py "$TEST_IMG".base dump-header | grep incompatible_features + +_check_test_img +TEST_IMG="$TEST_IMG".base _check_test_img + + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index 077fa64cbf..fb31ae0624 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -54,4 +54,15 @@ wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) incompatible_features 0x0 No errors were found on the image. + +== Committing to a backing file with lazy_refcounts=on == +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file='TEST_DIR/t.IMGFMT.base' +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Image committed. +incompatible_features 0x0 +incompatible_features 0x0 +No errors were found on the image. +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/044.out b/tests/qemu-iotests/044.out index 5c5aa929fb..4789a5310e 100644 --- a/tests/qemu-iotests/044.out +++ b/tests/qemu-iotests/044.out @@ -1,6 +1,6 @@ No errors were found on the image. 7292415/33554432 = 21.73% allocated, 0.00% fragmented, 0.00% compressed clusters -Image end offset: 4296448000 +Image end offset: 4296152064 . ---------------------------------------------------------------------- Ran 1 tests diff --git a/tests/qemu-iotests/075 b/tests/qemu-iotests/075 new file mode 100755 index 0000000000..40032c563d --- /dev/null +++ b/tests/qemu-iotests/075 @@ -0,0 +1,106 @@ +#!/bin/bash +# +# cloop format input validation tests +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=stefanha@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt cloop +_supported_proto generic +_supported_os Linux + +block_size_offset=128 +n_blocks_offset=132 +offsets_offset=136 + +echo +echo "== check that the first sector can be read ==" +_use_sample_img simple-pattern.cloop.bz2 +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== check that the last sector can be read ==" +_use_sample_img simple-pattern.cloop.bz2 +$QEMU_IO -c "read $((1024 * 1024 - 512)) 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== block_size must be a multiple of 512 ==" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$block_size_offset" "\x00\x00\x02\x01" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== block_size cannot be zero ==" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$block_size_offset" "\x00\x00\x00\x00" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== huge block_size ===" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$block_size_offset" "\xff\xff\xfe\x00" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== offsets_size overflow ===" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$n_blocks_offset" "\xff\xff\xff\xff" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== refuse images that require too many offsets ===" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$n_blocks_offset" "\x04\x00\x00\x01" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== refuse images with non-monotonically increasing offsets ==" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$offsets_offset" "\x00\x00\x00\x00\xff\xff\xff\xff" +poke_file "$TEST_IMG" $((offsets_offset + 8)) "\x00\x00\x00\x00\xff\xfe\x00\x00" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== refuse images with invalid compressed block size ==" +_use_sample_img simple-pattern.cloop.bz2 +poke_file "$TEST_IMG" "$offsets_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +poke_file "$TEST_IMG" $((offsets_offset + 8)) "\xff\xff\xff\xff\xff\xff\xff\xff" +$QEMU_IO -c "read 0 512" $TEST_IMG 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/075.out b/tests/qemu-iotests/075.out new file mode 100644 index 0000000000..5f1d6c120a --- /dev/null +++ b/tests/qemu-iotests/075.out @@ -0,0 +1,38 @@ +QA output created by 075 + +== check that the first sector can be read == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== check that the last sector can be read == +read 512/512 bytes at offset 1048064 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== block_size must be a multiple of 512 == +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: block_size 513 must be a multiple of 512 +no file open, try 'help open' + +== block_size cannot be zero == +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: block_size cannot be zero +no file open, try 'help open' + +== huge block_size === +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: block_size 4294966784 must be 64 MB or less +no file open, try 'help open' + +== offsets_size overflow === +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: n_blocks 4294967295 must be 536870911 or less +no file open, try 'help open' + +== refuse images that require too many offsets === +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: image requires too many offsets, try increasing block size +no file open, try 'help open' + +== refuse images with non-monotonically increasing offsets == +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: offsets not monotonically increasing at index 1, image file is corrupt +no file open, try 'help open' + +== refuse images with invalid compressed block size == +qemu-io: can't open device TEST_DIR/simple-pattern.cloop: invalid compressed block size at index 1, image file is corrupt +no file open, try 'help open' +*** done diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076 new file mode 100755 index 0000000000..b614a7dd6e --- /dev/null +++ b/tests/qemu-iotests/076 @@ -0,0 +1,76 @@ +#!/bin/bash +# +# parallels format input validation tests +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt parallels +_supported_proto generic +_supported_os Linux + +tracks_offset=$((0x1c)) +catalog_entries_offset=$((0x20)) +nb_sectors_offset=$((0x24)) + +echo +echo "== Read from a valid (enough) image ==" +_use_sample_img fake.parallels.bz2 +{ $QEMU_IO -c "read -P 0x11 0 64k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Negative catalog size ==" +_use_sample_img fake.parallels.bz2 +poke_file "$TEST_IMG" "$catalog_entries_offset" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Overflow in catalog allocation ==" +_use_sample_img fake.parallels.bz2 +poke_file "$TEST_IMG" "$nb_sectors_offset" "\xff\xff\xff\xff" +poke_file "$TEST_IMG" "$catalog_entries_offset" "\x01\x00\x00\x40" +{ $QEMU_IO -c "read 64M 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Zero sectors per track ==" +_use_sample_img fake.parallels.bz2 +poke_file "$TEST_IMG" "$tracks_offset" "\x00\x00\x00\x00" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/076.out b/tests/qemu-iotests/076.out new file mode 100644 index 0000000000..f7745d8b0d --- /dev/null +++ b/tests/qemu-iotests/076.out @@ -0,0 +1,18 @@ +QA output created by 076 + +== Read from a valid (enough) image == +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== Negative catalog size == +qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large +no file open, try 'help open' + +== Overflow in catalog allocation == +qemu-io: can't open device TEST_DIR/fake.parallels: Catalog too large +no file open, try 'help open' + +== Zero sectors per track == +qemu-io: can't open device TEST_DIR/fake.parallels: Invalid image: Zero sectors per track +no file open, try 'help open' +*** done diff --git a/tests/qemu-iotests/078 b/tests/qemu-iotests/078 new file mode 100755 index 0000000000..872e734cab --- /dev/null +++ b/tests/qemu-iotests/078 @@ -0,0 +1,87 @@ +#!/bin/bash +# +# bochs format input validation tests +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt bochs +_supported_proto generic +_supported_os Linux + +catalog_size_offset=$((0x48)) +extent_size_offset=$((0x50)) +disk_size_offset=$((0x58)) + +echo +echo "== Read from a valid image ==" +_use_sample_img empty.bochs.bz2 +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Negative catalog size ==" +_use_sample_img empty.bochs.bz2 +poke_file "$TEST_IMG" "$catalog_size_offset" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Overflow for catalog size * sizeof(uint32_t) ==" +_use_sample_img empty.bochs.bz2 +poke_file "$TEST_IMG" "$catalog_size_offset" "\x00\x00\x00\x40" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Too small catalog bitmap for image size ==" +_use_sample_img empty.bochs.bz2 +poke_file "$TEST_IMG" "$disk_size_offset" "\x00\xc0\x0f\x00\x00\x00\x00\x7f" +{ $QEMU_IO -c "read 2T 4k" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Negative extent size ==" +_use_sample_img empty.bochs.bz2 +poke_file "$TEST_IMG" "$extent_size_offset" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 768k 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Zero extent size ==" +_use_sample_img empty.bochs.bz2 +poke_file "$TEST_IMG" "$extent_size_offset" "\x00\x00\x00\x00" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/078.out b/tests/qemu-iotests/078.out new file mode 100644 index 0000000000..ea95ffdbb8 --- /dev/null +++ b/tests/qemu-iotests/078.out @@ -0,0 +1,26 @@ +QA output created by 078 + +== Read from a valid image == +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== Negative catalog size == +qemu-io: can't open device TEST_DIR/empty.bochs: Catalog size is too large +no file open, try 'help open' + +== Overflow for catalog size * sizeof(uint32_t) == +qemu-io: can't open device TEST_DIR/empty.bochs: Catalog size is too large +no file open, try 'help open' + +== Too small catalog bitmap for image size == +qemu-io: can't open device TEST_DIR/empty.bochs: Catalog size is too small for this disk size +no file open, try 'help open' + +== Negative extent size == +qemu-io: can't open device TEST_DIR/empty.bochs: Extent size 4294967295 is too large +no file open, try 'help open' + +== Zero extent size == +qemu-io: can't open device TEST_DIR/empty.bochs: Extent size may not be zero +no file open, try 'help open' +*** done diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 new file mode 100755 index 0000000000..6b3a3e77a5 --- /dev/null +++ b/tests/qemu-iotests/080 @@ -0,0 +1,180 @@ +#!/bin/bash +# +# qcow2 format input validation tests +# +# Copyright (C) 2013 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + rm -f $TEST_IMG.snap + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow2 +_supported_proto generic +_supported_os Linux + +header_size=104 + +offset_backing_file_offset=8 +offset_backing_file_size=16 +offset_l1_size=36 +offset_l1_table_offset=40 +offset_refcount_table_offset=48 +offset_refcount_table_clusters=56 +offset_nb_snapshots=60 +offset_snapshots_offset=64 +offset_header_size=100 +offset_ext_magic=$header_size +offset_ext_size=$((header_size + 4)) + +offset_l2_table_0=$((0x40000)) + +offset_snap1=$((0x70000)) +offset_snap1_l1_offset=$((offset_snap1 + 0)) +offset_snap1_l1_size=$((offset_snap1 + 8)) + +echo +echo "== Huge header size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_header_size" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_header_size" "\x7f\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Huge unknown header extension ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_backing_file_offset" "\xff\xff\xff\xff\xff\xff\xff\xff" +poke_file "$TEST_IMG" "$offset_ext_magic" "\x12\x34\x56\x78" +poke_file "$TEST_IMG" "$offset_ext_size" "\x7f\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\x00\x00\x00\x00\x00" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Huge refcount table size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\x00\x02\x00\x01" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Misaligned refcount table ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_refcount_table_offset" "\x12\x34\x56\x78\x90\xab\xcd\xef" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Huge refcount offset ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_refcount_table_offset" "\xff\xff\xff\xff\xff\xff\x00\x00" +poke_file "$TEST_IMG" "$offset_refcount_table_clusters" "\x00\x00\x00\x7f" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid snapshot table ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_nb_snapshots" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_nb_snapshots" "\x7f\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +poke_file "$TEST_IMG" "$offset_snapshots_offset" "\xff\xff\xff\xff\xff\xff\x00\x00" +poke_file "$TEST_IMG" "$offset_nb_snapshots" "\x00\x00\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +poke_file "$TEST_IMG" "$offset_snapshots_offset" "\x12\x34\x56\x78\x90\xab\xcd\xef" +poke_file "$TEST_IMG" "$offset_nb_snapshots" "\x00\x00\x00\x00" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Hitting snapshot table size limit ==" +_make_test_img 64M +# Put the refcount table in a more or less safe place (16 MB) +poke_file "$TEST_IMG" "$offset_snapshots_offset" "\x00\x00\x00\x00\x01\x00\x00\x00" +poke_file "$TEST_IMG" "$offset_nb_snapshots" "\x00\x01\x00\x00" +{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid L1 table ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_l1_size" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_l1_size" "\x7f\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +poke_file "$TEST_IMG" "$offset_l1_table_offset" "\x7f\xff\xff\xff\xff\xff\x00\x00" +poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +poke_file "$TEST_IMG" "$offset_l1_table_offset" "\x12\x34\x56\x78\x90\xab\xcd\xef" +poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x01" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid L1 table (with internal snapshot in the image) ==" +_make_test_img 64M +{ $QEMU_IMG snapshot -c foo $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_l1_size" "\x00\x00\x00\x00" +_img_info + +echo +echo "== Invalid backing file size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\x00\x00\x00\x10\x00" +poke_file "$TEST_IMG" "$offset_backing_file_size" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid L2 entry (huge physical offset) ==" +_make_test_img 64M +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_l2_table_0" "\xbf\xff\xff\xff\xff\xff\x00\x00" +{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_l2_table_0" "\x80\x00\x00\xff\xff\xff\x00\x00" +{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid snapshot L1 table ==" +_make_test_img 64M +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IMG snapshot -c test $TEST_IMG; } 2>&1 | _filter_testdir +poke_file "$TEST_IMG" "$offset_snap1_l1_size" "\x10\x00\x00\x00" +{ $QEMU_IMG convert -s test $TEST_IMG $TEST_IMG.snap; } 2>&1 | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/080.out b/tests/qemu-iotests/080.out new file mode 100644 index 0000000000..f7a943c7a4 --- /dev/null +++ b/tests/qemu-iotests/080.out @@ -0,0 +1,83 @@ +QA output created by 080 + +== Huge header size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: qcow2 header exceeds cluster size +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: qcow2 header exceeds cluster size +no file open, try 'help open' + +== Huge unknown header extension == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid backing file offset +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Header extension too large +no file open, try 'help open' + +== Huge refcount table size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Reference count table too large +no file open, try 'help open' + +== Misaligned refcount table == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset +no file open, try 'help open' + +== Huge refcount offset == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid reference count table offset +no file open, try 'help open' + +== Invalid snapshot table == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Too many snapshots +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Too many snapshots +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid snapshot table offset +no file open, try 'help open' + +== Hitting snapshot table size limit == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: Could not create snapshot 'test': -27 (File too large) +read 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== Invalid L1 table == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Active L1 table too large +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Active L1 table too large +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid L1 table offset +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow2: Invalid L1 table offset +no file open, try 'help open' + +== Invalid L1 table (with internal snapshot in the image) == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': L1 table is too small + +== Invalid backing file size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow2: Backing file name too long +no file open, try 'help open' + +== Invalid L2 entry (huge physical offset) == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Could not create snapshot 'test': -27 (File too large) +qemu-img: Could not create snapshot 'test': -11 (Resource temporarily unavailable) + +== Invalid snapshot L1 table == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 512/512 bytes at offset 0 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: Failed to load snapshot: Snapshot L1 table too large +*** done diff --git a/tests/qemu-iotests/088 b/tests/qemu-iotests/088 new file mode 100755 index 0000000000..c09adf8023 --- /dev/null +++ b/tests/qemu-iotests/088 @@ -0,0 +1,64 @@ +#!/bin/bash +# +# vpc (VHD) format input validation tests +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + rm -f $TEST_IMG.snap + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt vpc +_supported_proto generic +_supported_os Linux + +offset_block_size=$((512 + 32)) + +echo +echo "== Invalid block size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_block_size" "\x00\x00\x00\x00" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_block_size" "\x00\x00\x00\x80" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_block_size" "\x12\x34\x56\x78" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +{ $QEMU_IO -c "write 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/088.out b/tests/qemu-iotests/088.out new file mode 100644 index 0000000000..d961609e49 --- /dev/null +++ b/tests/qemu-iotests/088.out @@ -0,0 +1,17 @@ +QA output created by 088 + +== Invalid block size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 0 +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 0 +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 128 +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 128 +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 305419896 +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.vpc: Invalid block size 305419896 +no file open, try 'help open' +*** done diff --git a/tests/qemu-iotests/092 b/tests/qemu-iotests/092 new file mode 100755 index 0000000000..a8c0c9ca2b --- /dev/null +++ b/tests/qemu-iotests/092 @@ -0,0 +1,98 @@ +#!/bin/bash +# +# qcow1 format input validation tests +# +# Copyright (C) 2014 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +here=`pwd` +tmp=/tmp/$$ +status=1 # failure is the default! + +_cleanup() +{ + rm -f $TEST_IMG.snap + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt qcow +_supported_proto generic +_supported_os Linux + +offset_backing_file_offset=8 +offset_backing_file_size=16 +offset_size=24 +offset_cluster_bits=32 +offset_l2_bits=33 + +echo +echo "== Invalid cluster size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_cluster_bits" "\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_cluster_bits" "\x1f" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_cluster_bits" "\x08" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_cluster_bits" "\x11" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid L2 table size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_l2_bits" "\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_l2_bits" "\x05" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_l2_bits" "\x0e" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +# 1 << 0x1b = 2^31 / L2_CACHE_SIZE +poke_file "$TEST_IMG" "$offset_l2_bits" "\x1b" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid size ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_size" "\xee\xee\xee\xee\xee\xee\xee\xee" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_size" "\x7f\xff\xff\xff\xff\xff\xff\xff" +{ $QEMU_IO -c "write 0 64M" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "== Invalid backing file length ==" +_make_test_img 64M +poke_file "$TEST_IMG" "$offset_backing_file_offset" "\x00\x00\x00\xff" +poke_file "$TEST_IMG" "$offset_backing_file_size" "\xff\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir +poke_file "$TEST_IMG" "$offset_backing_file_size" "\x7f\xff\xff\xff" +{ $QEMU_IO -c "read 0 512" $TEST_IMG; } 2>&1 | _filter_qemu_io | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/092.out b/tests/qemu-iotests/092.out new file mode 100644 index 0000000000..496d8f0a63 --- /dev/null +++ b/tests/qemu-iotests/092.out @@ -0,0 +1,38 @@ +QA output created by 092 + +== Invalid cluster size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: Cluster size must be between 512 and 64k +no file open, try 'help open' + +== Invalid L2 table size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: L2 table size must be between 512 and 64k +no file open, try 'help open' + +== Invalid size == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow: Image too large +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: Image too large +no file open, try 'help open' + +== Invalid backing file length == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long +no file open, try 'help open' +qemu-io: can't open device TEST_DIR/t.qcow: Backing file name too long +no file open, try 'help open' +*** done diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 8cde7f11fa..f8c1b56a50 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -128,7 +128,10 @@ common options check options -raw test raw (default) + -bochs test bochs -cow test cow + -cloop test cloop + -parallels test parallels -qcow test qcow -qcow2 test qcow2 -qed test qed @@ -162,11 +165,29 @@ testlist options xpand=false ;; + -bochs) + IMGFMT=bochs + IMGFMT_GENERIC=false + xpand=false + ;; + -cow) IMGFMT=cow xpand=false ;; + -cloop) + IMGFMT=cloop + IMGFMT_GENERIC=false + xpand=false + ;; + + -parallels) + IMGFMT=parallels + IMGFMT_GENERIC=false + xpand=false + ;; + -qcow) IMGFMT=qcow xpand=false diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 7f6245770a..65c5bc118a 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -350,6 +350,9 @@ _fail() # _supported_fmt() { + # "generic" is suitable for most image formats. For some formats it doesn't + # work, however (most notably read-only formats), so they can opt out by + # setting IMGFMT_GENERIC to false. for f; do if [ "$f" = "$IMGFMT" -o "$f" = "generic" -a "$IMGFMT_GENERIC" = "true" ]; then return diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index b63b18c7aa..b5abc5b8f8 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -77,3 +77,9 @@ 069 rw auto 070 rw auto 073 rw auto +075 rw auto +076 auto +078 rw auto +080 rw auto +088 rw auto +092 rw auto quick diff --git a/tests/qemu-iotests/sample_images/empty.bochs.bz2 b/tests/qemu-iotests/sample_images/empty.bochs.bz2 new file mode 100644 index 0000000000..7a29c6ed76 Binary files /dev/null and b/tests/qemu-iotests/sample_images/empty.bochs.bz2 differ diff --git a/tests/qemu-iotests/sample_images/fake.parallels.bz2 b/tests/qemu-iotests/sample_images/fake.parallels.bz2 new file mode 100644 index 0000000000..ffb5f13bac Binary files /dev/null and b/tests/qemu-iotests/sample_images/fake.parallels.bz2 differ diff --git a/tests/qemu-iotests/sample_images/simple-pattern.cloop.bz2 b/tests/qemu-iotests/sample_images/simple-pattern.cloop.bz2 new file mode 100644 index 0000000000..a02d2ee4c7 Binary files /dev/null and b/tests/qemu-iotests/sample_images/simple-pattern.cloop.bz2 differ diff --git a/tests/tcg/test_path.c b/tests/tcg/test_path.c index a064eea8fb..f8dd36aab2 100644 --- a/tests/tcg/test_path.c +++ b/tests/tcg/test_path.c @@ -1,12 +1,15 @@ /* Test path override code */ #define _GNU_SOURCE #include "config-host.h" -#include "iov.c" -#include "cutils.c" -#include "path.c" -#include "trace.c" +#include "util/cutils.c" +#include "util/hexdump.c" +#include "util/iov.c" +#include "util/path.c" +#include "util/qemu-timer-common.c" +#include "trace/control.c" +#include "../trace/generated-events.c" #ifdef CONFIG_TRACE_SIMPLE -#include "../trace/simple.c" +#include "trace/simple.c" #endif #include diff --git a/trace-events b/trace-events index 8695e9e5b7..87fe42e37c 100644 --- a/trace-events +++ b/trace-events @@ -81,7 +81,7 @@ mirror_start(void *bs, void *s, void *co, void *opaque) "bs %p s %p co %p opaque mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64 mirror_before_flush(void *s) "s %p" mirror_before_drain(void *s, int64_t cnt) "s %p dirty count %"PRId64 -mirror_before_sleep(void *s, int64_t cnt, int synced) "s %p dirty count %"PRId64" synced %d" +mirror_before_sleep(void *s, int64_t cnt, int synced, uint64_t delay_ns) "s %p dirty count %"PRId64" synced %d delay %"PRIu64"ns" mirror_one_iteration(void *s, int64_t sector_num, int nb_sectors) "s %p sector_num %"PRId64" nb_sectors %d" mirror_iteration_done(void *s, int64_t sector_num, int nb_sectors, int ret) "s %p sector_num %"PRId64" nb_sectors %d ret %d" mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d" @@ -670,6 +670,7 @@ megasas_dcmd_ld_get_list(int cmd, int num, int max) "scmd %d: DCMD LD get list: megasas_dcmd_ld_get_info(int cmd, int ld_id) "scmd %d: DCMD LD get info for dev %d" megasas_dcmd_pd_get_info(int cmd, int pd_id) "scmd %d: DCMD PD get info for dev %d" megasas_dcmd_pd_list_query(int cmd, int flags) "scmd %d: DCMD PD list query flags %x" +megasas_dcmd_ld_list_query(int cmd, int flags) "scmd %d: DCMD LD list query flags %x" megasas_dcmd_unsupported(int cmd, unsigned long size) "scmd %d: set properties len %ld" megasas_abort_frame(int cmd, int abort_cmd) "scmd %d: aborting frame %x" megasas_abort_no_cmd(int cmd, uint64_t context) "scmd %d: no active command for frame context %" PRIx64 "" diff --git a/translate-all.c b/translate-all.c index aeda54dfbd..1c63d78b7d 100644 --- a/translate-all.c +++ b/translate-all.c @@ -96,12 +96,16 @@ typedef struct PageDesc { # define L1_MAP_ADDR_SPACE_BITS TARGET_VIRT_ADDR_SPACE_BITS #endif +/* Size of the L2 (and L3, etc) page tables. */ +#define V_L2_BITS 10 +#define V_L2_SIZE (1 << V_L2_BITS) + /* The bits remaining after N lower levels of page tables. */ #define V_L1_BITS_REM \ - ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % L2_BITS) + ((L1_MAP_ADDR_SPACE_BITS - TARGET_PAGE_BITS) % V_L2_BITS) #if V_L1_BITS_REM < 4 -#define V_L1_BITS (V_L1_BITS_REM + L2_BITS) +#define V_L1_BITS (V_L1_BITS_REM + V_L2_BITS) #else #define V_L1_BITS V_L1_BITS_REM #endif @@ -395,18 +399,18 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) lp = l1_map + ((index >> V_L1_SHIFT) & (V_L1_SIZE - 1)); /* Level 2..N-1. */ - for (i = V_L1_SHIFT / L2_BITS - 1; i > 0; i--) { + for (i = V_L1_SHIFT / V_L2_BITS - 1; i > 0; i--) { void **p = *lp; if (p == NULL) { if (!alloc) { return NULL; } - ALLOC(p, sizeof(void *) * L2_SIZE); + ALLOC(p, sizeof(void *) * V_L2_SIZE); *lp = p; } - lp = p + ((index >> (i * L2_BITS)) & (L2_SIZE - 1)); + lp = p + ((index >> (i * V_L2_BITS)) & (V_L2_SIZE - 1)); } pd = *lp; @@ -414,13 +418,13 @@ static PageDesc *page_find_alloc(tb_page_addr_t index, int alloc) if (!alloc) { return NULL; } - ALLOC(pd, sizeof(PageDesc) * L2_SIZE); + ALLOC(pd, sizeof(PageDesc) * V_L2_SIZE); *lp = pd; } #undef ALLOC - return pd + (index & (L2_SIZE - 1)); + return pd + (index & (V_L2_SIZE - 1)); } static inline PageDesc *page_find(tb_page_addr_t index) @@ -655,14 +659,14 @@ static void page_flush_tb_1(int level, void **lp) if (level == 0) { PageDesc *pd = *lp; - for (i = 0; i < L2_SIZE; ++i) { + for (i = 0; i < V_L2_SIZE; ++i) { pd[i].first_tb = NULL; invalidate_page_bitmap(pd + i); } } else { void **pp = *lp; - for (i = 0; i < L2_SIZE; ++i) { + for (i = 0; i < V_L2_SIZE; ++i) { page_flush_tb_1(level - 1, pp + i); } } @@ -673,7 +677,7 @@ static void page_flush_tb(void) int i; for (i = 0; i < V_L1_SIZE; i++) { - page_flush_tb_1(V_L1_SHIFT / L2_BITS - 1, l1_map + i); + page_flush_tb_1(V_L1_SHIFT / V_L2_BITS - 1, l1_map + i); } } @@ -1600,7 +1604,7 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data, if (level == 0) { PageDesc *pd = *lp; - for (i = 0; i < L2_SIZE; ++i) { + for (i = 0; i < V_L2_SIZE; ++i) { int prot = pd[i].flags; pa = base | (i << TARGET_PAGE_BITS); @@ -1614,9 +1618,9 @@ static int walk_memory_regions_1(struct walk_memory_regions_data *data, } else { void **pp = *lp; - for (i = 0; i < L2_SIZE; ++i) { + for (i = 0; i < V_L2_SIZE; ++i) { pa = base | ((abi_ulong)i << - (TARGET_PAGE_BITS + L2_BITS * level)); + (TARGET_PAGE_BITS + V_L2_BITS * level)); rc = walk_memory_regions_1(data, pa, level - 1, pp + i); if (rc != 0) { return rc; @@ -1639,7 +1643,7 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn) for (i = 0; i < V_L1_SIZE; i++) { int rc = walk_memory_regions_1(&data, (abi_ulong)i << V_L1_SHIFT, - V_L1_SHIFT / L2_BITS - 1, l1_map + i); + V_L1_SHIFT / V_L2_BITS - 1, l1_map + i); if (rc != 0) { return rc; diff --git a/translate-all.h b/translate-all.h index 5c38819eb8..f7e5932d65 100644 --- a/translate-all.h +++ b/translate-all.h @@ -19,13 +19,6 @@ #ifndef TRANSLATE_ALL_H #define TRANSLATE_ALL_H -/* Size of the L2 (and L3, etc) page tables. */ -#define L2_BITS 10 -#define L2_SIZE (1 << L2_BITS) - -#define P_L2_LEVELS \ - (((TARGET_PHYS_ADDR_SPACE_BITS - TARGET_PAGE_BITS - 1) / L2_BITS) + 1) - /* translate-all.c */ void tb_invalidate_phys_page_fast(tb_page_addr_t start, int len); void cpu_unlink_tb(CPUState *cpu); diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index e6966aebc3..59b59c0c79 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -330,7 +330,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h) } else { errors = tight_detect_smooth_image16(vs, w, h); } - if (quality != -1) { + if (quality != (uint8_t)-1) { return (errors < tight_conf[quality].jpeg_threshold); } return (errors < tight_conf[compression].gradient_threshold); diff --git a/vl.c b/vl.c index f9af0582e5..6b759de490 100644 --- a/vl.c +++ b/vl.c @@ -601,6 +601,7 @@ typedef struct { static const RunStateTransition runstate_transitions_def[] = { /* from -> to */ { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, + { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, { RUN_STATE_INMIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_INMIGRATE, RUN_STATE_PAUSED }, @@ -3500,11 +3501,16 @@ int main(int argc, char **argv, char **envp) } case QEMU_OPTION_acpitable: opts = qemu_opts_parse(qemu_find_opts("acpi"), optarg, 1); - g_assert(opts != NULL); + if (!opts) { + exit(1); + } do_acpitable_option(opts); break; case QEMU_OPTION_smbios: opts = qemu_opts_parse(qemu_find_opts("smbios"), optarg, 0); + if (!opts) { + exit(1); + } do_smbios_option(opts); break; case QEMU_OPTION_enable_kvm: