diff options
102 files changed, 1431 insertions, 565 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index adc59735a9..026ea4f021 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -937,6 +937,11 @@ M: Peter Lieven <pl@kamp.de> S: Supported F: block/iscsi.c +NFS +M: Peter Lieven <pl@kamp.de> +S: Maintained +F: block/nfs.c + SSH M: Richard W.M. Jones <rjones@redhat.com> S: Supported diff --git a/block.c b/block.c index cb21a5fa61..636aa117c3 100644 --- a/block.c +++ b/block.c @@ -832,6 +832,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockDriverState *file, filename = qdict_get_try_str(options, "filename"); } + if (drv->bdrv_needs_filename && !filename) { + error_setg(errp, "The '%s' block driver requires a file name", + drv->format_name); + return -EINVAL; + } + trace_bdrv_open_common(bs, filename ?: "", flags, drv->format_name); node_name = qdict_get_try_str(options, "node-name"); @@ -1031,11 +1037,6 @@ int bdrv_file_open(BlockDriverState **pbs, const char *filename, goto fail; } qdict_del(options, "filename"); - } else if (drv->bdrv_needs_filename && !filename) { - error_setg(errp, "The '%s' block driver requires a file name", - drv->format_name); - ret = -EINVAL; - goto fail; } if (!drv->bdrv_file_open) { @@ -2239,11 +2240,11 @@ static void tracked_request_begin(BdrvTrackedRequest *req, QLIST_INSERT_HEAD(&bs->tracked_requests, req, list); } -static void mark_request_serialising(BdrvTrackedRequest *req, size_t align) +static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align) { int64_t overlap_offset = req->offset & ~(align - 1); - int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) - - overlap_offset; + unsigned int overlap_bytes = ROUND_UP(req->offset + req->bytes, align) + - overlap_offset; if (!req->serialising) { req->bs->serialising_in_flight++; @@ -2914,8 +2915,8 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs, } total_sectors = DIV_ROUND_UP(len, BDRV_SECTOR_SIZE); - max_nb_sectors = MAX(0, ROUND_UP(total_sectors - sector_num, - align >> BDRV_SECTOR_BITS)); + max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num), + align >> BDRV_SECTOR_BITS); if (max_nb_sectors > 0) { ret = drv->bdrv_co_readv(bs, sector_num, MIN(nb_sectors, max_nb_sectors), qiov); @@ -3133,6 +3134,8 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs, waited = wait_serialising_requests(req); assert(!waited || !req->serialising); + assert(req->overlap_offset <= offset); + assert(offset + bytes <= req->overlap_offset + req->overlap_bytes); ret = notifier_with_return_list_notify(&bs->before_write_notifiers, req); @@ -3278,9 +3281,9 @@ fail: if (use_local_qiov) { qemu_iovec_destroy(&local_qiov); - qemu_vfree(head_buf); - qemu_vfree(tail_buf); } + qemu_vfree(head_buf); + qemu_vfree(tail_buf); return ret; } diff --git a/block/Makefile.objs b/block/Makefile.objs index 4e8c91ec34..e254a2180a 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -12,6 +12,7 @@ block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o ifeq ($(CONFIG_POSIX),y) block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o +block-obj-$(CONFIG_LIBNFS) += nfs.o block-obj-$(CONFIG_CURL) += curl.o block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o diff --git a/block/blkdebug.c b/block/blkdebug.c index 56c4cd084f..8eb0db0723 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -396,14 +396,14 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, if (error_is_set(&local_err)) { error_propagate(errp, local_err); ret = -EINVAL; - goto fail; + goto out; } /* Read rules from config file or command line options */ config = qemu_opt_get(opts, "config"); ret = read_config(s, config, options, errp); if (ret) { - goto fail; + goto out; } /* Set initial state */ @@ -414,7 +414,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, flags, true, false, &local_err); if (ret < 0) { error_propagate(errp, local_err); - goto fail; + goto out; } /* Set request alignment */ @@ -424,11 +424,15 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, } else { error_setg(errp, "Invalid alignment"); ret = -EINVAL; - goto fail; + goto fail_unref; } ret = 0; -fail: + goto out; + +fail_unref: + bdrv_unref(bs->file); +out: qemu_opts_del(opts); return ret; } diff --git a/block/iscsi.c b/block/iscsi.c index 6f4af72a75..8d0f9667c5 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -1330,10 +1330,9 @@ static int iscsi_refresh_limits(BlockDriverState *bs) } bs->bl.write_zeroes_alignment = sector_lun2qemu(iscsilun->bl.opt_unmap_gran, iscsilun); - - bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len, - iscsilun); } + bs->bl.opt_transfer_length = sector_lun2qemu(iscsilun->bl.opt_xfer_len, + iscsilun); return 0; } diff --git a/block/nfs.c b/block/nfs.c new file mode 100644 index 0000000000..ef731f04e3 --- /dev/null +++ b/block/nfs.c @@ -0,0 +1,439 @@ +/* + * QEMU Block driver for native access to files on NFS shares + * + * Copyright (c) 2014 Peter Lieven <pl@kamp.de> + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "config-host.h" + +#include <poll.h> +#include "qemu-common.h" +#include "qemu/config-file.h" +#include "qemu/error-report.h" +#include "block/block_int.h" +#include "trace.h" +#include "qemu/iov.h" +#include "qemu/uri.h" +#include "sysemu/sysemu.h" +#include <nfsc/libnfs.h> + +typedef struct NFSClient { + struct nfs_context *context; + struct nfsfh *fh; + int events; + bool has_zero_init; +} NFSClient; + +typedef struct NFSRPC { + int ret; + int complete; + QEMUIOVector *iov; + struct stat *st; + Coroutine *co; + QEMUBH *bh; +} NFSRPC; + +static void nfs_process_read(void *arg); +static void nfs_process_write(void *arg); + +static void nfs_set_events(NFSClient *client) +{ + int ev = nfs_which_events(client->context); + if (ev != client->events) { + qemu_aio_set_fd_handler(nfs_get_fd(client->context), + (ev & POLLIN) ? nfs_process_read : NULL, + (ev & POLLOUT) ? nfs_process_write : NULL, + client); + + } + client->events = ev; +} + +static void nfs_process_read(void *arg) +{ + NFSClient *client = arg; + nfs_service(client->context, POLLIN); + nfs_set_events(client); +} + +static void nfs_process_write(void *arg) +{ + NFSClient *client = arg; + nfs_service(client->context, POLLOUT); + nfs_set_events(client); +} + +static void nfs_co_init_task(NFSClient *client, NFSRPC *task) +{ + *task = (NFSRPC) { + .co = qemu_coroutine_self(), + }; +} + +static void nfs_co_generic_bh_cb(void *opaque) +{ + NFSRPC *task = opaque; + qemu_bh_delete(task->bh); + qemu_coroutine_enter(task->co, NULL); +} + +static void +nfs_co_generic_cb(int ret, struct nfs_context *nfs, void *data, + void *private_data) +{ + NFSRPC *task = private_data; + task->complete = 1; + task->ret = ret; + if (task->ret > 0 && task->iov) { + if (task->ret <= task->iov->size) { + qemu_iovec_from_buf(task->iov, 0, data, task->ret); + } else { + task->ret = -EIO; + } + } + if (task->ret == 0 && task->st) { + memcpy(task->st, data, sizeof(struct stat)); + } + if (task->co) { + task->bh = qemu_bh_new(nfs_co_generic_bh_cb, task); + qemu_bh_schedule(task->bh); + } +} + +static int coroutine_fn nfs_co_readv(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + + nfs_co_init_task(client, &task); + task.iov = iov; + + if (nfs_pread_async(client->context, client->fh, + sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, + nfs_co_generic_cb, &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + if (task.ret < 0) { + return task.ret; + } + + /* zero pad short reads */ + if (task.ret < iov->size) { + qemu_iovec_memset(iov, task.ret, 0, iov->size - task.ret); + } + + return 0; +} + +static int coroutine_fn nfs_co_writev(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, + QEMUIOVector *iov) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + char *buf = NULL; + + nfs_co_init_task(client, &task); + + buf = g_malloc(nb_sectors * BDRV_SECTOR_SIZE); + qemu_iovec_to_buf(iov, 0, buf, nb_sectors * BDRV_SECTOR_SIZE); + + if (nfs_pwrite_async(client->context, client->fh, + sector_num * BDRV_SECTOR_SIZE, + nb_sectors * BDRV_SECTOR_SIZE, + buf, nfs_co_generic_cb, &task) != 0) { + g_free(buf); + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + g_free(buf); + + if (task.ret != nb_sectors * BDRV_SECTOR_SIZE) { + return task.ret < 0 ? task.ret : -EIO; + } + + return 0; +} + +static int coroutine_fn nfs_co_flush(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + NFSRPC task; + + nfs_co_init_task(client, &task); + + if (nfs_fsync_async(client->context, client->fh, nfs_co_generic_cb, + &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_coroutine_yield(); + } + + return task.ret; +} + +/* TODO Convert to fine grained options */ +static QemuOptsList runtime_opts = { + .name = "nfs", + .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), + .desc = { + { + .name = "filename", + .type = QEMU_OPT_STRING, + .help = "URL to the NFS file", + }, + { /* end of list */ } + }, +}; + +static void nfs_client_close(NFSClient *client) +{ + if (client->context) { + if (client->fh) { + nfs_close(client->context, client->fh); + } + qemu_aio_set_fd_handler(nfs_get_fd(client->context), NULL, NULL, NULL); + nfs_destroy_context(client->context); + } + memset(client, 0, sizeof(NFSClient)); +} + +static void nfs_file_close(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + nfs_client_close(client); +} + +static int64_t nfs_client_open(NFSClient *client, const char *filename, + int flags, Error **errp) +{ + int ret = -EINVAL, i; + struct stat st; + URI *uri; + QueryParams *qp = NULL; + char *file = NULL, *strp = NULL; + + uri = uri_parse(filename); + if (!uri) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + strp = strrchr(uri->path, '/'); + if (strp == NULL) { + error_setg(errp, "Invalid URL specified"); + goto fail; + } + file = g_strdup(strp); + *strp = 0; + + client->context = nfs_init_context(); + if (client->context == NULL) { + error_setg(errp, "Failed to init NFS context"); + goto fail; + } + + qp = query_params_parse(uri->query); + for (i = 0; i < qp->n; i++) { + if (!qp->p[i].value) { + error_setg(errp, "Value for NFS parameter expected: %s", + qp->p[i].name); + goto fail; + } + if (!strncmp(qp->p[i].name, "uid", 3)) { + nfs_set_uid(client->context, atoi(qp->p[i].value)); + } else if (!strncmp(qp->p[i].name, "gid", 3)) { + nfs_set_gid(client->context, atoi(qp->p[i].value)); + } else if (!strncmp(qp->p[i].name, "tcp-syncnt", 10)) { + nfs_set_tcp_syncnt(client->context, atoi(qp->p[i].value)); + } else { + error_setg(errp, "Unknown NFS parameter name: %s", + qp->p[i].name); + goto fail; + } + } + + ret = nfs_mount(client->context, uri->server, uri->path); + if (ret < 0) { + error_setg(errp, "Failed to mount nfs share: %s", + nfs_get_error(client->context)); + goto fail; + } + + if (flags & O_CREAT) { + ret = nfs_creat(client->context, file, 0600, &client->fh); + if (ret < 0) { + error_setg(errp, "Failed to create file: %s", + nfs_get_error(client->context)); + goto fail; + } + } else { + ret = nfs_open(client->context, file, flags, &client->fh); + if (ret < 0) { + error_setg(errp, "Failed to open file : %s", + nfs_get_error(client->context)); + goto fail; + } + } + + ret = nfs_fstat(client->context, client->fh, &st); + if (ret < 0) { + error_setg(errp, "Failed to fstat file: %s", + nfs_get_error(client->context)); + goto fail; + } + + ret = DIV_ROUND_UP(st.st_size, BDRV_SECTOR_SIZE); + client->has_zero_init = S_ISREG(st.st_mode); + goto out; +fail: + nfs_client_close(client); +out: + if (qp) { + query_params_free(qp); + } + uri_free(uri); + g_free(file); + return ret; +} + +static int nfs_file_open(BlockDriverState *bs, QDict *options, int flags, + Error **errp) { + NFSClient *client = bs->opaque; + int64_t ret; + QemuOpts *opts; + Error *local_err = NULL; + + opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options, &local_err); + if (error_is_set(&local_err)) { + error_propagate(errp, local_err); + return -EINVAL; + } + ret = nfs_client_open(client, qemu_opt_get(opts, "filename"), + (flags & BDRV_O_RDWR) ? O_RDWR : O_RDONLY, + errp); + if (ret < 0) { + return ret; + } + bs->total_sectors = ret; + return 0; +} + +static int nfs_file_create(const char *url, QEMUOptionParameter *options, + Error **errp) +{ + int ret = 0; + int64_t total_size = 0; + NFSClient *client = g_malloc0(sizeof(NFSClient)); + + /* Read out options */ + while (options && options->name) { + if (!strcmp(options->name, "size")) { + total_size = options->value.n; + } + options++; + } + + ret = nfs_client_open(client, url, O_CREAT, errp); + if (ret < 0) { + goto out; + } + ret = nfs_ftruncate(client->context, client->fh, total_size); + nfs_client_close(client); +out: + g_free(client); + return ret; +} + +static int nfs_has_zero_init(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + return client->has_zero_init; +} + +static int64_t nfs_get_allocated_file_size(BlockDriverState *bs) +{ + NFSClient *client = bs->opaque; + NFSRPC task = {0}; + struct stat st; + + task.st = &st; + if (nfs_fstat_async(client->context, client->fh, nfs_co_generic_cb, + &task) != 0) { + return -ENOMEM; + } + + while (!task.complete) { + nfs_set_events(client); + qemu_aio_wait(); + } + + return (task.ret < 0 ? task.ret : st.st_blocks * st.st_blksize); +} + +static int nfs_file_truncate(BlockDriverState *bs, int64_t offset) +{ + NFSClient *client = bs->opaque; + return nfs_ftruncate(client->context, client->fh, offset); +} + +static BlockDriver bdrv_nfs = { + .format_name = "nfs", + .protocol_name = "nfs", + + .instance_size = sizeof(NFSClient), + .bdrv_needs_filename = true, + .bdrv_has_zero_init = nfs_has_zero_init, + .bdrv_get_allocated_file_size = nfs_get_allocated_file_size, + .bdrv_truncate = nfs_file_truncate, + + .bdrv_file_open = nfs_file_open, + .bdrv_close = nfs_file_close, + .bdrv_create = nfs_file_create, + + .bdrv_co_readv = nfs_co_readv, + .bdrv_co_writev = nfs_co_writev, + .bdrv_co_flush_to_disk = nfs_co_flush, +}; + +static void nfs_block_init(void) +{ + bdrv_register(&bdrv_nfs); +} + +block_init(nfs_block_init); diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index 853408438a..c57f39dd2b 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -1182,7 +1182,7 @@ fail: * Return 0 on success and -errno in error cases */ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, - int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m) + int *num, uint64_t *host_offset, QCowL2Meta **m) { BDRVQcowState *s = bs->opaque; uint64_t start, remaining; @@ -1190,15 +1190,13 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, uint64_t cur_bytes; int ret; - trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, - n_start, n_end); + trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *num); - assert(n_start * BDRV_SECTOR_SIZE == offset_into_cluster(s, offset)); - offset = start_of_cluster(s, offset); + assert((offset & ~BDRV_SECTOR_MASK) == 0); again: - start = offset + (n_start << BDRV_SECTOR_BITS); - remaining = (n_end - n_start) << BDRV_SECTOR_BITS; + start = offset; + remaining = *num << BDRV_SECTOR_BITS; cluster_offset = 0; *host_offset = 0; cur_bytes = 0; @@ -1284,7 +1282,7 @@ again: } } - *num = (n_end - n_start) - (remaining >> BDRV_SECTOR_BITS); + *num -= remaining >> BDRV_SECTOR_BITS; assert(*num > 0); assert(*host_offset != 0); diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index c974abe795..8712d8bd54 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -676,7 +676,13 @@ 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; + + assert(nb_clusters >= 0); + if (nb_clusters == 0) { + return 0; + } /* Check how many clusters there are free */ cluster_index = offset >> s->cluster_bits; diff --git a/block/qcow2.c b/block/qcow2.c index 99a1ad13e6..0b4335ca5b 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1000,7 +1000,6 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, { BDRVQcowState *s = bs->opaque; int index_in_cluster; - int n_end; int ret; int cur_nr_sectors; /* number of sectors in current iteration */ uint64_t cluster_offset; @@ -1024,14 +1023,16 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, trace_qcow2_writev_start_part(qemu_coroutine_self()); index_in_cluster = sector_num & (s->cluster_sectors - 1); - n_end = index_in_cluster + remaining_sectors; + cur_nr_sectors = remaining_sectors; if (s->crypt_method && - n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) { - n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; + cur_nr_sectors > + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) { + cur_nr_sectors = + QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster; } ret = qcow2_alloc_cluster_offset(bs, sector_num << 9, - index_in_cluster, n_end, &cur_nr_sectors, &cluster_offset, &l2meta); + &cur_nr_sectors, &cluster_offset, &l2meta); if (ret < 0) { goto fail; } @@ -1403,34 +1404,34 @@ static int preallocate(BlockDriverState *bs) int ret; QCowL2Meta *meta; - nb_sectors = bdrv_getlength(bs) >> 9; + nb_sectors = bdrv_getlength(bs) >> BDRV_SECTOR_BITS; offset = 0; while (nb_sectors) { - num = MIN(nb_sectors, INT_MAX >> 9); - ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, + num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS); + ret = qcow2_alloc_cluster_offset(bs, offset, &num, &host_offset, &meta); if (ret < 0) { return ret; } - ret = qcow2_alloc_cluster_link_l2(bs, meta); - if (ret < 0) { - qcow2_free_any_clusters(bs, meta->alloc_offset, meta->nb_clusters, - QCOW2_DISCARD_NEVER); - return ret; - } - - /* There are no dependent requests, but we need to remove our request - * from the list of in-flight requests */ if (meta != NULL) { + ret = qcow2_alloc_cluster_link_l2(bs, meta); + if (ret < 0) { + qcow2_free_any_clusters(bs, meta->alloc_offset, + meta->nb_clusters, QCOW2_DISCARD_NEVER); + return ret; + } + + /* There are no dependent requests, but we need to remove our + * request from the list of in-flight requests */ QLIST_REMOVE(meta, next_in_flight); } /* TODO Preallocate data if requested */ nb_sectors -= num; - offset += num << 9; + offset += num << BDRV_SECTOR_BITS; } /* @@ -1439,9 +1440,10 @@ static int preallocate(BlockDriverState *bs) * EOF). Extend the image to the last allocated sector. */ if (host_offset != 0) { - uint8_t buf[512]; - memset(buf, 0, 512); - ret = bdrv_write(bs->file, (host_offset >> 9) + num - 1, buf, 1); + uint8_t buf[BDRV_SECTOR_SIZE]; + memset(buf, 0, BDRV_SECTOR_SIZE); + ret = bdrv_write(bs->file, (host_offset >> BDRV_SECTOR_BITS) + num - 1, + buf, 1); if (ret < 0) { return ret; } diff --git a/block/qcow2.h b/block/qcow2.h index b5b7d13630..0b0eac899c 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -468,7 +468,7 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int *num, uint64_t *cluster_offset); int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, - int n_start, int n_end, int *num, uint64_t *host_offset, QCowL2Meta **m); + int *num, uint64_t *host_offset, QCowL2Meta **m); uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size); diff --git a/block/raw_bsd.c b/block/raw_bsd.c index 978ae7a102..af8706dc97 100644 --- a/block/raw_bsd.c +++ b/block/raw_bsd.c @@ -90,6 +90,12 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return bdrv_get_info(bs->file, bdi); } +static int raw_refresh_limits(BlockDriverState *bs) +{ + bs->bl = bs->file->bl; + return 0; +} + static int raw_truncate(BlockDriverState *bs, int64_t offset) { return bdrv_truncate(bs->file, offset); @@ -150,7 +156,6 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { bs->sg = bs->file->sg; - bs->bl = bs->file->bl; return 0; } @@ -182,6 +187,7 @@ static BlockDriver bdrv_raw = { .bdrv_getlength = &raw_getlength, .has_variable_length = true, .bdrv_get_info = &raw_get_info, + .bdrv_refresh_limits = &raw_refresh_limits, .bdrv_is_inserted = &raw_is_inserted, .bdrv_media_changed = &raw_media_changed, .bdrv_eject = &raw_eject, diff --git a/configure b/configure index 8f3cc204cd..88133a100e 100755 --- a/configure +++ b/configure @@ -251,6 +251,7 @@ vss_win32_sdk="" win_sdk="no" want_tools="yes" libiscsi="" +libnfs="" coroutine="" coroutine_pool="" seccomp="" @@ -840,6 +841,10 @@ for opt do ;; --enable-libiscsi) libiscsi="yes" ;; + --disable-libnfs) libnfs="no" + ;; + --enable-libnfs) libnfs="yes" + ;; --enable-profiler) profiler="yes" ;; --disable-cocoa) cocoa="no" @@ -1229,6 +1234,8 @@ Advanced options (experts only): --enable-rbd enable building the rados block device (rbd) --disable-libiscsi disable iscsi support --enable-libiscsi enable iscsi support + --disable-libnfs disable nfs support + --enable-libnfs enable nfs support --disable-smartcard-nss disable smartcard nss support --enable-smartcard-nss enable smartcard nss support --disable-libusb disable libusb (for usb passthrough) @@ -3600,6 +3607,20 @@ elif test "$debug" = "no" ; then CFLAGS="-O2 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 $CFLAGS" fi +########################################## +# Do we have libnfs +if test "$libnfs" != "no" ; then + if $pkg_config --atleast-version=1.9.2 libnfs; then + libnfs="yes" + libnfs_libs=$($pkg_config --libs libnfs) + LIBS="$LIBS $libnfs_libs" + else + if test "$libnfs" = "yes" ; then + feature_not_found "libnfs" + fi + libnfs="no" + fi +fi # Disable zero malloc errors for official releases unless explicitly told to # enable/disable @@ -3829,6 +3850,7 @@ echo "libiscsi support $libiscsi (1.4.0)" else echo "libiscsi support $libiscsi" fi +echo "libnfs support $libnfs" echo "build guest agent $guest_agent" echo "QGA VSS support $guest_agent_with_vss" echo "seccomp support $seccomp" @@ -4165,6 +4187,10 @@ if test "$libiscsi" = "yes" ; then fi fi +if test "$libnfs" = "yes" ; then + echo "CONFIG_LIBNFS=y" >> $config_host_mak +fi + if test "$seccomp" = "yes"; then echo "CONFIG_SECCOMP=y" >> $config_host_mak fi diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index f5cd0bdcc0..f380ca47d7 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -41,6 +41,7 @@ CONFIG_I8259=y CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_OPENPIC=y +CONFIG_PREP=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) # For PReP diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index fb34a9b074..fcc04529b4 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -42,6 +42,7 @@ CONFIG_XILINX=y CONFIG_XILINX_ETHLITE=y CONFIG_OPENPIC=y CONFIG_PSERIES=y +CONFIG_PREP=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) # For pSeries diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak index 4411203a9a..fdf1e14a95 100644 --- a/default-configs/ppcemb-softmmu.mak +++ b/default-configs/ppcemb-softmmu.mak @@ -10,11 +10,9 @@ CONFIG_VGA=y CONFIG_VGA_PCI=y CONFIG_SERIAL=y CONFIG_I8254=y -CONFIG_PCKBD=y CONFIG_FDC=y CONFIG_I8257=y CONFIG_OPENPIC=y -CONFIG_PREP_PCI=y CONFIG_MACIO=y CONFIG_CUDA=y CONFIG_ADB=y @@ -38,6 +36,3 @@ CONFIG_XILINX_ETHLITE=y CONFIG_OPENPIC=y CONFIG_E500=y CONFIG_OPENPIC_KVM=$(and $(CONFIG_E500),$(CONFIG_KVM)) -# For PReP -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 3fa3d7c290..f80c48008c 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -46,13 +46,15 @@ # define ACPI_PCIHP_DPRINTF(format, ...) do { } while (0) #endif -#define PCI_HOTPLUG_ADDR 0xae00 -#define PCI_HOTPLUG_SIZE 0x0014 -#define PCI_UP_BASE 0xae00 -#define PCI_DOWN_BASE 0xae04 -#define PCI_EJ_BASE 0xae08 -#define PCI_RMV_BASE 0xae0c -#define PCI_SEL_BASE 0xae10 +#define ACPI_PCI_HOTPLUG_STATUS 2 +#define ACPI_PCIHP_ADDR 0xae00 +#define ACPI_PCIHP_SIZE 0x0014 +#define ACPI_PCIHP_LEGACY_SIZE 0x000f +#define PCI_UP_BASE 0x0000 +#define PCI_DOWN_BASE 0x0004 +#define PCI_EJ_BASE 0x0008 +#define PCI_RMV_BASE 0x000c +#define PCI_SEL_BASE 0x0010 typedef struct AcpiPciHpFind { int bsel; @@ -104,19 +106,19 @@ static PCIBus *acpi_pcihp_find_hotplug_bus(AcpiPciHpState *s, int bsel) static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) { PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); + DeviceClass *dc = DEVICE_GET_CLASS(dev); /* * ACPI doesn't allow hotplug of bridge devices. Don't allow * hot-unplug of bridge devices unless they were added by hotplug * (and so, not described by acpi). */ - return (pc->is_bridge && !dev->qdev.hotplugged) || pc->no_hotplug; + return (pc->is_bridge && !dev->qdev.hotplugged) || !dc->hotpluggable; } static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) { BusChild *kid, *next; int slot = ffs(slots) - 1; - bool slot_free = true; PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); if (!bus) { @@ -125,21 +127,17 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo /* Mark request as complete */ s->acpi_pcihp_pci_status[bsel].down &= ~(1U << slot); + s->acpi_pcihp_pci_status[bsel].up &= ~(1U << slot); QTAILQ_FOREACH_SAFE(kid, &bus->qbus.children, sibling, next) { DeviceState *qdev = kid->child; PCIDevice *dev = PCI_DEVICE(qdev); if (PCI_SLOT(dev->devfn) == slot) { - if (acpi_pcihp_pc_no_hotplug(s, dev)) { - slot_free = false; - } else { + if (!acpi_pcihp_pc_no_hotplug(s, dev)) { object_unparent(OBJECT(qdev)); } } } - if (slot_free) { - s->acpi_pcihp_pci_status[bsel].device_present &= ~(1U << slot); - } } static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel) @@ -153,7 +151,6 @@ static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel) } s->acpi_pcihp_pci_status[bsel].hotplug_enable = ~0; - s->acpi_pcihp_pci_status[bsel].device_present = 0; if (!bus) { return; @@ -166,8 +163,6 @@ static void acpi_pcihp_update_hotplug_bus(AcpiPciHpState *s, int bsel) if (acpi_pcihp_pc_no_hotplug(s, pdev)) { s->acpi_pcihp_pci_status[bsel].hotplug_enable &= ~(1U << slot); } - - s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot); } } @@ -185,40 +180,47 @@ void acpi_pcihp_reset(AcpiPciHpState *s) acpi_pcihp_update(s); } -static void enable_device(AcpiPciHpState *s, unsigned bsel, int slot) -{ - s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot); -} - -static void disable_device(AcpiPciHpState *s, unsigned bsel, int slot) -{ - s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); -} - -int acpi_pcihp_device_hotplug(AcpiPciHpState *s, PCIDevice *dev, - PCIHotplugState state) +void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, + DeviceState *dev, Error **errp) { - int slot = PCI_SLOT(dev->devfn); - int bsel = acpi_pcihp_get_bsel(dev->bus); + PCIDevice *pdev = PCI_DEVICE(dev); + int slot = PCI_SLOT(pdev->devfn); + int bsel = acpi_pcihp_get_bsel(pdev->bus); if (bsel < 0) { - return -1; + error_setg(errp, "Unsupported bus. Bus doesn't have property '" + ACPI_PCIHP_PROP_BSEL "' set"); + return; } /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an * event when the device is disabled later. */ - if (state == PCI_COLDPLUG_ENABLED) { - s->acpi_pcihp_pci_status[bsel].device_present |= (1U << slot); - return 0; + if (!dev->hotplugged) { + return; } - if (state == PCI_HOTPLUG_ENABLED) { - enable_device(s, bsel, slot); - } else { - disable_device(s, bsel, slot); + s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); + + ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS; + acpi_update_sci(ar, irq); +} + +void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, + DeviceState *dev, Error **errp) +{ + PCIDevice *pdev = PCI_DEVICE(dev); + int slot = PCI_SLOT(pdev->devfn); + int bsel = acpi_pcihp_get_bsel(pdev->bus); + if (bsel < 0) { + error_setg(errp, "Unsupported bus. Bus doesn't have property '" + ACPI_PCIHP_PROP_BSEL "' set"); + return; } - return 0; + s->acpi_pcihp_pci_status[bsel].down |= (1U << slot); + + ar->gpe.sts[0] |= ACPI_PCI_HOTPLUG_STATUS; + acpi_update_sci(ar, irq); } static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) @@ -232,26 +234,26 @@ static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) } switch (addr) { - case PCI_UP_BASE - PCI_HOTPLUG_ADDR: - /* Manufacture an "up" value to cause a device check on any hotplug - * slot with a device. Extra device checks are harmless. */ - val = s->acpi_pcihp_pci_status[bsel].device_present & - s->acpi_pcihp_pci_status[bsel].hotplug_enable; + case PCI_UP_BASE: + val = s->acpi_pcihp_pci_status[bsel].up; + if (!s->legacy_piix) { + s->acpi_pcihp_pci_status[bsel].up = 0; + } ACPI_PCIHP_DPRINTF("pci_up_read %" PRIu32 "\n", val); break; - case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR: + case PCI_DOWN_BASE: val = s->acpi_pcihp_pci_status[bsel].down; ACPI_PCIHP_DPRINTF("pci_down_read %" PRIu32 "\n", val); break; - case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: + case PCI_EJ_BASE: /* No feature defined yet */ ACPI_PCIHP_DPRINTF("pci_features_read %" PRIu32 "\n", val); break; - case PCI_RMV_BASE - PCI_HOTPLUG_ADDR: + case PCI_RMV_BASE: val = s->acpi_pcihp_pci_status[bsel].hotplug_enable; ACPI_PCIHP_DPRINTF("pci_rmv_read %" PRIu32 "\n", val); break; - case PCI_SEL_BASE - PCI_HOTPLUG_ADDR: + case PCI_SEL_BASE: val = s->hotplug_select; ACPI_PCIHP_DPRINTF("pci_sel_read %" PRIu32 "\n", val); default: @@ -266,7 +268,7 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data, { AcpiPciHpState *s = opaque; switch (addr) { - case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: + case PCI_EJ_BASE: if (s->hotplug_select >= ACPI_PCIHP_MAX_HOTPLUG_BUS) { break; } @@ -274,7 +276,7 @@ static void pci_write(void *opaque, hwaddr addr, uint64_t data, ACPI_PCIHP_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, data); break; - case PCI_SEL_BASE - PCI_HOTPLUG_ADDR: + case PCI_SEL_BASE: s->hotplug_select = data; ACPI_PCIHP_DPRINTF("pcisel write %" HWADDR_PRIx " <== %" PRIu64 "\n", addr, data); @@ -294,13 +296,26 @@ static const MemoryRegionOps acpi_pcihp_io_ops = { }; void acpi_pcihp_init(AcpiPciHpState *s, PCIBus *root_bus, - MemoryRegion *address_space_io) + MemoryRegion *address_space_io, bool bridges_enabled) { + uint16_t io_size = ACPI_PCIHP_SIZE; + s->root= root_bus; + s->legacy_piix = !bridges_enabled; + + if (s->legacy_piix) { + unsigned *bus_bsel = g_malloc(sizeof *bus_bsel); + + io_size = ACPI_PCIHP_LEGACY_SIZE; + + *bus_bsel = ACPI_PCIHP_BSEL_DEFAULT; + object_property_add_uint32_ptr(OBJECT(root_bus), ACPI_PCIHP_PROP_BSEL, + bus_bsel, NULL); + } + memory_region_init_io(&s->io, NULL, &acpi_pcihp_io_ops, s, - "acpi-pci-hotplug", - PCI_HOTPLUG_SIZE); - memory_region_add_subregion(address_space_io, PCI_HOTPLUG_ADDR, &s->io); + "acpi-pci-hotplug", io_size); + memory_region_add_subregion(address_space_io, ACPI_PCIHP_ADDR, &s->io); } const VMStateDescription vmstate_acpi_pcihp_pci_status = { diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 5d55a3c222..9f21653e94 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -32,6 +32,7 @@ #include "hw/acpi/piix4.h" #include "hw/acpi/pcihp.h" #include "hw/acpi/cpu_hotplug.h" +#include "hw/hotplug.h" //#define DEBUG @@ -44,15 +45,6 @@ #define GPE_BASE 0xafe0 #define GPE_LEN 4 -#define PCI_HOTPLUG_ADDR 0xae00 -#define PCI_HOTPLUG_SIZE 0x000f -#define PCI_UP_BASE 0xae00 -#define PCI_DOWN_BASE 0xae04 -#define PCI_EJ_BASE 0xae08 -#define PCI_RMV_BASE 0xae0c - -#define PIIX4_PCI_HOTPLUG_STATUS 2 - struct pci_status { uint32_t up; /* deprecated, maintained for migration compatibility */ uint32_t down; @@ -80,13 +72,6 @@ typedef struct PIIX4PMState { Notifier machine_ready; Notifier powerdown_notifier; - /* for legacy pci hotplug (compatible with qemu 1.6 and older) */ - MemoryRegion io_pci; - struct pci_status pci0_status; - uint32_t pci0_hotplug_enable; - uint32_t pci0_slot_device_present; - - /* for new pci hotplug (with PCI2PCI bridge support) */ AcpiPciHpState acpi_pci_hotplug; bool use_acpi_pci_hotplug; @@ -170,17 +155,6 @@ static void pm_write_config(PCIDevice *d, } } -static void vmstate_pci_status_pre_save(void *opaque) -{ - struct pci_status *pci0_status = opaque; - PIIX4PMState *s = container_of(pci0_status, PIIX4PMState, pci0_status); - - /* We no longer track up, so build a safe value for migrating - * to a version that still does... of course these might get lost - * by an old buggy implementation, but we try. */ - pci0_status->up = s->pci0_slot_device_present & s->pci0_hotplug_enable; -} - static int vmstate_acpi_post_load(void *opaque, int version_id) { PIIX4PMState *s = opaque; @@ -216,10 +190,9 @@ static const VMStateDescription vmstate_pci_status = { .version_id = 1, .minimum_version_id = 1, .minimum_version_id_old = 1, - .pre_save = vmstate_pci_status_pre_save, .fields = (VMStateField []) { - VMSTATE_UINT32(up, struct pci_status), - VMSTATE_UINT32(down, struct pci_status), + VMSTATE_UINT32(up, struct AcpiPciHpPciStatus), + VMSTATE_UINT32(down, struct AcpiPciHpPciStatus), VMSTATE_END_OF_LIST() } }; @@ -256,7 +229,8 @@ static int acpi_load_old(QEMUFile *f, void *opaque, int version_id) qemu_get_be16s(f, &temp); } - ret = vmstate_load_state(f, &vmstate_pci_status, &s->pci0_status, 1); + ret = vmstate_load_state(f, &vmstate_pci_status, + &s->acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], 1); return ret; } @@ -294,70 +268,18 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_TIMER(ar.tmr.timer, PIIX4PMState), VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), - VMSTATE_STRUCT_TEST(pci0_status, PIIX4PMState, - vmstate_test_no_use_acpi_pci_hotplug, - 2, vmstate_pci_status, - struct pci_status), + VMSTATE_STRUCT_TEST( + acpi_pci_hotplug.acpi_pcihp_pci_status[ACPI_PCIHP_BSEL_DEFAULT], + PIIX4PMState, + vmstate_test_no_use_acpi_pci_hotplug, + 2, vmstate_pci_status, + struct AcpiPciHpPciStatus), VMSTATE_PCI_HOTPLUG(acpi_pci_hotplug, PIIX4PMState, vmstate_test_use_acpi_pci_hotplug), VMSTATE_END_OF_LIST() } }; -static void acpi_piix_eject_slot(PIIX4PMState *s, unsigned slots) -{ - BusChild *kid, *next; - BusState *bus = qdev_get_parent_bus(DEVICE(s)); - int slot = ffs(slots) - 1; - bool slot_free = true; - - /* Mark request as complete */ - s->pci0_status.down &= ~(1U << slot); - - QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) { - DeviceState *qdev = kid->child; - PCIDevice *dev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - if (PCI_SLOT(dev->devfn) == slot) { - if (pc->no_hotplug) { - slot_free = false; - } else { - object_unparent(OBJECT(qdev)); - } - } - } - if (slot_free) { - s->pci0_slot_device_present &= ~(1U << slot); - } -} - -static void piix4_update_hotplug(PIIX4PMState *s) -{ - BusState *bus = qdev_get_parent_bus(DEVICE(s)); - BusChild *kid, *next; - - /* Execute any pending removes during reset */ - while (s->pci0_status.down) { - acpi_piix_eject_slot(s, s->pci0_status.down); - } - - s->pci0_hotplug_enable = ~0; - s->pci0_slot_device_present = 0; - - QTAILQ_FOREACH_SAFE(kid, &bus->children, sibling, next) { - DeviceState *qdev = kid->child; - PCIDevice *pdev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pdev); - int slot = PCI_SLOT(pdev->devfn); - - if (pc->no_hotplug) { - s->pci0_hotplug_enable &= ~(1U << slot); - } - - s->pci0_slot_device_present |= (1U << slot); - } -} - static void piix4_reset(void *opaque) { PIIX4PMState *s = opaque; @@ -377,11 +299,7 @@ static void piix4_reset(void *opaque) pci_conf[0x5B] = 0x02; } pm_io_space_update(s); - if (s->use_acpi_pci_hotplug) { - acpi_pcihp_reset(&s->acpi_pci_hotplug); - } else { - piix4_update_hotplug(s); - } + acpi_pcihp_reset(&s->acpi_pci_hotplug); } static void piix4_pm_powerdown_req(Notifier *n, void *opaque) @@ -392,24 +310,26 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&s->ar); } -static int piix4_acpi_pci_hotplug(DeviceState *qdev, PCIDevice *dev, - PCIHotplugState state) +static void piix4_pci_device_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - PIIX4PMState *s = PIIX4_PM(qdev); - int ret = acpi_pcihp_device_hotplug(&s->acpi_pci_hotplug, dev, state); - if (ret < 0) { - return ret; - } - s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; + PIIX4PMState *s = PIIX4_PM(hotplug_dev); + acpi_pcihp_device_plug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, errp); +} - acpi_update_sci(&s->ar, s->irq); - return 0; +static void piix4_pci_device_unplug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PIIX4PMState *s = PIIX4_PM(hotplug_dev); + acpi_pcihp_device_unplug_cb(&s->ar, s->irq, &s->acpi_pci_hotplug, dev, + errp); } -static void piix4_update_bus_hotplug(PCIBus *bus, void *opaque) +static void piix4_update_bus_hotplug(PCIBus *pci_bus, void *opaque) { PIIX4PMState *s = opaque; - pci_bus_hotplug(bus, piix4_acpi_pci_hotplug, DEVICE(s)); + + qbus_set_hotplug_handler(BUS(pci_bus), DEVICE(s), &error_abort); } static void piix4_pm_machine_ready(Notifier *n, void *opaque) @@ -428,6 +348,8 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque) if (s->use_acpi_pci_hotplug) { pci_for_each_bus(d->bus, piix4_update_bus_hotplug, s); + } else { + piix4_update_bus_hotplug(d->bus, s); } } @@ -545,52 +467,6 @@ i2c_bus *piix4_pm_init(PCIBus *bus, int devfn, uint32_t smb_io_base, return s->smb.smbus; } -static Property piix4_pm_properties[] = { - DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), - DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), - DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), - DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), - DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, - use_acpi_pci_hotplug, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void piix4_pm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->no_hotplug = 1; - k->init = piix4_pm_initfn; - k->config_write = pm_write_config; - k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3; - k->revision = 0x03; - k->class_id = PCI_CLASS_BRIDGE_OTHER; - dc->desc = "PM"; - dc->vmsd = &vmstate_acpi; - dc->props = piix4_pm_properties; - /* - * Reason: part of PIIX4 southbridge, needs to be wired up, - * e.g. by mips_malta_init() - */ - dc->cannot_instantiate_with_device_add_yet = true; -} - -static const TypeInfo piix4_pm_info = { - .name = TYPE_PIIX4_PM, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(PIIX4PMState), - .class_init = piix4_pm_class_init, -}; - -static void piix4_pm_register_types(void) -{ - type_register_static(&piix4_pm_info); -} - -type_init(piix4_pm_register_types) - static uint64_t gpe_readb(void *opaque, hwaddr addr, unsigned width) { PIIX4PMState *s = opaque; @@ -621,60 +497,6 @@ static const MemoryRegionOps piix4_gpe_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static uint64_t pci_read(void *opaque, hwaddr addr, unsigned int size) -{ - PIIX4PMState *s = opaque; - uint32_t val = 0; - - switch (addr) { - case PCI_UP_BASE - PCI_HOTPLUG_ADDR: - /* Manufacture an "up" value to cause a device check on any hotplug - * slot with a device. Extra device checks are harmless. */ - val = s->pci0_slot_device_present & s->pci0_hotplug_enable; - PIIX4_DPRINTF("pci_up_read %" PRIu32 "\n", val); - break; - case PCI_DOWN_BASE - PCI_HOTPLUG_ADDR: - val = s->pci0_status.down; - PIIX4_DPRINTF("pci_down_read %" PRIu32 "\n", val); - break; - case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: - /* No feature defined yet */ - PIIX4_DPRINTF("pci_features_read %" PRIu32 "\n", val); - break; - case PCI_RMV_BASE - PCI_HOTPLUG_ADDR: - val = s->pci0_hotplug_enable; - break; - default: - break; - } - - return val; -} - -static void pci_write(void *opaque, hwaddr addr, uint64_t data, - unsigned int size) -{ - switch (addr) { - case PCI_EJ_BASE - PCI_HOTPLUG_ADDR: - acpi_piix_eject_slot(opaque, (uint32_t)data); - PIIX4_DPRINTF("pciej write %" HWADDR_PRIx " <== %" PRIu64 "\n", - addr, data); - break; - default: - break; - } -} - -static const MemoryRegionOps piix4_pci_ops = { - .read = pci_read, - .write = pci_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 4, - .max_access_size = 4, - }, -}; - static void piix4_cpu_added_req(Notifier *n, void *opaque) { PIIX4PMState *s = container_of(n, PIIX4PMState, cpu_added_notifier); @@ -684,9 +506,6 @@ static void piix4_cpu_added_req(Notifier *n, void *opaque) acpi_update_sci(&s->ar, s->irq); } -static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, - PCIHotplugState state); - static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, PCIBus *bus, PIIX4PMState *s) { @@ -694,15 +513,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, "acpi-gpe0", GPE_LEN); memory_region_add_subregion(parent, GPE_BASE, &s->io_gpe); - if (s->use_acpi_pci_hotplug) { - acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent); - } else { - memory_region_init_io(&s->io_pci, OBJECT(s), &piix4_pci_ops, s, - "acpi-pci-hotplug", PCI_HOTPLUG_SIZE); - memory_region_add_subregion(parent, PCI_HOTPLUG_ADDR, - &s->io_pci); - pci_bus_hotplug(bus, piix4_device_hotplug, DEVICE(s)); - } + acpi_pcihp_init(&s->acpi_pci_hotplug, bus, parent, + s->use_acpi_pci_hotplug); AcpiCpuHotplug_init(parent, OBJECT(s), &s->gpe_cpu, PIIX4_CPU_HOTPLUG_IO_BASE); @@ -710,39 +522,55 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, qemu_register_cpu_added_notifier(&s->cpu_added_notifier); } -static void enable_device(PIIX4PMState *s, int slot) -{ - s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; - s->pci0_slot_device_present |= (1U << slot); -} +static Property piix4_pm_properties[] = { + DEFINE_PROP_UINT32("smb_io_base", PIIX4PMState, smb_io_base, 0), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S3_DISABLED, PIIX4PMState, disable_s3, 0), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_DISABLED, PIIX4PMState, disable_s4, 0), + DEFINE_PROP_UINT8(ACPI_PM_PROP_S4_VAL, PIIX4PMState, s4_val, 2), + DEFINE_PROP_BOOL("acpi-pci-hotplug-with-bridge-support", PIIX4PMState, + use_acpi_pci_hotplug, true), + DEFINE_PROP_END_OF_LIST(), +}; -static void disable_device(PIIX4PMState *s, int slot) +static void piix4_pm_class_init(ObjectClass *klass, void *data) { - s->ar.gpe.sts[0] |= PIIX4_PCI_HOTPLUG_STATUS; - s->pci0_status.down |= (1U << slot); -} + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); -static int piix4_device_hotplug(DeviceState *qdev, PCIDevice *dev, - PCIHotplugState state) -{ - int slot = PCI_SLOT(dev->devfn); - PIIX4PMState *s = PIIX4_PM(qdev); - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (state == PCI_COLDPLUG_ENABLED) { - s->pci0_slot_device_present |= (1U << slot); - return 0; - } + k->init = piix4_pm_initfn; + k->config_write = pm_write_config; + k->vendor_id = PCI_VENDOR_ID_INTEL; + k->device_id = PCI_DEVICE_ID_INTEL_82371AB_3; + k->revision = 0x03; + k->class_id = PCI_CLASS_BRIDGE_OTHER; + dc->desc = "PM"; + dc->vmsd = &vmstate_acpi; + dc->props = piix4_pm_properties; + /* + * Reason: part of PIIX4 southbridge, needs to be wired up, + * e.g. by mips_malta_init() + */ + dc->cannot_instantiate_with_device_add_yet = true; + dc->hotpluggable = false; + hc->plug = piix4_pci_device_plug_cb; + hc->unplug = piix4_pci_device_unplug_cb; +} - if (state == PCI_HOTPLUG_ENABLED) { - enable_device(s, slot); - } else { - disable_device(s, slot); +static const TypeInfo piix4_pm_info = { + .name = TYPE_PIIX4_PM, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PIIX4PMState), + .class_init = piix4_pm_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } } +}; - acpi_update_sci(&s->ar, s->irq); - - return 0; +static void piix4_pm_register_types(void) +{ + type_register_static(&piix4_pm_info); } + +type_init(piix4_pm_register_types) diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index 950146c6ff..9e324befd6 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -2,6 +2,7 @@ common-obj-y += qdev.o qdev-properties.o # irq.o needed for qdev GPIO handling: common-obj-y += irq.o +common-obj-y += hotplug.o common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o common-obj-$(CONFIG_XILINX_AXI) += stream.o diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c new file mode 100644 index 0000000000..5573d9d2d9 --- /dev/null +++ b/hw/core/hotplug.c @@ -0,0 +1,48 @@ +/* + * Hotplug handler interface. + * + * Copyright (c) 2014 Red Hat Inc. + * + * Authors: + * Igor Mammedov <imammedo@redhat.com>, + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "hw/hotplug.h" +#include "qemu/module.h" + +void hotplug_handler_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->plug) { + hdc->plug(plug_handler, plugged_dev, errp); + } +} + +void hotplug_handler_unplug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp) +{ + HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); + + if (hdc->unplug) { + hdc->unplug(plug_handler, plugged_dev, errp); + } +} + +static const TypeInfo hotplug_handler_info = { + .name = TYPE_HOTPLUG_HANDLER, + .parent = TYPE_INTERFACE, + .class_size = sizeof(HotplugHandlerClass), +}; + +static void hotplug_handler_register_types(void) +{ + type_register_static(&hotplug_handler_info); +} + +type_init(hotplug_handler_register_types) diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 82a9123038..64b66e07ef 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -32,6 +32,7 @@ #include "qapi/visitor.h" #include "qapi/qmp/qjson.h" #include "monitor/monitor.h" +#include "hw/hotplug.h" int qdev_hotplug = 0; static bool qdev_hot_added = false; @@ -212,13 +213,22 @@ void qdev_unplug(DeviceState *dev, Error **errp) error_set(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); return; } - assert(dc->unplug != NULL); + + if (!dc->hotpluggable) { + error_set(errp, QERR_DEVICE_NO_HOTPLUG, + object_get_typename(OBJECT(dev))); + return; + } qdev_hot_removed = true; - if (dc->unplug(dev) < 0) { - error_set(errp, QERR_UNDEFINED_ERROR); - return; + if (dev->parent_bus && dev->parent_bus->hotplug_handler) { + hotplug_handler_unplug(dev->parent_bus->hotplug_handler, dev, errp); + } else { + assert(dc->unplug != NULL); + if (dc->unplug(dev) < 0) { /* legacy handler */ + error_set(errp, QERR_UNDEFINED_ERROR); + } } } @@ -693,6 +703,11 @@ static void device_set_realized(Object *obj, bool value, Error **err) DeviceClass *dc = DEVICE_GET_CLASS(dev); Error *local_err = NULL; + if (dev->hotplugged && !dc->hotpluggable) { + error_set(err, QERR_DEVICE_NO_HOTPLUG, object_get_typename(obj)); + return; + } + if (value && !dev->realized) { if (!obj->parent && local_err == NULL) { static int unattached_count; @@ -708,6 +723,12 @@ static void device_set_realized(Object *obj, bool value, Error **err) dc->realize(dev, &local_err); } + if (dev->parent_bus && dev->parent_bus->hotplug_handler && + local_err == NULL) { + hotplug_handler_plug(dev->parent_bus->hotplug_handler, + dev, &local_err); + } + if (qdev_get_vmsd(dev) && local_err == NULL) { vmstate_register_with_alias_id(dev, -1, qdev_get_vmsd(dev), dev, dev->instance_id_alias, @@ -733,6 +754,14 @@ static void device_set_realized(Object *obj, bool value, Error **err) dev->realized = value; } +static bool device_get_hotpluggable(Object *obj, Error **err) +{ + DeviceClass *dc = DEVICE_GET_CLASS(obj); + DeviceState *dev = DEVICE(obj); + + return dc->hotpluggable && dev->parent_bus->allow_hotplug; +} + static void device_initfn(Object *obj) { DeviceState *dev = DEVICE(obj); @@ -749,6 +778,8 @@ static void device_initfn(Object *obj) object_property_add_bool(obj, "realized", device_get_realized, device_set_realized, NULL); + object_property_add_bool(obj, "hotpluggable", + device_get_hotpluggable, NULL, NULL); class = object_get_class(OBJECT(dev)); do { @@ -785,6 +816,14 @@ static void device_class_base_init(ObjectClass *class, void *data) * so do not propagate them to the subclasses. */ klass->props = NULL; + + /* by default all devices were considered as hotpluggable, + * so with intent to check it in generic qdev_unplug() / + * device_set_realized() functions make every device + * hotpluggable. Devices that shouldn't be hotpluggable, + * should override it in their class_init() + */ + klass->hotpluggable = true; } static void device_unparent(Object *obj) @@ -870,6 +909,9 @@ static void qbus_initfn(Object *obj) BusState *bus = BUS(obj); QTAILQ_INIT(&bus->children); + object_property_add_link(obj, QDEV_HOTPLUG_HANDLER_PROPERTY, + TYPE_HOTPLUG_HANDLER, + (Object **)&bus->hotplug_handler, NULL); } static char *default_bus_get_fw_dev_path(DeviceState *dev) diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index e4c345fa82..3a8fc0bf8e 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2996,7 +2996,6 @@ static void cirrus_vga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = pci_cirrus_vga_initfn; k->romfile = VGABIOS_CIRRUS_FILENAME; k->vendor_id = PCI_VENDOR_ID_CIRRUS; @@ -3006,6 +3005,7 @@ static void cirrus_vga_class_init(ObjectClass *klass, void *data) dc->desc = "Cirrus CLGD 54xx VGA"; dc->vmsd = &vmstate_pci_cirrus_vga; dc->props = pci_vga_cirrus_properties; + dc->hotpluggable = false; } static const TypeInfo cirrus_vga_info = { diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 334c2719f8..1471cc093b 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2303,7 +2303,6 @@ static void qxl_primary_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = qxl_init_primary; k->romfile = "vgabios-qxl.bin"; k->vendor_id = REDHAT_PCI_VENDOR_ID; @@ -2314,6 +2313,7 @@ static void qxl_primary_class_init(ObjectClass *klass, void *data) dc->reset = qxl_reset_handler; dc->vmsd = &qxl_vmstate; dc->props = qxl_properties; + dc->hotpluggable = false; } static const TypeInfo qxl_primary_info = { diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index b3a45c81da..f74fc43aa6 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -190,7 +190,6 @@ static void vga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = pci_std_vga_initfn; k->romfile = "vgabios-stdvga.bin"; k->vendor_id = PCI_VENDOR_ID_QEMU; @@ -198,6 +197,7 @@ static void vga_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_DISPLAY_VGA; dc->vmsd = &vmstate_vga_pci; dc->props = vga_pci_properties; + dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index aba292ccde..334e71856e 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1296,7 +1296,6 @@ static void vmsvga_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = pci_vmsvga_initfn; k->romfile = "vgabios-vmware.bin"; k->vendor_id = PCI_VENDOR_ID_VMWARE; @@ -1307,6 +1306,7 @@ static void vmsvga_class_init(ObjectClass *klass, void *data) dc->reset = vmsvga_reset; dc->vmsd = &vmstate_vmware_vga; dc->props = vga_vmware_properties; + dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 50e83f3b46..b1a7ebb8e3 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -768,6 +768,7 @@ static void build_pci_bus_end(PCIBus *bus, void *bus_state) memset(slot_hotplug_enable, 0xff, sizeof slot_hotplug_enable); for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + DeviceClass *dc; PCIDeviceClass *pc; PCIDevice *pdev = bus->devices[i]; @@ -776,8 +777,9 @@ static void build_pci_bus_end(PCIBus *bus, void *bus_state) } pc = PCI_DEVICE_GET_CLASS(pdev); + dc = DEVICE_GET_CLASS(pdev); - if (pc->no_hotplug || pc->is_bridge) { + if (!dc->hotpluggable || pc->is_bridge) { int slot = PCI_SLOT(i); clear_bit(slot, slot_hotplug_enable); diff --git a/hw/i386/acpi-dsdt-hpet.dsl b/hw/i386/acpi-dsdt-hpet.dsl index dfde174317..44961b87a1 100644 --- a/hw/i386/acpi-dsdt-hpet.dsl +++ b/hw/i386/acpi-dsdt-hpet.dsl @@ -39,9 +39,6 @@ Scope(\_SB) { Return (0x0F) } Name(_CRS, ResourceTemplate() { -#if 0 /* This makes WinXP BSOD for not yet figured reasons. */ - IRQNoFlags() {2, 8} -#endif Memory32Fixed(ReadOnly, 0xFED00000, // Address Base 0x00000400, // Address Length diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 348b15f267..e715a3312d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -835,8 +835,8 @@ static void load_linux(FWCfgState *fw_cfg, initrd_size = get_image_size(initrd_filename); if (initrd_size < 0) { - fprintf(stderr, "qemu: error reading initrd %s\n", - initrd_filename); + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, strerror(errno)); exit(1); } diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index a327d71fb1..1acd2b2a20 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -793,6 +793,17 @@ static QEMUMachine xenfv_machine = { .max_cpus = HVM_MAX_VCPUS, .default_machine_opts = "accel=xen", .hot_add_cpu = pc_hot_add_cpu, + .compat_props = (GlobalProperty[]) { + /* xenfv has no fwcfg and so does not load acpi from QEMU. + * as such new acpi features don't work. + */ + { + .driver = "PIIX4_PM", + .property = "acpi-pci-hotplug-with-bridge-support", + .value = "off", + }, + { /* end of list */ } + }, }; #endif diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 9b5960b44e..0eda301ba9 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -241,13 +241,13 @@ static void piix3_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = pci_piix_ide_initfn; k->exit = pci_piix_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82371SB_1; k->class_id = PCI_CLASS_STORAGE_IDE; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->hotpluggable = false; } static const TypeInfo piix3_ide_info = { @@ -280,13 +280,13 @@ static void piix4_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = pci_piix_ide_initfn; k->exit = pci_piix_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82371AB; k->class_id = PCI_CLASS_STORAGE_IDE; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->hotpluggable = false; } static const TypeInfo piix4_ide_info = { diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index def6fe3a0f..492cd22fcf 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -107,7 +107,6 @@ static void piix4_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = piix4_initfn; k->vendor_id = PCI_VENDOR_ID_INTEL; k->device_id = PCI_DEVICE_ID_INTEL_82371AB_0; @@ -119,6 +118,7 @@ static void piix4_class_init(ObjectClass *klass, void *data) * e.g. by mips_malta_init() */ dc->cannot_instantiate_with_device_add_yet = true; + dc->hotpluggable = false; } static const TypeInfo piix4_info = { diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 440e187c46..e68145c52f 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -26,6 +26,7 @@ #include "hw/pci/slotid_cap.h" #include "exec/memory.h" #include "hw/pci/pci_bus.h" +#include "hw/hotplug.h" #define TYPE_PCI_BRIDGE_DEV "pci-bridge" #define PCI_BRIDGE_DEV(obj) \ @@ -136,6 +137,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + k->init = pci_bridge_dev_initfn; k->exit = pci_bridge_dev_exitfn; k->config_write = pci_bridge_dev_write_config; @@ -148,6 +151,8 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) dc->props = pci_bridge_dev_properties; dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + hc->plug = shpc_device_hotplug_cb; + hc->unplug = shpc_device_hot_unplug_cb; } static const TypeInfo pci_bridge_dev_info = { @@ -155,6 +160,10 @@ static const TypeInfo pci_bridge_dev_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(PCIBridgeDev), .class_init = pci_bridge_dev_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void pci_bridge_dev_register(void) diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index e89d5c1dfa..ffdc853a62 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -628,7 +628,7 @@ static void piix3_class_init(ObjectClass *klass, void *data) dc->desc = "ISA bridge"; dc->vmsd = &vmstate_piix3; - k->no_hotplug = 1; + dc->hotpluggable = false; k->init = piix3_initfn; k->config_write = piix3_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -656,7 +656,7 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data) dc->desc = "ISA bridge"; dc->vmsd = &vmstate_piix3; - k->no_hotplug = 1; + dc->hotpluggable = false; k->init = piix3_initfn; k->config_write = piix3_write_config_xen; k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -682,7 +682,6 @@ static void i440fx_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->no_hotplug = 1; k->init = i440fx_initfn; k->config_write = i440fx_write_config; k->vendor_id = PCI_VENDOR_ID_INTEL; @@ -696,6 +695,7 @@ static void i440fx_class_init(ObjectClass *klass, void *data) * host-facing part, which can't be device_add'ed, yet. */ dc->cannot_instantiate_with_device_add_yet = true; + dc->hotpluggable = false; } static const TypeInfo i440fx_info = { diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 042dc8f225..0c948e2c30 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -47,7 +47,7 @@ typedef struct PRePPCIState { PCIHostState parent_obj; MemoryRegion intack; - qemu_irq irq[4]; + qemu_irq irq[PCI_NUM_PINS]; PCIBus pci_bus; RavenPCIState pci_dev; } PREPPCIState; @@ -121,11 +121,11 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) isa_mem_base = 0xc0000000; - for (i = 0; i < 4; i++) { + for (i = 0; i < PCI_NUM_PINS; i++) { sysbus_init_irq(dev, &s->irq[i]); } - pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, 4); + pci_bus_irqs(&s->pci_bus, prep_set_irq, prep_map_irq, s->irq, PCI_NUM_PINS); memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_be_ops, s, "pci-conf-idx", 1); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1221f32847..4e0701df38 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -35,6 +35,7 @@ #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "exec/address-spaces.h" +#include "hw/hotplug.h" //#define DEBUG_PCI #ifdef DEBUG_PCI @@ -346,13 +347,6 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); } -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *qdev) -{ - bus->qbus.allow_hotplug = 1; - bus->hotplug = hotplug; - bus->hotplug_qdev = qdev; -} - PCIBus *pci_register_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, @@ -1761,11 +1755,7 @@ static int pci_qdev_init(DeviceState *qdev) pci_dev->devfn); if (pci_dev == NULL) return -1; - if (qdev->hotplugged && pc->no_hotplug) { - qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(pci_dev))); - do_pci_unregister_device(pci_dev); - return -1; - } + if (pc->init) { rc = pc->init(pci_dev); if (rc != 0) { @@ -1782,34 +1772,9 @@ static int pci_qdev_init(DeviceState *qdev) } pci_add_option_rom(pci_dev, is_default_rom); - if (bus->hotplug) { - /* Let buses differentiate between hotplug and when device is - * enabled during qemu machine creation. */ - rc = bus->hotplug(bus->hotplug_qdev, pci_dev, - qdev->hotplugged ? PCI_HOTPLUG_ENABLED: - PCI_COLDPLUG_ENABLED); - if (rc != 0) { - int r = pci_unregister_device(&pci_dev->qdev); - assert(!r); - return rc; - } - } return 0; } -static int pci_unplug_device(DeviceState *qdev) -{ - PCIDevice *dev = PCI_DEVICE(qdev); - PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(dev); - - if (pc->no_hotplug) { - qerror_report(QERR_DEVICE_NO_HOTPLUG, object_get_typename(OBJECT(dev))); - return -1; - } - return dev->bus->hotplug(dev->bus->hotplug_qdev, dev, - PCI_HOTPLUG_DISABLED); -} - PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction, const char *name) { @@ -2280,7 +2245,6 @@ static void pci_device_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->init = pci_qdev_init; - k->unplug = pci_unplug_device; k->exit = pci_unregister_device; k->bus_type = TYPE_PCI_BUS; k->props = pci_props; diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index ca60cf2177..8ecd11eca2 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -26,6 +26,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pcie_regs.h" #include "qemu/range.h" +#include "qapi/qmp/qerror.h" //#define DEBUG_PCIE #ifdef DEBUG_PCIE @@ -216,28 +217,20 @@ static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) hotplug_event_notify(dev); } -static int pcie_cap_slot_hotplug(DeviceState *qdev, - PCIDevice *pci_dev, PCIHotplugState state) +static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev, + DeviceState *dev, + uint8_t **exp_cap, Error **errp) { - PCIDevice *d = PCI_DEVICE(qdev); - uint8_t *exp_cap = d->config + d->exp.exp_cap; - uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); - - /* Don't send event when device is enabled during qemu machine creation: - * it is present on boot, no hotplug event is necessary. We do send an - * event when the device is disabled later. */ - if (state == PCI_COLDPLUG_ENABLED) { - pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - return 0; - } + PCIDevice *pci_dev = PCI_DEVICE(dev); + *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; + uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA); PCIE_DEV_PRINTF(pci_dev, "hotplug state: %d\n", state); if (sltsta & PCI_EXP_SLTSTA_EIS) { /* the slot is electromechanically locked. * This error is propagated up to qdev and then to HMP/QMP. */ - return -EBUSY; + error_setg_errno(errp, -EBUSY, "slot is electromechanically locked"); } /* TODO: multifunction hot-plug. @@ -245,18 +238,40 @@ static int pcie_cap_slot_hotplug(DeviceState *qdev, * hot plugged/unplugged. */ assert(PCI_FUNC(pci_dev->devfn) == 0); +} - if (state == PCI_HOTPLUG_ENABLED) { +void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + uint8_t *exp_cap; + + pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + + /* Don't send event when device is enabled during qemu machine creation: + * it is present on boot, no hotplug event is necessary. We do send an + * event when the device is disabled later. */ + if (!dev->hotplugged) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); - } else { - object_unparent(OBJECT(pci_dev)); - pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, - PCI_EXP_SLTSTA_PDS); - pcie_cap_slot_event(d, PCI_EXP_HP_EV_PDC); + return; } - return 0; + + pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC); +} + +void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + uint8_t *exp_cap; + + pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + + object_unparent(OBJECT(dev)); + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, + PCI_EXP_SLTSTA_PDS); + pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC); } /* pci express slot for pci express root/downstream port @@ -305,8 +320,8 @@ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) dev->exp.hpev_notified = false; - pci_bus_hotplug(pci_bridge_get_sec_bus(PCI_BRIDGE(dev)), - pcie_cap_slot_hotplug, &dev->qdev); + qbus_set_hotplug_handler(BUS(pci_bridge_get_sec_bus(PCI_BRIDGE(dev))), + DEVICE(dev), NULL); } void pcie_cap_slot_reset(PCIDevice *dev) diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 2adb0300f4..fa24877955 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -19,6 +19,7 @@ */ #include "hw/pci/pcie_port.h" +#include "hw/hotplug.h" void pcie_port_init_reg(PCIDevice *d) { @@ -149,8 +150,11 @@ static Property pcie_slot_props[] = { static void pcie_slot_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); dc->props = pcie_slot_props; + hc->plug = pcie_cap_slot_hotplug_cb; + hc->unplug = pcie_cap_slot_hot_unplug_cb; } static const TypeInfo pcie_slot_type_info = { @@ -159,6 +163,10 @@ static const TypeInfo pcie_slot_type_info = { .instance_size = sizeof(PCIESlot), .abstract = true, .class_init = pcie_slot_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } }; static void pcie_port_register_types(void) diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index 576244b9f6..180faa7adb 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -7,6 +7,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" #include "hw/pci/msi.h" +#include "qapi/qmp/qerror.h" /* TODO: model power only and disabled slot states. */ /* TODO: handle SERR and wakeups */ @@ -490,65 +491,93 @@ static const MemoryRegionOps shpc_mmio_ops = { .max_access_size = 4, }, }; - -static int shpc_device_hotplug(DeviceState *qdev, PCIDevice *affected_dev, - PCIHotplugState hotplug_state) +static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot, + SHPCDevice *shpc, Error **errp) { int pci_slot = PCI_SLOT(affected_dev->devfn); - uint8_t state; - uint8_t led; - PCIDevice *d = DO_UPCAST(PCIDevice, qdev, qdev); - SHPCDevice *shpc = d->shpc; - int slot = SHPC_PCI_TO_IDX(pci_slot); - if (pci_slot < SHPC_IDX_TO_PCI(0) || slot >= shpc->nslots) { - error_report("Unsupported PCI slot %d for standard hotplug " - "controller. Valid slots are between %d and %d.", - pci_slot, SHPC_IDX_TO_PCI(0), - SHPC_IDX_TO_PCI(shpc->nslots) - 1); - return -1; + *slot = SHPC_PCI_TO_IDX(pci_slot); + + if (pci_slot < SHPC_IDX_TO_PCI(0) || *slot >= shpc->nslots) { + error_setg(errp, "Unsupported PCI slot %d for standard hotplug " + "controller. Valid slots are between %d and %d.", + pci_slot, SHPC_IDX_TO_PCI(0), + SHPC_IDX_TO_PCI(shpc->nslots) - 1); + return; + } +} + +void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + Error *local_err = NULL; + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + SHPCDevice *shpc = pci_hotplug_dev->shpc; + int slot; + + shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; } + /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an * event when the device is disabled later. */ - if (hotplug_state == PCI_COLDPLUG_ENABLED) { + if (!dev->hotplugged) { shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, SHPC_SLOT_STATUS_PRSNT_MASK); - return 0; + return; } - if (hotplug_state == PCI_HOTPLUG_DISABLED) { - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; - state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); - led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); - if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { - shpc_free_devices_in_slot(shpc, slot); - shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } + + /* This could be a cancellation of the previous removal. + * We check MRL state to figure out. */ + if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON | + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; } else { - /* This could be a cancellation of the previous removal. - * We check MRL state to figure out. */ - if (shpc_get_status(shpc, slot, SHPC_SLOT_STATUS_MRL_OPEN)) { - shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_MRL_OPEN); - shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_7_5W, - SHPC_SLOT_STATUS_PRSNT_MASK); - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON | - SHPC_SLOT_EVENT_MRL | - SHPC_SLOT_EVENT_PRESENCE; - } else { - /* Press attention button to cancel removal */ - shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= - SHPC_SLOT_EVENT_BUTTON; - } + /* Press attention button to cancel removal */ + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_BUTTON; } shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); - shpc_interrupt_update(d); - return 0; + shpc_interrupt_update(pci_hotplug_dev); +} + +void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + Error *local_err = NULL; + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + SHPCDevice *shpc = pci_hotplug_dev->shpc; + uint8_t state; + uint8_t led; + int slot; + + shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, errp); + if (local_err) { + return; + } + + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= SHPC_SLOT_EVENT_BUTTON; + state = shpc_get_status(shpc, slot, SHPC_SLOT_STATE_MASK); + led = shpc_get_status(shpc, slot, SHPC_SLOT_PWR_LED_MASK); + if (state == SHPC_STATE_DISABLED && led == SHPC_LED_OFF) { + shpc_free_devices_in_slot(shpc, slot); + shpc_set_status(shpc, slot, 1, SHPC_SLOT_STATUS_MRL_OPEN); + shpc_set_status(shpc, slot, SHPC_SLOT_STATUS_PRSNT_EMPTY, + SHPC_SLOT_STATUS_PRSNT_MASK); + shpc->config[SHPC_SLOT_EVENT_LATCH(slot)] |= + SHPC_SLOT_EVENT_MRL | + SHPC_SLOT_EVENT_PRESENCE; + } + shpc_set_status(shpc, slot, 0, SHPC_SLOT_STATUS_66); + shpc_interrupt_update(pci_hotplug_dev); } /* Initialize the SHPC structure in bridge's BAR. */ @@ -616,7 +645,8 @@ int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, unsigned offset) d, "shpc-mmio", SHPC_SIZEOF(d)); shpc_cap_update_dword(d); memory_region_add_subregion(bar, offset, &shpc->mmio); - pci_bus_hotplug(sec_bus, shpc_device_hotplug, &d->qdev); + + qbus_set_hotplug_handler(BUS(sec_bus), DEVICE(d), NULL); d->cap_present |= QEMU_PCI_CAP_SHPC; return 0; diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index 7a1cd5d89e..2dd5284532 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -8,7 +8,7 @@ obj-$(CONFIG_PSERIES) += spapr_pci.o obj-y += ppc405_boards.o ppc4xx_devs.o ppc405_uc.o ppc440_bamboo.o obj-y += ppc4xx_pci.o # PReP -obj-y += prep.o +obj-$(CONFIG_PREP) += prep.o # OldWorld PowerMac obj-y += mac_oldworld.o # NewWorld PowerMac diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index aad0f69117..9f8538cd20 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -656,7 +656,7 @@ static void ppc_prep_init(QEMUMachineInitArgs *args) sysctrl->reset_irq = cpu->env.irq_inputs[PPC6xx_INPUT_HRESET]; portio_list_init(port_list, NULL, prep_portio_list, sysctrl, "prep"); - portio_list_add(port_list, get_system_io(), 0x0); + portio_list_add(port_list, isa_address_space_io(isa), 0x0); /* PowerPC control and status register group */ #if 0 diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 0c985942f9..484a9bd059 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -123,7 +123,7 @@ static void ehci_class_init(ObjectClass *klass, void *data) k->init = usb_ehci_pci_initfn; k->class_id = PCI_CLASS_SERIAL_USB; k->config_write = usb_ehci_pci_write_config; - k->no_hotplug = 1; + dc->hotpluggable = false; dc->vmsd = &vmstate_ehci_pci; dc->props = ehci_pci_properties; } diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index e38cdebfec..3d35058b14 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -1993,10 +1993,10 @@ static void ohci_pci_class_init(ObjectClass *klass, void *data) k->vendor_id = PCI_VENDOR_ID_APPLE; k->device_id = PCI_DEVICE_ID_APPLE_IPID_USB; k->class_id = PCI_CLASS_SERIAL_USB; - k->no_hotplug = 1; set_bit(DEVICE_CATEGORY_USB, dc->categories); dc->desc = "Apple USB Controller"; dc->props = ohci_pci_properties; + dc->hotpluggable = false; } static const TypeInfo ohci_pci_info = { diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 238d1d2b5f..ad814b58d4 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1318,7 +1318,7 @@ static void uhci_class_init(ObjectClass *klass, void *data) k->device_id = info->device_id; k->revision = info->revision; k->class_id = PCI_CLASS_SERIAL_USB; - k->no_hotplug = 1; + dc->hotpluggable = false; dc->vmsd = &vmstate_uhci; dc->props = uhci_properties; set_bit(DEVICE_CATEGORY_USB, dc->categories); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index bafe08590b..0fa814ee09 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3798,6 +3798,7 @@ static void xhci_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_xhci; dc->props = xhci_properties; dc->reset = xhci_reset; + dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_USB, dc->categories); k->init = usb_xhci_initfn; k->vendor_id = PCI_VENDOR_ID_NEC; @@ -3805,7 +3806,6 @@ static void xhci_class_init(ObjectClass *klass, void *data) k->class_id = PCI_CLASS_SERIAL_USB; k->revision = 0x03; k->is_express = 1; - k->no_hotplug = 1; } static const TypeInfo xhci_info = { diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index 3e53297a99..a9fae9d5c5 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -24,6 +24,7 @@ #include "qemu/notify.h" #include "qemu/option.h" #include "exec/memory.h" +#include "hw/irq.h" /* from linux include/acpi/actype.h */ /* Default ACPI register widths */ diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 6230e60954..9323838319 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -29,31 +29,34 @@ #include <inttypes.h> #include <qemu/typedefs.h> -#include "hw/pci/pci.h" /* for PCIHotplugState */ +#include "hw/acpi/acpi.h" +#include "migration/vmstate.h" typedef struct AcpiPciHpPciStatus { - uint32_t up; /* deprecated, maintained for migration compatibility */ + uint32_t up; uint32_t down; uint32_t hotplug_enable; - uint32_t device_present; } AcpiPciHpPciStatus; #define ACPI_PCIHP_PROP_BSEL "acpi-pcihp-bsel" #define ACPI_PCIHP_MAX_HOTPLUG_BUS 256 +#define ACPI_PCIHP_BSEL_DEFAULT 0x0 typedef struct AcpiPciHpState { AcpiPciHpPciStatus acpi_pcihp_pci_status[ACPI_PCIHP_MAX_HOTPLUG_BUS]; uint32_t hotplug_select; PCIBus *root; MemoryRegion io; + bool legacy_piix; } AcpiPciHpState; void acpi_pcihp_init(AcpiPciHpState *, PCIBus *root, - MemoryRegion *address_space_io); + MemoryRegion *address_space_io, bool bridges_enabled); -/* Invoke on device hotplug */ -int acpi_pcihp_device_hotplug(AcpiPciHpState *, PCIDevice *, - PCIHotplugState state); +void acpi_pcihp_device_plug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, + DeviceState *dev, Error **errp); +void acpi_pcihp_device_unplug_cb(ACPIREGS *ar, qemu_irq irq, AcpiPciHpState *s, + DeviceState *dev, Error **errp); /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h new file mode 100644 index 0000000000..a6533cb0b1 --- /dev/null +++ b/include/hw/hotplug.h @@ -0,0 +1,78 @@ +/* + * Hotplug handler interface. + * + * Copyright (c) 2014 Red Hat Inc. + * + * Authors: + * Igor Mammedov <imammedo@redhat.com>, + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef HOTPLUG_H +#define HOTPLUG_H + +#include "qom/object.h" +#include "qemu/typedefs.h" + +#define TYPE_HOTPLUG_HANDLER "hotplug-handler" + +#define HOTPLUG_HANDLER_CLASS(klass) \ + OBJECT_CLASS_CHECK(HotplugHandlerClass, (klass), TYPE_HOTPLUG_HANDLER) +#define HOTPLUG_HANDLER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(HotplugHandlerClass, (obj), TYPE_HOTPLUG_HANDLER) +#define HOTPLUG_HANDLER(obj) \ + INTERFACE_CHECK(HotplugHandler, (obj), TYPE_HOTPLUG_HANDLER) + + +typedef struct HotplugHandler { + /* <private> */ + Object Parent; +} HotplugHandler; + +/** + * hotplug_fn: + * @plug_handler: a device performing plug/uplug action + * @plugged_dev: a device that has been (un)plugged + * @errp: returns an error if this function fails + */ +typedef void (*hotplug_fn)(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp); + +/** + * HotplugDeviceClass: + * + * Interface to be implemented by a device performing + * hardware (un)plug functions. + * + * @parent: Opaque parent interface. + * @plug: plug callback. + * @unplug: unplug callback. + */ +typedef struct HotplugHandlerClass { + /* <private> */ + InterfaceClass parent; + + /* <public> */ + hotplug_fn plug; + hotplug_fn unplug; +} HotplugHandlerClass; + +/** + * hotplug_handler_plug: + * + * Call #HotplugHandlerClass.plug callback of @plug_handler. + */ +void hotplug_handler_plug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp); + +/** + * hotplug_handler_unplug: + * + * Call #HotplugHandlerClass.unplug callback of @plug_handler. + */ +void hotplug_handler_unplug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, + Error **errp); +#endif diff --git a/include/hw/loader.h b/include/hw/loader.h index 7a23d6bdc1..91b01224a3 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -4,6 +4,13 @@ #include "hw/nvram/fw_cfg.h" /* loader.c */ +/** + * get_image_size: retrieve size of an image file + * @filename: Path to the image file + * + * Returns the size of the image file on success, -1 otherwise. + * On error, errno is also set as appropriate. + */ int get_image_size(const char *filename); int load_image(const char *filename, uint8_t *addr); /* deprecated */ int load_image_targphys(const char *filename, hwaddr, diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 52523467b6..693dd6b658 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -201,9 +201,6 @@ typedef struct PCIDeviceClass { /* pcie stuff */ int is_express; /* is this device pci express? */ - /* device isn't hot-pluggable */ - int no_hotplug; - /* rom bar */ const char *romfile; } PCIDeviceClass; @@ -330,15 +327,6 @@ typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); -typedef enum { - PCI_HOTPLUG_DISABLED, - PCI_HOTPLUG_ENABLED, - PCI_COLDPLUG_ENABLED, -} PCIHotplugState; - -typedef int (*pci_hotplug_fn)(DeviceState *qdev, PCIDevice *pci_dev, - PCIHotplugState state); - #define TYPE_PCI_BUS "PCI" #define PCI_BUS(obj) OBJECT_CHECK(PCIBus, (obj), TYPE_PCI_BUS) #define TYPE_PCIE_BUS "PCIE" @@ -357,7 +345,6 @@ PCIBus *pci_bus_new(DeviceState *parent, const char *name, void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); -void pci_bus_hotplug(PCIBus *bus, pci_hotplug_fn hotplug, DeviceState *dev); /* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin); PCIBus *pci_register_bus(DeviceState *parent, const char *name, diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 9df17885ec..fabaeee86b 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -16,8 +16,6 @@ struct PCIBus { pci_set_irq_fn set_irq; pci_map_irq_fn map_irq; pci_route_irq_fn route_intx_to_irq; - pci_hotplug_fn hotplug; - DeviceState *hotplug_qdev; void *irq_opaque; PCIDevice *devices[PCI_SLOT_MAX * PCI_FUNC_MAX]; PCIDevice *parent_dev; diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 1966169553..b0bf7e3ce1 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -25,6 +25,7 @@ #include "hw/pci/pci_regs.h" #include "hw/pci/pcie_regs.h" #include "hw/pci/pcie_aer.h" +#include "hw/hotplug.h" typedef enum { /* for attention and power indicator */ @@ -122,4 +123,8 @@ extern const VMStateDescription vmstate_pcie_device; .offset = vmstate_offset_value(_state, _field, PCIDevice), \ } +void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void pcie_cap_slot_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index 467911a558..eef1a1ad6e 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -4,6 +4,8 @@ #include "qemu-common.h" #include "exec/memory.h" #include "migration/vmstate.h" +#include "qapi/error.h" +#include "hw/hotplug.h" struct SHPCDevice { /* Capability offset in device's config space */ @@ -41,6 +43,12 @@ int shpc_init(PCIDevice *dev, PCIBus *sec_bus, MemoryRegion *bar, unsigned off); void shpc_cleanup(PCIDevice *dev, MemoryRegion *bar); void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); + +void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void shpc_device_hot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); + extern VMStateInfo shpc_vmstate_info; #define SHPC_VMSTATE(_field, _type) \ VMSTATE_BUFFER_UNSAFE_INFO(_field, _type, 0, shpc_vmstate_info, 0) diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index 2c4f140b9c..08d329da71 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -8,6 +8,7 @@ #include "qom/object.h" #include "hw/irq.h" #include "qapi/error.h" +#include "hw/hotplug.h" enum { DEV_NVECTORS_UNSPECIFIED = -1, @@ -49,6 +50,8 @@ struct VMStateDescription; * is changed to %true. Deprecated, new types inheriting directly from * TYPE_DEVICE should use @realize instead, new leaf types should consult * their respective parent type. + * @hotpluggable: indicates if #DeviceClass is hotpluggable, available + * as readonly "hotpluggable" property of #DeviceState instance * * # Realization # * Devices are constructed in two stages, @@ -109,6 +112,7 @@ typedef struct DeviceClass { * TODO remove once we're there */ bool cannot_instantiate_with_device_add_yet; + bool hotpluggable; /* callbacks */ void (*reset)(DeviceState *dev); @@ -180,14 +184,18 @@ typedef struct BusChild { QTAILQ_ENTRY(BusChild) sibling; } BusChild; +#define QDEV_HOTPLUG_HANDLER_PROPERTY "hotplug-handler" + /** * BusState: + * @hotplug_device: link to a hotplug device associated with bus. */ struct BusState { Object obj; DeviceState *parent; const char *name; int allow_hotplug; + HotplugHandler *hotplug_handler; int max_index; QTAILQ_HEAD(ChildrenHead, BusChild) children; QLIST_ENTRY(BusState) sibling; @@ -321,4 +329,11 @@ extern int qdev_hotplug; char *qdev_get_dev_path(DeviceState *dev); +static inline void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, + Error **errp) +{ + object_property_set_link(OBJECT(bus), OBJECT(handler), + QDEV_HOTPLUG_HANDLER_PROPERTY, errp); + bus->allow_hotplug = 1; +} #endif diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 112a661ac4..6aca8e4c1f 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -23,6 +23,8 @@ static inline bool qtest_enabled(void) return qtest_allowed; } +bool qtest_driver(void); + int qtest_init_accel(void); void qtest_init(const char *qtest_chrdev, const char *qtest_log); diff --git a/libcacard/Makefile b/libcacard/Makefile index 4d15da49b8..6b064485fa 100644 --- a/libcacard/Makefile +++ b/libcacard/Makefile @@ -25,7 +25,7 @@ vscclient$(EXESUF): libcacard/vscclient.o libcacard.la libcacard.la: LDFLAGS += -rpath $(libdir) -no-undefined \ -export-syms $(SRC_PATH)/libcacard/libcacard.syms -libcacard.la: LIBS += $(libcacard_libs) +libcacard.la: LIBS = $(libcacard_libs) libcacard.la: $(libcacard-lobj-y) $(call LINK,$^) diff --git a/qapi-schema.json b/qapi-schema.json index 05ced9d572..7cfb5e5d1e 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -4371,6 +4371,7 @@ # TODO gluster: Wait for structured options # TODO iscsi: Wait for structured options # TODO nbd: Should take InetSocketAddress for 'host'? +# TODO nfs: Wait for structured options # TODO rbd: Wait for structured options # TODO sheepdog: Wait for structured options # TODO ssh: Should take InetSocketAddress for 'host'? diff --git a/qtest.c b/qtest.c index dcf1301229..a738afc724 100644 --- a/qtest.c +++ b/qtest.c @@ -528,3 +528,8 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log) qtest_chr = chr; } + +bool qtest_driver(void) +{ + return qtest_chr; +} diff --git a/target-mips/cpu.h b/target-mips/cpu.h index 9caf4474b9..60c80617a5 100644 --- a/target-mips/cpu.h +++ b/target-mips/cpu.h @@ -73,6 +73,7 @@ struct CPUMIPSFPUContext { float_status fp_status; /* fpu implementation/revision register (fir) */ uint32_t fcr0; +#define FCR0_UFRP 28 #define FCR0_F64 22 #define FCR0_L 21 #define FCR0_W 20 @@ -368,6 +369,18 @@ struct CPUMIPSState { #define CP0C3_MT 2 #define CP0C3_SM 1 #define CP0C3_TL 0 + uint32_t CP0_Config4; + uint32_t CP0_Config4_rw_bitmask; +#define CP0C4_M 31 + uint32_t CP0_Config5; + uint32_t CP0_Config5_rw_bitmask; +#define CP0C5_M 31 +#define CP0C5_K 30 +#define CP0C5_CV 29 +#define CP0C5_EVA 28 +#define CP0C5_MSAEn 27 +#define CP0C5_UFR 2 +#define CP0C5_NFExists 0 int32_t CP0_Config6; int32_t CP0_Config7; /* XXX: Maybe make LLAddr per-TC? */ diff --git a/target-mips/helper.h b/target-mips/helper.h index 1a8b86dea5..8c7921a724 100644 --- a/target-mips/helper.h +++ b/target-mips/helper.h @@ -134,6 +134,8 @@ DEF_HELPER_2(mtc0_ebase, void, env, tl) DEF_HELPER_2(mttc0_ebase, void, env, tl) DEF_HELPER_2(mtc0_config0, void, env, tl) DEF_HELPER_2(mtc0_config2, void, env, tl) +DEF_HELPER_2(mtc0_config4, void, env, tl) +DEF_HELPER_2(mtc0_config5, void, env, tl) DEF_HELPER_2(mtc0_lladdr, void, env, tl) DEF_HELPER_3(mtc0_watchlo, void, env, tl, i32) DEF_HELPER_3(mtc0_watchhi, void, env, tl, i32) @@ -177,7 +179,7 @@ DEF_HELPER_2(yield, tl, env, tl) /* CP1 functions */ DEF_HELPER_2(cfc1, tl, env, i32) -DEF_HELPER_3(ctc1, void, env, tl, i32) +DEF_HELPER_4(ctc1, void, env, tl, i32, i32) DEF_HELPER_2(float_cvtd_s, i64, env, i32) DEF_HELPER_2(float_cvtd_w, i64, env, i32) diff --git a/target-mips/mips-defs.h b/target-mips/mips-defs.h index bf094a3bd5..9dfa5168da 100644 --- a/target-mips/mips-defs.h +++ b/target-mips/mips-defs.h @@ -29,6 +29,8 @@ #define ISA_MIPS32R2 0x00000040 #define ISA_MIPS64 0x00000080 #define ISA_MIPS64R2 0x00000100 +#define ISA_MIPS32R3 0x00000200 +#define ISA_MIPS32R5 0x00000400 /* MIPS ASEs. */ #define ASE_MIPS16 0x00001000 @@ -64,6 +66,12 @@ #define CPU_MIPS32R2 (CPU_MIPS32 | ISA_MIPS32R2) #define CPU_MIPS64R2 (CPU_MIPS64 | CPU_MIPS32R2 | ISA_MIPS64R2) +/* MIPS Technologies "Release 3" */ +#define CPU_MIPS32R3 (CPU_MIPS32R2 | ISA_MIPS32R3) + +/* MIPS Technologies "Release 5" */ +#define CPU_MIPS32R5 (CPU_MIPS32R3 | ISA_MIPS32R5) + /* Strictly follow the architecture standard: - Disallow "special" instruction handling for PMON/SPIM. Note that we still maintain Count/Compare to match the host clock. */ diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c index 8e3a6d7da6..2ef6633f47 100644 --- a/target-mips/op_helper.c +++ b/target-mips/op_helper.c @@ -1489,6 +1489,18 @@ void helper_mtc0_config2(CPUMIPSState *env, target_ulong arg1) env->CP0_Config2 = (env->CP0_Config2 & 0x8FFF0FFF); } +void helper_mtc0_config4(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Config4 = (env->CP0_Config4 & (~env->CP0_Config4_rw_bitmask)) | + (arg1 & env->CP0_Config4_rw_bitmask); +} + +void helper_mtc0_config5(CPUMIPSState *env, target_ulong arg1) +{ + env->CP0_Config5 = (env->CP0_Config5 & (~env->CP0_Config5_rw_bitmask)) | + (arg1 & env->CP0_Config5_rw_bitmask); +} + void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) { target_long mask = env->CP0_LLAddr_rw_bitmask; @@ -2187,12 +2199,23 @@ static inline void restore_flush_mode(CPUMIPSState *env) target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) { - target_ulong arg1; + target_ulong arg1 = 0; switch (reg) { case 0: arg1 = (int32_t)env->active_fpu.fcr0; break; + case 1: + /* UFR Support - Read Status FR */ + if (env->active_fpu.fcr0 & (1 << FCR0_UFRP)) { + if (env->CP0_Config5 & (1 << CP0C5_UFR)) { + arg1 = (int32_t) + ((env->CP0_Status & (1 << CP0St_FR)) >> CP0St_FR); + } else { + helper_raise_exception(env, EXCP_RI); + } + } + break; case 25: arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1); break; @@ -2210,9 +2233,33 @@ target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg) return arg1; } -void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t reg) +void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt) { - switch(reg) { + switch (fs) { + case 1: + /* UFR Alias - Reset Status FR */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFR)) { + env->CP0_Status &= ~(1 << CP0St_FR); + compute_hflags(env); + } else { + helper_raise_exception(env, EXCP_RI); + } + break; + case 4: + /* UNFR Alias - Set Status FR */ + if (!((env->active_fpu.fcr0 & (1 << FCR0_UFRP)) && (rt == 0))) { + return; + } + if (env->CP0_Config5 & (1 << CP0C5_UFR)) { + env->CP0_Status |= (1 << CP0St_FR); + compute_hflags(env); + } else { + helper_raise_exception(env, EXCP_RI); + } + break; case 25: if (arg1 & 0xffffff00) return; diff --git a/target-mips/translate.c b/target-mips/translate.c index ef0a2c36b0..083f6ab283 100644 --- a/target-mips/translate.c +++ b/target-mips/translate.c @@ -4405,7 +4405,14 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3)); rn = "Config3"; break; - /* 4,5 are reserved */ + case 4: + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4)); + rn = "Config4"; + break; + case 5: + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5)); + rn = "Config5"; + break; /* 6,7 are implementation dependent */ case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6)); @@ -4982,7 +4989,17 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* ignored, read only */ rn = "Config3"; break; - /* 4,5 are reserved */ + case 4: + gen_helper_mtc0_config4(cpu_env, arg); + rn = "Config4"; + ctx->bstate = BS_STOP; + break; + case 5: + gen_helper_mtc0_config5(cpu_env, arg); + rn = "Config5"; + /* Stop translation as we may have switched the execution mode */ + ctx->bstate = BS_STOP; + break; /* 6,7 are implementation dependent */ case 6: /* ignored */ @@ -6801,7 +6818,12 @@ static void gen_mttr(CPUMIPSState *env, DisasContext *ctx, int rd, int rt, break; case 3: /* XXX: For now we support only a single FPU context. */ - gen_helper_0e1i(ctc1, t0, rd); + { + TCGv_i32 fs_tmp = tcg_const_i32(rd); + + gen_helper_0e2i(ctc1, t0, fs_tmp, rt); + tcg_temp_free_i32(fs_tmp); + } break; /* COP2: Not implemented. */ case 4: @@ -7237,7 +7259,12 @@ static void gen_cp1 (DisasContext *ctx, uint32_t opc, int rt, int fs) break; case OPC_CTC1: gen_load_gpr(t0, rt); - gen_helper_0e1i(ctc1, t0, fs); + { + TCGv_i32 fs_tmp = tcg_const_i32(fs); + + gen_helper_0e2i(ctc1, t0, fs_tmp, rt); + tcg_temp_free_i32(fs_tmp); + } opn = "ctc1"; break; #if defined(TARGET_MIPS64) @@ -15916,6 +15943,10 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Config1 = env->cpu_model->CP0_Config1; env->CP0_Config2 = env->cpu_model->CP0_Config2; env->CP0_Config3 = env->cpu_model->CP0_Config3; + env->CP0_Config4 = env->cpu_model->CP0_Config4; + env->CP0_Config4_rw_bitmask = env->cpu_model->CP0_Config4_rw_bitmask; + env->CP0_Config5 = env->cpu_model->CP0_Config5; + env->CP0_Config5_rw_bitmask = env->cpu_model->CP0_Config5_rw_bitmask; env->CP0_Config6 = env->cpu_model->CP0_Config6; env->CP0_Config7 = env->cpu_model->CP0_Config7; env->CP0_LLAddr_rw_bitmask = env->cpu_model->CP0_LLAddr_rw_bitmask diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c index c45b1b21b2..29d39e2a39 100644 --- a/target-mips/translate_init.c +++ b/target-mips/translate_init.c @@ -45,6 +45,12 @@ (0 << CP0C3_VEIC) | (0 << CP0C3_VInt) | (0 << CP0C3_SP) | \ (0 << CP0C3_SM) | (0 << CP0C3_TL)) +#define MIPS_CONFIG4 \ +((0 << CP0C4_M)) + +#define MIPS_CONFIG5 \ +((0 << CP0C5_M)) + /* MMU types, the first four entries have the same layout as the CP0C0_MT field. */ enum mips_mmu_types { @@ -64,6 +70,10 @@ struct mips_def_t { int32_t CP0_Config1; int32_t CP0_Config2; int32_t CP0_Config3; + int32_t CP0_Config4; + int32_t CP0_Config4_rw_bitmask; + int32_t CP0_Config5; + int32_t CP0_Config5_rw_bitmask; int32_t CP0_Config6; int32_t CP0_Config7; target_ulong CP0_LLAddr_rw_bitmask; @@ -333,6 +343,39 @@ static const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_DSPR2, .mmu_type = MMU_TYPE_R4000, }, + { + /* A generic CPU providing MIPS32 Release 5 features. + FIXME: Eventually this should be replaced by a real CPU model. */ + .name = "mips32r5-generic", + .CP0_PRid = 0x00019700, + .CP0_Config0 = MIPS_CONFIG0 | (0x1 << CP0C0_AR) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | + (0 << CP0C1_IS) | (3 << CP0C1_IL) | (1 << CP0C1_IA) | + (0 << CP0C1_DS) | (3 << CP0C1_DL) | (1 << CP0C1_DA) | + (1 << CP0C1_CA), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1 << CP0C3_M), + .CP0_Config4 = MIPS_CONFIG4 | (1 << CP0C4_M), + .CP0_Config4_rw_bitmask = 0, + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_UFR), + .CP0_Config5_rw_bitmask = (0 << CP0C5_M) | (1 << CP0C5_K) | + (1 << CP0C5_CV) | (0 << CP0C5_EVA) | + (1 << CP0C5_MSAEn) | (1 << CP0C5_UFR) | + (0 << CP0C5_NFExists), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 4, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x3778FF1F, + .CP1_fcr0 = (1 << FCR0_UFRP) | (1 << FCR0_F64) | (1 << FCR0_L) | + (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) | + (0x93 << FCR0_PRID), + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_MIPS32R5 | ASE_MIPS16 | ASE_DSP | ASE_DSPR2, + .mmu_type = MMU_TYPE_R4000, + }, #if defined(TARGET_MIPS64) { .name = "R4000", diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c index 781b72f1ea..7af3fe277d 100644 --- a/target-ppc/kvm.c +++ b/target-ppc/kvm.c @@ -1228,7 +1228,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) #endif case KVM_EXIT_EPR: DPRINTF("handle epr\n"); - run->epr.epr = ldl_phys(env->mpic_iack); + run->epr.epr = ldl_phys(cs->as, env->mpic_iack); ret = 0; break; case KVM_EXIT_WATCHDOG: diff --git a/tests/Makefile b/tests/Makefile index fd36eee641..9a7d2f1008 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -163,7 +163,7 @@ tests/test-xbzrle$(EXESUF): tests/test-xbzrle.o xbzrle.o page_cache.o libqemuuti tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o tests/test-int128$(EXESUF): tests/test-int128.o tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \ - hw/core/qdev.o hw/core/qdev-properties.o \ + hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\ hw/core/irq.o \ $(qom-core-obj) \ $(test-qapi-obj-y) \ diff --git a/tests/qemu-iotests/013 b/tests/qemu-iotests/013 index 389f4b8156..ea3cab91d6 100755 --- a/tests/qemu-iotests/013 +++ b/tests/qemu-iotests/013 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # much of this could be generic for any format supporting compression. _supported_fmt qcow qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux TEST_OFFSETS="0 4294967296" diff --git a/tests/qemu-iotests/014 b/tests/qemu-iotests/014 index 0edeb4b6f5..b23c2db9b6 100755 --- a/tests/qemu-iotests/014 +++ b/tests/qemu-iotests/014 @@ -43,7 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # much of this could be generic for any format supporting snapshots _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux TEST_OFFSETS="0 4294967296" diff --git a/tests/qemu-iotests/016 b/tests/qemu-iotests/016 index b87a32bc27..7ea9e94b5d 100755 --- a/tests/qemu-iotests/016 +++ b/tests/qemu-iotests/016 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt raw -_supported_proto file sheepdog +_supported_proto file sheepdog nfs _supported_os Linux diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018 index 6f7f0545d0..d8a7d435ab 100755 --- a/tests/qemu-iotests/018 +++ b/tests/qemu-iotests/018 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index b43e70f3cb..e67445c754 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -45,7 +45,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index 73a0429481..2f258dc6e9 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -49,6 +49,11 @@ _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ "subformat=twoGbMaxExtentSparse" +# NFS does not support bdrv_reopen_prepare thus qemu-img commit fails. +if [ "$IMGPROTO" = "nfs" ]; then + _notrun "image protocol $IMGPROTO does not support bdrv_commit" +fi + TEST_OFFSETS="0 4294967296" _make_test_img 6G diff --git a/tests/qemu-iotests/023 b/tests/qemu-iotests/023 index 090ed23dec..9ad06b990e 100755 --- a/tests/qemu-iotests/023 +++ b/tests/qemu-iotests/023 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # much of this could be generic for any format supporting compression. _supported_fmt qcow qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux TEST_OFFSETS="0 4294967296" diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index be974f02a2..9bf99e198a 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -43,7 +43,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Currently only qcow2 and qed support rebasing _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=65536 diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 index 9426c93bd0..a5f45b454c 100755 --- a/tests/qemu-iotests/025 +++ b/tests/qemu-iotests/025 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.pattern _supported_fmt raw qcow2 qed -_supported_proto file sheepdog rbd +_supported_proto file sheepdog rbd nfs _supported_os Linux echo "=== Creating image" diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index c9c5f83936..df2884ba51 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Currently only qcow2 supports rebasing _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" "none" diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 93a9fa6e83..a99e4fa2bd 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -45,7 +45,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files except vmdk and qcow which do not support # smaller backing files. _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux # Choose a size that is not necessarily a cluster size multiple for image diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index c9070b0513..1d920ea87a 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qcow2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=65536 diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 index 7349789583..69c7858586 100755 --- a/tests/qemu-iotests/034 +++ b/tests/qemu-iotests/034 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index e049a645e7..03b6aa9de7 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -44,7 +44,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qcow2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux # Only qcow2v3 and later supports feature bits diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037 index e444349e6d..9171d8c8a6 100755 --- a/tests/qemu-iotests/037 +++ b/tests/qemu-iotests/037 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow qcow2 vmdk qed -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/038 b/tests/qemu-iotests/038 index 7bb7906e7f..cfaf00a783 100755 --- a/tests/qemu-iotests/038 +++ b/tests/qemu-iotests/038 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=2M diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 6abf47267f..9b355c0977 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 index d7f12319b3..b316b97c0c 100755 --- a/tests/qemu-iotests/043 +++ b/tests/qemu-iotests/043 @@ -41,7 +41,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # Any format supporting backing files _supported_fmt qcow2 qed -_supported_proto generic +_supported_proto file _supported_os Linux diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 index 3f17ceb1b9..2d44bbb187 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux CLUSTER_SIZE=64k diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index b23d91b6f0..46345fb155 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -172,6 +172,18 @@ run_qemu -drive file="$TEST_IMG",file.driver=file run_qemu -drive file="$TEST_IMG",file.driver=qcow2 echo +echo === Leaving out required options === +echo + +run_qemu -drive driver=file +run_qemu -drive driver=nbd +run_qemu -drive driver=raw +run_qemu -drive file.driver=file +run_qemu -drive file.driver=nbd +run_qemu -drive file.driver=raw +run_qemu -drive foo=bar + +echo echo === Parsing protocol from file name === echo diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index d0c5173626..30e2dbd6d7 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -225,6 +225,30 @@ Testing: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2 QEMU_PROG: -drive file=TEST_DIR/t.qcow2,file.driver=qcow2: could not open disk image TEST_DIR/t.qcow2: Block format 'qcow2' used by device '' doesn't support the option 'filename' +=== Leaving out required options === + +Testing: -drive driver=file +QEMU_PROG: -drive driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name + +Testing: -drive driver=nbd +QEMU_PROG: -drive driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument + +Testing: -drive driver=raw +QEMU_PROG: -drive driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level + +Testing: -drive file.driver=file +QEMU_PROG: -drive file.driver=file: could not open disk image ide0-hd0: The 'file' block driver requires a file name + +Testing: -drive file.driver=nbd +QEMU_PROG: -drive file.driver=nbd: could not open disk image ide0-hd0: Could not open image: Invalid argument + +Testing: -drive file.driver=raw +QEMU_PROG: -drive file.driver=raw: could not open disk image ide0-hd0: Can't use 'raw' as a block driver for the protocol level + +Testing: -drive foo=bar +QEMU_PROG: -drive foo=bar: could not open disk image ide0-hd0: Must specify either driver or file + + === Parsing protocol from file name === Testing: -hda foo:bar diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052 index 4d4e411339..6bdae92780 100755 --- a/tests/qemu-iotests/052 +++ b/tests/qemu-iotests/052 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt generic -_supported_proto generic +_supported_proto file _supported_os Linux _default_cache_mode "writethrough" _supported_cache_modes "writethrough" diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054 index 5a0d1b16c2..c8b7082b4e 100755 --- a/tests/qemu-iotests/054 +++ b/tests/qemu-iotests/054 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux echo diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 2d604d3a91..ca5aa16ff7 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests vmdk-specific low-level functionality _supported_fmt vmdk -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index bbb19090a1..af8ed9f39a 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qocw2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux rt_offset=65536 # 0x10000 (XXX: just an assumption) diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index e42f9bd5e8..d3a6b388b5 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -40,7 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 # This tests qocw2-specific low-level functionality _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux echo diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index 77503a2984..a47493a076 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -42,7 +42,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.pattern _supported_fmt qcow qcow2 vmdk qed raw -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" \ "subformat=twoGbMaxExtentFlat" \ diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 index 50347d91d2..e661598c4a 100755 --- a/tests/qemu-iotests/069 +++ b/tests/qemu-iotests/069 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt cow qed qcow qcow2 vmdk -_supported_proto generic +_supported_proto file _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index dbc07c6c4f..3924e51f51 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt qcow2 -_supported_proto generic +_supported_proto file _supported_os Linux function do_run_qemu() diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072 index a3876c2161..58faa8b5a7 100755 --- a/tests/qemu-iotests/072 +++ b/tests/qemu-iotests/072 @@ -39,7 +39,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.filter _supported_fmt vpc vmdk vhdx vdi qed qcow2 qcow cow -_supported_proto generic +_supported_proto file _supported_os Linux IMG_SIZE=64M diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077 index bbf7b5145a..4dd1bdde20 100755 --- a/tests/qemu-iotests/077 +++ b/tests/qemu-iotests/077 @@ -38,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw _supported_proto generic _supported_os Linux diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 new file mode 100755 index 0000000000..2142bbb377 --- /dev/null +++ b/tests/qemu-iotests/079 @@ -0,0 +1,63 @@ +#!/bin/bash +# +# Test qcow2 preallocation with different cluster_sizes +# +# Copyright (C) 2014 Fujitsu. +# +# 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 <http://www.gnu.org/licenses/>. +# + +# creator +owner=hutao@cn.fujitsu.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 qcow2 +_supported_proto file +_supported_os Linux + +function test_qemu_img() +{ + echo qemu-img "$@" | _filter_testdir + $QEMU_IMG "$@" 2>&1 | _filter_testdir + echo +} + +echo "=== Check option preallocation and cluster_size ===" +echo +cluster_sizes="16384 32768 65536 131072 262144 524288 1048576 2097152 4194304" + +for s in $cluster_sizes; do + test_qemu_img create -f $IMGFMT -o preallocation=metadata,cluster_size=$s "$TEST_IMG" 4G +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/079.out b/tests/qemu-iotests/079.out new file mode 100644 index 0000000000..ef4b8c9117 --- /dev/null +++ b/tests/qemu-iotests/079.out @@ -0,0 +1,32 @@ +QA output created by 079 +=== Check option preallocation and cluster_size === + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=16384 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=16384 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=32768 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=32768 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=65536 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=65536 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=131072 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=131072 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=262144 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=262144 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=524288 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=524288 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=1048576 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=1048576 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=2097152 TEST_DIR/t.qcow2 4G +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=2097152 preallocation='metadata' lazy_refcounts=off + +qemu-img create -f qcow2 -o preallocation=metadata,cluster_size=4194304 TEST_DIR/t.qcow2 4G +qemu-img: TEST_DIR/t.qcow2: Cluster size must be a power of two between 512 and 2048k +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=4294967296 encryption=off cluster_size=4194304 preallocation='metadata' lazy_refcounts=off + +*** done diff --git a/tests/qemu-iotests/common b/tests/qemu-iotests/common index 8b4e22c856..5795358924 100644 --- a/tests/qemu-iotests/common +++ b/tests/qemu-iotests/common @@ -144,10 +144,12 @@ check options -vpc test vpc -vhdx test vhdx -vmdk test vmdk + -file test file (default) -rbd test rbd -sheepdog test sheepdog -nbd test nbd -ssh test ssh + -nfs test nfs -xdiff graphical mode diff -nocache use O_DIRECT on backing file -misalign misalign memory allocations @@ -211,22 +213,36 @@ testlist options xpand=false ;; + -file) + IMGPROTO=file + xpand=false + ;; + -rbd) IMGPROTO=rbd xpand=false ;; + -sheepdog) IMGPROTO=sheepdog xpand=false ;; + -nbd) IMGPROTO=nbd xpand=false ;; + -ssh) IMGPROTO=ssh xpand=false ;; + + -nfs) + IMGPROTO=nfs + xpand=false + ;; + -nocache) CACHEMODE="none" CACHEMODE_IS_DEFAULT=false @@ -238,10 +254,10 @@ testlist options xpand=false ;; - -valgrind) - valgrind=true + -valgrind) + valgrind=true xpand=false - ;; + ;; -g) # -g group ... pick from group file group=true diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 0f68156400..71e9a7462d 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -61,6 +61,9 @@ elif [ "$IMGPROTO" = "nbd" ]; then elif [ "$IMGPROTO" = "ssh" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" +elif [ "$IMGPROTO" = "nfs" ]; then + TEST_DIR="nfs://127.0.0.1/$TEST_DIR" + TEST_IMG=$TEST_DIR/t.$IMGFMT else TEST_IMG=$IMGPROTO:$TEST_DIR/t.$IMGFMT fi diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group index 03c762fb4f..d8be74a17e 100644 --- a/tests/qemu-iotests/group +++ b/tests/qemu-iotests/group @@ -82,3 +82,4 @@ 073 rw auto 074 rw auto 077 rw auto +079 rw auto diff --git a/trace-events b/trace-events index 1b668d1ac2..ab11f9721d 100644 --- a/trace-events +++ b/trace-events @@ -495,7 +495,7 @@ qcow2_writev_done_part(void *co, int cur_nr_sectors) "co %p cur_nr_sectors %d" qcow2_writev_data(void *co, uint64_t offset) "co %p offset %" PRIx64 # block/qcow2-cluster.c -qcow2_alloc_clusters_offset(void *co, uint64_t offset, int n_start, int n_end) "co %p offet %" PRIx64 " n_start %d n_end %d" +qcow2_alloc_clusters_offset(void *co, uint64_t offset, int num) "co %p offet %" PRIx64 " num %d" qcow2_handle_copied(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offet %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64 qcow2_handle_alloc(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offet %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64 qcow2_do_alloc_clusters_offset(void *co, uint64_t guest_offset, uint64_t host_offset, int nb_clusters) "co %p guest_offet %" PRIx64 " host_offset %" PRIx64 " nb_clusters %d" diff --git a/vl.c b/vl.c index 383be1b617..a7b00cdd37 100644 --- a/vl.c +++ b/vl.c @@ -1750,7 +1750,7 @@ static int qemu_shutdown_requested(void) static void qemu_kill_report(void) { - if (!qtest_enabled() && shutdown_signal != -1) { + if (!qtest_driver() && shutdown_signal != -1) { fprintf(stderr, "qemu: terminating on signal %d", shutdown_signal); if (shutdown_pid == 0) { /* This happens for eg ^C at the terminal, so it's worth |