diff options
131 files changed, 2411 insertions, 653 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 772ac209e1..9cff9737d3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1317,6 +1317,17 @@ S: Maintained F: device_tree.c F: include/sysemu/device_tree.h +Dump +S: Supported +M: Marc-AndrĂ© Lureau <marcandre.lureau@redhat.com> +F: dump.c +F: hw/misc/vmcoreinfo.c +F: include/hw/misc/vmcoreinfo.h +F: include/sysemu/dump-arch.h +F: include/sysemu/dump.h +F: scripts/dump-guest-memory.py +F: stubs/dump.c + Error reporting M: Markus Armbruster <armbru@redhat.com> S: Supported diff --git a/backends/tpm.c b/backends/tpm.c index 536f262bb7..37c84b7c66 100644 --- a/backends/tpm.c +++ b/backends/tpm.c @@ -18,27 +18,30 @@ #include "qapi/qmp/qerror.h" #include "sysemu/tpm.h" #include "qemu/thread.h" -#include "sysemu/tpm_backend_int.h" -enum TpmType tpm_backend_get_type(TPMBackend *s) +static void tpm_backend_worker_thread(gpointer data, gpointer user_data) { - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + TPMBackend *s = TPM_BACKEND(user_data); + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - return k->ops->type; + assert(k->handle_request != NULL); + k->handle_request(s, (TPMBackendCmd)data); } -const char *tpm_backend_get_desc(TPMBackend *s) +static void tpm_backend_thread_end(TPMBackend *s) { - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - return k->ops->desc(); + if (s->thread_pool) { + g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_END, NULL); + g_thread_pool_free(s->thread_pool, FALSE, TRUE); + s->thread_pool = NULL; + } } -void tpm_backend_destroy(TPMBackend *s) +enum TpmType tpm_backend_get_type(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - k->ops->destroy(s); + return k->ops->type; } int tpm_backend_init(TPMBackend *s, TPMState *state, @@ -46,48 +49,62 @@ int tpm_backend_init(TPMBackend *s, TPMState *state, { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - return k->ops->init(s, state, datacb); + s->tpm_state = state; + s->recv_data_callback = datacb; + s->had_startup_error = false; + + return k->ops->init ? k->ops->init(s) : 0; } int tpm_backend_startup_tpm(TPMBackend *s) { + int res = 0; TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - return k->ops->startup_tpm(s); -} + /* terminate a running TPM */ + tpm_backend_thread_end(s); -bool tpm_backend_had_startup_error(TPMBackend *s) -{ - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + s->thread_pool = g_thread_pool_new(tpm_backend_worker_thread, s, 1, TRUE, + NULL); + g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL); + + res = k->ops->startup_tpm ? k->ops->startup_tpm(s) : 0; - return k->ops->had_startup_error(s); + s->had_startup_error = (res != 0); + + return res; } -size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb) +bool tpm_backend_had_startup_error(TPMBackend *s) { - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - return k->ops->realloc_buffer(sb); + return s->had_startup_error; } void tpm_backend_deliver_request(TPMBackend *s) { - TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - - k->ops->deliver_request(s); + g_thread_pool_push(s->thread_pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, + NULL); } void tpm_backend_reset(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - k->ops->reset(s); + if (k->ops->reset) { + k->ops->reset(s); + } + + tpm_backend_thread_end(s); + + s->had_startup_error = false; } void tpm_backend_cancel_cmd(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + assert(k->ops->cancel_cmd); + k->ops->cancel_cmd(s); } @@ -95,23 +112,40 @@ bool tpm_backend_get_tpm_established_flag(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - return k->ops->get_tpm_established_flag(s); + return k->ops->get_tpm_established_flag ? + k->ops->get_tpm_established_flag(s) : false; } int tpm_backend_reset_tpm_established_flag(TPMBackend *s, uint8_t locty) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); - return k->ops->reset_tpm_established_flag(s, locty); + return k->ops->reset_tpm_established_flag ? + k->ops->reset_tpm_established_flag(s, locty) : 0; } TPMVersion tpm_backend_get_tpm_version(TPMBackend *s) { TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + assert(k->ops->get_tpm_version); + return k->ops->get_tpm_version(s); } +TPMInfo *tpm_backend_query_tpm(TPMBackend *s) +{ + TPMInfo *info = g_new0(TPMInfo, 1); + TPMBackendClass *k = TPM_BACKEND_GET_CLASS(s); + + info->id = g_strdup(s->id); + info->model = s->fe_model; + info->options = k->ops->get_tpm_options ? + k->ops->get_tpm_options(s) : NULL; + + return info; +} + static bool tpm_backend_prop_get_opened(Object *obj, Error **errp) { TPMBackend *s = TPM_BACKEND(obj); @@ -152,33 +186,21 @@ static void tpm_backend_prop_set_opened(Object *obj, bool value, Error **errp) static void tpm_backend_instance_init(Object *obj) { + TPMBackend *s = TPM_BACKEND(obj); + object_property_add_bool(obj, "opened", tpm_backend_prop_get_opened, tpm_backend_prop_set_opened, NULL); + s->fe_model = -1; } -void tpm_backend_thread_deliver_request(TPMBackendThread *tbt) -{ - g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_PROCESS_CMD, NULL); -} - -void tpm_backend_thread_create(TPMBackendThread *tbt, - GFunc func, gpointer user_data) +static void tpm_backend_instance_finalize(Object *obj) { - if (!tbt->pool) { - tbt->pool = g_thread_pool_new(func, user_data, 1, TRUE, NULL); - g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_INIT, NULL); - } -} + TPMBackend *s = TPM_BACKEND(obj); -void tpm_backend_thread_end(TPMBackendThread *tbt) -{ - if (tbt->pool) { - g_thread_pool_push(tbt->pool, (gpointer)TPM_BACKEND_CMD_END, NULL); - g_thread_pool_free(tbt->pool, FALSE, TRUE); - tbt->pool = NULL; - } + g_free(s->id); + tpm_backend_thread_end(s); } static const TypeInfo tpm_backend_info = { @@ -186,6 +208,7 @@ static const TypeInfo tpm_backend_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(TPMBackend), .instance_init = tpm_backend_instance_init, + .instance_finalize = tpm_backend_instance_finalize, .class_size = sizeof(TPMBackendClass), .abstract = true, }; diff --git a/block/io.c b/block/io.c index 8e419070b5..0854e0fdac 100644 --- a/block/io.c +++ b/block/io.c @@ -156,6 +156,7 @@ typedef struct { Coroutine *co; BlockDriverState *bs; bool done; + bool begin; } BdrvCoDrainData; static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) @@ -163,18 +164,23 @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque) BdrvCoDrainData *data = opaque; BlockDriverState *bs = data->bs; - bs->drv->bdrv_co_drain(bs); + if (data->begin) { + bs->drv->bdrv_co_drain_begin(bs); + } else { + bs->drv->bdrv_co_drain_end(bs); + } /* Set data->done before reading bs->wakeup. */ atomic_mb_set(&data->done, true); bdrv_wakeup(bs); } -static void bdrv_drain_invoke(BlockDriverState *bs) +static void bdrv_drain_invoke(BlockDriverState *bs, bool begin) { - BdrvCoDrainData data = { .bs = bs, .done = false }; + BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin}; - if (!bs->drv || !bs->drv->bdrv_co_drain) { + if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) || + (!begin && !bs->drv->bdrv_co_drain_end)) { return; } @@ -183,15 +189,16 @@ static void bdrv_drain_invoke(BlockDriverState *bs) BDRV_POLL_WHILE(bs, !data.done); } -static bool bdrv_drain_recurse(BlockDriverState *bs) +static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin) { BdrvChild *child, *tmp; bool waited; - waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); - /* Ensure any pending metadata writes are submitted to bs->file. */ - bdrv_drain_invoke(bs); + bdrv_drain_invoke(bs, begin); + + /* Wait for drained requests to finish */ + waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0); QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) { BlockDriverState *bs = child->bs; @@ -208,7 +215,7 @@ static bool bdrv_drain_recurse(BlockDriverState *bs) */ bdrv_ref(bs); } - waited |= bdrv_drain_recurse(bs); + waited |= bdrv_drain_recurse(bs, begin); if (in_main_loop) { bdrv_unref(bs); } @@ -224,12 +231,18 @@ static void bdrv_co_drain_bh_cb(void *opaque) BlockDriverState *bs = data->bs; bdrv_dec_in_flight(bs); - bdrv_drained_begin(bs); + if (data->begin) { + bdrv_drained_begin(bs); + } else { + bdrv_drained_end(bs); + } + data->done = true; aio_co_wake(co); } -static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) +static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs, + bool begin) { BdrvCoDrainData data; @@ -242,6 +255,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) .co = qemu_coroutine_self(), .bs = bs, .done = false, + .begin = begin, }; bdrv_inc_in_flight(bs); aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), @@ -256,7 +270,7 @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs) void bdrv_drained_begin(BlockDriverState *bs) { if (qemu_in_coroutine()) { - bdrv_co_yield_to_drain(bs); + bdrv_co_yield_to_drain(bs, true); return; } @@ -265,17 +279,22 @@ void bdrv_drained_begin(BlockDriverState *bs) bdrv_parent_drained_begin(bs); } - bdrv_drain_recurse(bs); + bdrv_drain_recurse(bs, true); } void bdrv_drained_end(BlockDriverState *bs) { + if (qemu_in_coroutine()) { + bdrv_co_yield_to_drain(bs, false); + return; + } assert(bs->quiesce_counter > 0); if (atomic_fetch_dec(&bs->quiesce_counter) > 1) { return; } bdrv_parent_drained_end(bs); + bdrv_drain_recurse(bs, false); aio_enable_external(bdrv_get_aio_context(bs)); } @@ -353,7 +372,7 @@ void bdrv_drain_all_begin(void) aio_context_acquire(aio_context); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { if (aio_context == bdrv_get_aio_context(bs)) { - waited |= bdrv_drain_recurse(bs); + waited |= bdrv_drain_recurse(bs, true); } } aio_context_release(aio_context); @@ -374,6 +393,7 @@ void bdrv_drain_all_end(void) aio_context_acquire(aio_context); aio_enable_external(aio_context); bdrv_parent_drained_end(bs); + bdrv_drain_recurse(bs, false); aio_context_release(aio_context); } diff --git a/block/nbd-client.c b/block/nbd-client.c index 72651dcdb1..c0683c3c83 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -156,7 +156,6 @@ static int nbd_co_send_request(BlockDriverState *bs, qio_channel_set_cork(s->ioc, true); rc = nbd_send_request(s->ioc, request); if (rc >= 0 && !s->quit) { - assert(request->len == iov_size(qiov->iov, qiov->niov)); if (qio_channel_writev_all(s->ioc, qiov->iov, qiov->niov, NULL) < 0) { rc = -EIO; @@ -181,11 +180,11 @@ err: } static int nbd_co_receive_reply(NBDClientSession *s, - NBDRequest *request, + uint64_t handle, QEMUIOVector *qiov) { int ret; - int i = HANDLE_TO_INDEX(s, request->handle); + int i = HANDLE_TO_INDEX(s, handle); /* Wait until we're woken up by nbd_read_reply_entry. */ s->requests[i].receiving = true; @@ -194,10 +193,9 @@ static int nbd_co_receive_reply(NBDClientSession *s, if (!s->ioc || s->quit) { ret = -EIO; } else { - assert(s->reply.handle == request->handle); + assert(s->reply.handle == handle); ret = -s->reply.error; if (qiov && s->reply.error == 0) { - assert(request->len == iov_size(qiov->iov, qiov->niov)); if (qio_channel_readv_all(s->ioc, qiov->iov, qiov->niov, NULL) < 0) { ret = -EIO; @@ -231,15 +229,19 @@ static int nbd_co_request(BlockDriverState *bs, NBDClientSession *client = nbd_get_client_session(bs); int ret; - assert(!qiov || request->type == NBD_CMD_WRITE || - request->type == NBD_CMD_READ); + if (qiov) { + assert(request->type == NBD_CMD_WRITE || request->type == NBD_CMD_READ); + assert(request->len == iov_size(qiov->iov, qiov->niov)); + } else { + assert(request->type != NBD_CMD_WRITE && request->type != NBD_CMD_READ); + } ret = nbd_co_send_request(bs, request, request->type == NBD_CMD_WRITE ? qiov : NULL); if (ret < 0) { return ret; } - return nbd_co_receive_reply(client, request, + return nbd_co_receive_reply(client, request->handle, request->type == NBD_CMD_READ ? qiov : NULL); } diff --git a/block/qed.c b/block/qed.c index 28e2ec89e8..821dcaa055 100644 --- a/block/qed.c +++ b/block/qed.c @@ -265,7 +265,7 @@ static bool qed_plug_allocating_write_reqs(BDRVQEDState *s) assert(!s->allocating_write_reqs_plugged); if (s->allocating_acb != NULL) { /* Another allocating write came concurrently. This cannot happen - * from bdrv_qed_co_drain, but it can happen when the timer runs. + * from bdrv_qed_co_drain_begin, but it can happen when the timer runs. */ qemu_co_mutex_unlock(&s->table_lock); return false; @@ -358,7 +358,7 @@ static void bdrv_qed_attach_aio_context(BlockDriverState *bs, } } -static void coroutine_fn bdrv_qed_co_drain(BlockDriverState *bs) +static void coroutine_fn bdrv_qed_co_drain_begin(BlockDriverState *bs) { BDRVQEDState *s = bs->opaque; @@ -1608,7 +1608,7 @@ static BlockDriver bdrv_qed = { .bdrv_check = bdrv_qed_check, .bdrv_detach_aio_context = bdrv_qed_detach_aio_context, .bdrv_attach_aio_context = bdrv_qed_attach_aio_context, - .bdrv_co_drain = bdrv_qed_co_drain, + .bdrv_co_drain_begin = bdrv_qed_co_drain_begin, }; static void bdrv_qed_init(void) diff --git a/block/throttle.c b/block/throttle.c index 5bca76300f..833175ac77 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -197,6 +197,21 @@ static bool throttle_recurse_is_first_non_filter(BlockDriverState *bs, return bdrv_recurse_is_first_non_filter(bs->file->bs, candidate); } +static void coroutine_fn throttle_co_drain_begin(BlockDriverState *bs) +{ + ThrottleGroupMember *tgm = bs->opaque; + if (atomic_fetch_inc(&tgm->io_limits_disabled) == 0) { + throttle_group_restart_tgm(tgm); + } +} + +static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs) +{ + ThrottleGroupMember *tgm = bs->opaque; + assert(tgm->io_limits_disabled); + atomic_dec(&tgm->io_limits_disabled); +} + static BlockDriver bdrv_throttle = { .format_name = "throttle", .protocol_name = "throttle", @@ -226,6 +241,9 @@ static BlockDriver bdrv_throttle = { .bdrv_reopen_abort = throttle_reopen_abort, .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file, + .bdrv_co_drain_begin = throttle_co_drain_begin, + .bdrv_co_drain_end = throttle_co_drain_end, + .is_filter = true, }; @@ -3495,6 +3495,12 @@ else tpm_passthrough=no fi +# TPM emulator is for all posix systems +if test "$mingw32" != "yes"; then + tpm_emulator=$tpm +else + tpm_emulator=no +fi ########################################## # attr probe @@ -5412,6 +5418,7 @@ echo "gcov enabled $gcov" echo "TPM support $tpm" echo "libssh2 support $libssh2" echo "TPM passthrough $tpm_passthrough" +echo "TPM emulator $tpm_emulator" echo "QOM debugging $qom_cast_debug" echo "Live block migration $live_block_migration" echo "lzo support $lzo" @@ -6011,12 +6018,16 @@ if test "$live_block_migration" = "yes" ; then echo "CONFIG_LIVE_BLOCK_MIGRATION=y" >> $config_host_mak fi -# TPM passthrough support? if test "$tpm" = "yes"; then echo 'CONFIG_TPM=$(CONFIG_SOFTMMU)' >> $config_host_mak + # TPM passthrough support? if test "$tpm_passthrough" = "yes"; then echo "CONFIG_TPM_PASSTHROUGH=y" >> $config_host_mak fi + # TPM emulator support? + if test "$tpm_emulator" = "yes"; then + echo "CONFIG_TPM_EMULATOR=y" >> $config_host_mak + fi fi echo "TRACE_BACKENDS=$trace_backends" >> $config_host_mak diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index a0e0da4ccb..f409bd3d41 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -56,16 +56,13 @@ } while (0) static const char * -vu_request_to_string(int req) +vu_request_to_string(unsigned int req) { #define REQ(req) [req] = #req static const char *vu_request_str[] = { REQ(VHOST_USER_NONE), REQ(VHOST_USER_GET_FEATURES), REQ(VHOST_USER_SET_FEATURES), - REQ(VHOST_USER_NONE), - REQ(VHOST_USER_GET_FEATURES), - REQ(VHOST_USER_SET_FEATURES), REQ(VHOST_USER_SET_OWNER), REQ(VHOST_USER_RESET_OWNER), REQ(VHOST_USER_SET_MEM_TABLE), @@ -83,7 +80,10 @@ vu_request_to_string(int req) REQ(VHOST_USER_GET_QUEUE_NUM), REQ(VHOST_USER_SET_VRING_ENABLE), REQ(VHOST_USER_SEND_RARP), - REQ(VHOST_USER_INPUT_GET_CONFIG), + REQ(VHOST_USER_NET_SET_MTU), + REQ(VHOST_USER_SET_SLAVE_REQ_FD), + REQ(VHOST_USER_IOTLB_MSG), + REQ(VHOST_USER_SET_VRING_ENDIAN), REQ(VHOST_USER_MAX), }; #undef REQ @@ -726,7 +726,8 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg) static bool vu_get_protocol_features_exec(VuDev *dev, VhostUserMsg *vmsg) { - uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD; + uint64_t features = 1ULL << VHOST_USER_PROTOCOL_F_LOG_SHMFD | + 1ULL << VHOST_USER_PROTOCOL_F_SLAVE_REQ; if (dev->iface->get_protocol_features) { features |= dev->iface->get_protocol_features(dev); @@ -780,6 +781,23 @@ vu_set_vring_enable_exec(VuDev *dev, VhostUserMsg *vmsg) } static bool +vu_set_slave_req_fd(VuDev *dev, VhostUserMsg *vmsg) +{ + if (vmsg->fd_num != 1) { + vu_panic(dev, "Invalid slave_req_fd message (%d fd's)", vmsg->fd_num); + return false; + } + + if (dev->slave_fd != -1) { + close(dev->slave_fd); + } + dev->slave_fd = vmsg->fds[0]; + DPRINT("Got slave_fd: %d\n", vmsg->fds[0]); + + return false; +} + +static bool vu_process_message(VuDev *dev, VhostUserMsg *vmsg) { int do_reply = 0; @@ -842,6 +860,8 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg) return vu_get_queue_num_exec(dev, vmsg); case VHOST_USER_SET_VRING_ENABLE: return vu_set_vring_enable_exec(dev, vmsg); + case VHOST_USER_SET_SLAVE_REQ_FD: + return vu_set_slave_req_fd(dev, vmsg); case VHOST_USER_NONE: break; default: @@ -915,6 +935,10 @@ vu_deinit(VuDev *dev) vu_close_log(dev); + if (dev->slave_fd != -1) { + close(dev->slave_fd); + dev->slave_fd = -1; + } if (dev->sock != -1) { close(dev->sock); @@ -945,6 +969,7 @@ vu_init(VuDev *dev, dev->remove_watch = remove_watch; dev->iface = iface; dev->log_call_fd = -1; + dev->slave_fd = -1; for (i = 0; i < VHOST_MAX_NR_VIRTQUEUE; i++) { dev->vq[i] = (VuVirtq) { .call_fd = -1, .kick_fd = -1, .err_fd = -1, @@ -966,6 +991,12 @@ vu_queue_enabled(VuDev *dev, VuVirtq *vq) return vq->enable; } +bool +vu_queue_started(const VuDev *dev, const VuVirtq *vq) +{ + return vq->started; +} + static inline uint16_t vring_avail_flags(VuVirtq *vq) { diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 5825b66880..2f5864b5c4 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -34,6 +34,10 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_MQ = 0, VHOST_USER_PROTOCOL_F_LOG_SHMFD = 1, VHOST_USER_PROTOCOL_F_RARP = 2, + VHOST_USER_PROTOCOL_F_REPLY_ACK = 3, + VHOST_USER_PROTOCOL_F_NET_MTU = 4, + VHOST_USER_PROTOCOL_F_SLAVE_REQ = 5, + VHOST_USER_PROTOCOL_F_CROSS_ENDIAN = 6, VHOST_USER_PROTOCOL_F_MAX }; @@ -61,7 +65,10 @@ typedef enum VhostUserRequest { VHOST_USER_GET_QUEUE_NUM = 17, VHOST_USER_SET_VRING_ENABLE = 18, VHOST_USER_SEND_RARP = 19, - VHOST_USER_INPUT_GET_CONFIG = 20, + VHOST_USER_NET_SET_MTU = 20, + VHOST_USER_SET_SLAVE_REQ_FD = 21, + VHOST_USER_IOTLB_MSG = 22, + VHOST_USER_SET_VRING_ENDIAN = 23, VHOST_USER_MAX } VhostUserRequest; @@ -219,6 +226,7 @@ struct VuDev { VuDevRegion regions[VHOST_MEMORY_MAX_NREGIONS]; VuVirtq vq[VHOST_MAX_NR_VIRTQUEUE]; int log_call_fd; + int slave_fd; uint64_t log_size; uint8_t *log_table; uint64_t features; @@ -335,6 +343,15 @@ void vu_queue_set_notification(VuDev *dev, VuVirtq *vq, int enable); bool vu_queue_enabled(VuDev *dev, VuVirtq *vq); /** + * vu_queue_started: + * @dev: a VuDev context + * @vq: a VuVirtq queue + * + * Returns: whether the queue is started. + */ +bool vu_queue_started(const VuDev *dev, const VuVirtq *vq); + +/** * vu_queue_empty: * @dev: a VuDev context * @vq: a VuVirtq queue diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt new file mode 100644 index 0000000000..914daaceea --- /dev/null +++ b/docs/specs/tpm.txt @@ -0,0 +1,123 @@ +QEMU TPM Device +=============== + += Guest-side Hardware Interface = + +The QEMU TPM emulation implements a TPM TIS hardware interface following the +Trusted Computing Group's specification "TCG PC Client Specific TPM Interface +Specification (TIS)", Specification Version 1.3, 21 March 2013. This +specification, or a later version of it, can be accessed from the following +URL: + +https://trustedcomputinggroup.org/pc-client-work-group-pc-client-specific-tpm-interface-specification-tis/ + +The TIS interface makes a memory mapped IO region in the area 0xfed40000 - +0xfed44fff available to the guest operating system. + + +QEMU files related to TPM TIS interface: + - hw/tpm/tpm_tis.c + - hw/tpm/tpm_tis.h + + += ACPI Interface = + +The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT and passes +it into the guest through the fw_cfg device. The device description contains +the base address of the TIS interface 0xfed40000 and the size of the MMIO area +(0x5000). In case a TPM2 is used by QEMU, a TPM2 ACPI table is also provided. +The device is described to be used in polling mode rather than interrupt mode +primarily because no unused IRQ could be found. + +To support measurement logs to be written by the firmware, e.g. SeaBIOS, a TCPA +table is implemented. This table provides a 64kb buffer where the firmware can +write its log into. For TPM 2 only a more recent version of the TPM2 table +provides support for measurements logs and a TCPA table does not need to be +created. + +The TCPA and TPM2 ACPI tables follow the Trusted Computing Group specification +"TCG ACPI Specification" Family "1.2" and "2.0", Level 00 Revision 00.37. This +specification, or a later version of it, can be accessed from the following +URL: + +https://trustedcomputinggroup.org/tcg-acpi-specification/ + + +QEMU files related to TPM ACPI tables: + - hw/i386/acpi-build.c + - include/hw/acpi/tpm.h + + += TPM backend devices = + +The TPM implementation is split into two parts, frontend and backend. The +frontend part is the hardware interface, such as the TPM TIS interface +described earlier, and the other part is the TPM backend interface. The backend +interfaces implement the interaction with a TPM device, which may be a physical +or an emulated device. The split between the front- and backend devices allows +a frontend to be connected with any available backend. This enables the TIS +interface to be used with the passthrough backend or the (future) swtpm backend. + + +QEMU files related to TPM backends: + - backends/tpm.c + - include/sysemu/tpm_backend.h + - include/sysemu/tpm_backend_int.h + + +== The QEMU TPM passthrough device == + +In case QEMU is run on Linux as the host operating system it is possible to +make the hardware TPM device available to a single QEMU guest. In this case the +user must make sure that no other program is using the device, e.g., /dev/tpm0, +before trying to start QEMU with it. + +The passthrough driver uses the host's TPM device for sending TPM commands +and receiving responses from. Besides that it accesses the TPM device's sysfs +entry for support of command cancellation. Since none of the state of a +hardware TPM can be migrated between hosts, virtual machine migration is +disabled when the TPM passthrough driver is used. + +Since the host's TPM device will already be initialized by the host's firmware, +certain commands, e.g. TPM_Startup(), sent by the virtual firmware for device +initialization, will fail. In this case the firmware should not use the TPM. + +Sharing the device with the host is generally not a recommended usage scenario +for a TPM device. The primary reason for this is that two operating systems can +then access the device's single set of resources, such as platform configuration +registers (PCRs). Applications or kernel security subsystems, such as the +Linux Integrity Measurement Architecture (IMA), are not expecting to share PCRs. + + +QEMU files related to the TPM passthrough device: + - hw/tpm/tpm_passthrough.c + - hw/tpm/tpm_util.c + - hw/tpm/tpm_util.h + + +Command line to start QEMU with the TPM passthrough device using the host's +hardware TPM /dev/tpm0: + +qemu-system-x86_64 -display sdl -enable-kvm \ + -m 1024 -boot d -bios bios-256k.bin -boot menu=on \ + -tpmdev passthrough,id=tpm0,path=/dev/tpm0 \ + -device tpm-tis,tpmdev=tpm0 test.img + +The following commands should result in similar output inside the VM with a +Linux kernel that either has the TPM TIS driver built-in or available as a +module: + +#> dmesg | grep -i tpm +[ 0.711310] tpm_tis 00:06: 1.2 TPM (device=id 0x1, rev-id 1) + +#> dmesg | grep TCPA +[ 0.000000] ACPI: TCPA 0x0000000003FFD191C 000032 (v02 BOCHS \ + BXPCTCPA 0000001 BXPC 00000001) + +#> ls -l /dev/tpm* +crw-------. 1 root root 10, 224 Jul 11 10:11 /dev/tpm0 + +#> find /sys/devices/ | grep pcrs$ | xargs cat +PCR-00: 35 4E 3B CE 23 9F 38 59 ... +... +PCR-23: 00 00 00 00 00 00 00 00 ... diff --git a/docs/specs/vmcoreinfo.txt b/docs/specs/vmcoreinfo.txt new file mode 100644 index 0000000000..821261067f --- /dev/null +++ b/docs/specs/vmcoreinfo.txt @@ -0,0 +1,49 @@ +================= +VMCoreInfo device +================= + +The `-device vmcoreinfo` will create a fw_cfg entry for a guest to +store dump details. + +etc/vmcoreinfo +************** + +A guest may use this fw_cfg entry to add information details to qemu +dumps. + +The entry of 16 bytes has the following layout, in little-endian:: + +#define VMCOREINFO_FORMAT_NONE 0x0 +#define VMCOREINFO_FORMAT_ELF 0x1 + + struct FWCfgVMCoreInfo { + uint16_t host_format; /* formats host supports */ + uint16_t guest_format; /* format guest supplies */ + uint32_t size; /* size of vmcoreinfo region */ + uint64_t paddr; /* physical address of vmcoreinfo region */ + }; + +Only full write (of 16 bytes) are considered valid for further +processing of entry values. + +A write of 0 in guest_format will disable further processing of +vmcoreinfo entry values & content. + +Format & content +**************** + +As of qemu 2.11, only VMCOREINFO_FORMAT_ELF is supported. + +The entry gives location and size of an ELF note that is appended in +qemu dumps. + +The note format/class must be of the target bitness and the size must +be less than 1Mb. + +If the ELF note name is "VMCOREINFO", it is expected to be the Linux +vmcoreinfo note (see Documentation/ABI/testing/sysfs-kernel-vmcoreinfo +in Linux source). In this case, qemu dump code will read the content +as a key=value text file, looking for "NUMBER(phys_base)" key +value. The value is expected to be more accurate than architecture +guess of the value. This is useful for KASLR-enabled guest with +ancient tools not handling the VMCOREINFO note. @@ -25,6 +25,8 @@ #include "qapi/qmp/qerror.h" #include "qmp-commands.h" #include "qapi-event.h" +#include "qemu/error-report.h" +#include "hw/misc/vmcoreinfo.h" #include <zlib.h> #ifdef CONFIG_LZO @@ -37,6 +39,13 @@ #define ELF_MACHINE_UNAME "Unknown" #endif +#define MAX_GUEST_NOTE_SIZE (1 << 20) /* 1MB should be enough */ + +#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ + ((DIV_ROUND_UP((hdr_size), 4) + \ + DIV_ROUND_UP((name_size), 4) + \ + DIV_ROUND_UP((desc_size), 4)) * 4) + uint16_t cpu_to_dump16(DumpState *s, uint16_t val) { if (s->dump_info.d_endian == ELFDATA2LSB) { @@ -75,6 +84,8 @@ static int dump_cleanup(DumpState *s) guest_phys_blocks_free(&s->guest_phys_blocks); memory_mapping_list_free(&s->list); close(s->fd); + g_free(s->guest_note); + s->guest_note = NULL; if (s->resume) { if (s->detached) { qemu_mutex_lock_iothread(); @@ -234,6 +245,19 @@ static inline int cpu_index(CPUState *cpu) return cpu->cpu_index + 1; } +static void write_guest_note(WriteCoreDumpFunction f, DumpState *s, + Error **errp) +{ + int ret; + + if (s->guest_note) { + ret = f(s->guest_note, s->guest_note_size, s); + if (ret < 0) { + error_setg(errp, "dump: failed to write guest note"); + } + } +} + static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s, Error **errp) { @@ -257,6 +281,8 @@ static void write_elf64_notes(WriteCoreDumpFunction f, DumpState *s, return; } } + + write_guest_note(f, s, errp); } static void write_elf32_note(DumpState *s, Error **errp) @@ -302,6 +328,8 @@ static void write_elf32_notes(WriteCoreDumpFunction f, DumpState *s, return; } } + + write_guest_note(f, s, errp); } static void write_elf_section(DumpState *s, int type, Error **errp) @@ -713,6 +741,61 @@ static int buf_write_note(const void *buf, size_t size, void *opaque) return 0; } +/* + * This function retrieves various sizes from an elf header. + * + * @note has to be a valid ELF note. The return sizes are unmodified + * (not padded or rounded up to be multiple of 4). + */ +static void get_note_sizes(DumpState *s, const void *note, + uint64_t *note_head_size, + uint64_t *name_size, + uint64_t *desc_size) +{ + uint64_t note_head_sz; + uint64_t name_sz; + uint64_t desc_sz; + + if (s->dump_info.d_class == ELFCLASS64) { + const Elf64_Nhdr *hdr = note; + note_head_sz = sizeof(Elf64_Nhdr); + name_sz = tswap64(hdr->n_namesz); + desc_sz = tswap64(hdr->n_descsz); + } else { + const Elf32_Nhdr *hdr = note; + note_head_sz = sizeof(Elf32_Nhdr); + name_sz = tswap32(hdr->n_namesz); + desc_sz = tswap32(hdr->n_descsz); + } + + if (note_head_size) { + *note_head_size = note_head_sz; + } + if (name_size) { + *name_size = name_sz; + } + if (desc_size) { + *desc_size = desc_sz; + } +} + +static bool note_name_equal(DumpState *s, + const uint8_t *note, const char *name) +{ + int len = strlen(name) + 1; + uint64_t head_size, name_size; + + get_note_sizes(s, note, &head_size, &name_size, NULL); + head_size = ROUND_UP(head_size, 4); + + if (name_size != len || + memcmp(note + head_size, "VMCOREINFO", len)) { + return false; + } + + return true; +} + /* write common header, sub header and elf note to vmcore */ static void create_header32(DumpState *s, Error **errp) { @@ -774,6 +857,18 @@ static void create_header32(DumpState *s, Error **errp) kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL); offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; + if (s->guest_note && + note_name_equal(s, s->guest_note, "VMCOREINFO")) { + uint64_t hsize, name_size, size_vmcoreinfo_desc, offset_vmcoreinfo; + + get_note_sizes(s, s->guest_note, + &hsize, &name_size, &size_vmcoreinfo_desc); + offset_vmcoreinfo = offset_note + s->note_size - s->guest_note_size + + (DIV_ROUND_UP(hsize, 4) + DIV_ROUND_UP(name_size, 4)) * 4; + kh->offset_vmcoreinfo = cpu_to_dump64(s, offset_vmcoreinfo); + kh->size_vmcoreinfo = cpu_to_dump32(s, size_vmcoreinfo_desc); + } + kh->offset_note = cpu_to_dump64(s, offset_note); kh->note_size = cpu_to_dump32(s, s->note_size); @@ -874,6 +969,18 @@ static void create_header64(DumpState *s, Error **errp) kh->dump_level = cpu_to_dump32(s, DUMP_LEVEL); offset_note = DISKDUMP_HEADER_BLOCKS * block_size + size; + if (s->guest_note && + note_name_equal(s, s->guest_note, "VMCOREINFO")) { + uint64_t hsize, name_size, size_vmcoreinfo_desc, offset_vmcoreinfo; + + get_note_sizes(s, s->guest_note, + &hsize, &name_size, &size_vmcoreinfo_desc); + offset_vmcoreinfo = offset_note + s->note_size - s->guest_note_size + + (DIV_ROUND_UP(hsize, 4) + DIV_ROUND_UP(name_size, 4)) * 4; + kh->offset_vmcoreinfo = cpu_to_dump64(s, offset_vmcoreinfo); + kh->size_vmcoreinfo = cpu_to_dump64(s, size_vmcoreinfo_desc); + } + kh->offset_note = cpu_to_dump64(s, offset_note); kh->note_size = cpu_to_dump64(s, s->note_size); @@ -1487,10 +1594,44 @@ static int64_t dump_calculate_size(DumpState *s) return total; } +static void vmcoreinfo_update_phys_base(DumpState *s) +{ + uint64_t size, note_head_size, name_size, phys_base; + char **lines; + uint8_t *vmci; + size_t i; + + if (!note_name_equal(s, s->guest_note, "VMCOREINFO")) { + return; + } + + get_note_sizes(s, s->guest_note, ¬e_head_size, &name_size, &size); + note_head_size = ROUND_UP(note_head_size, 4); + + vmci = s->guest_note + note_head_size + ROUND_UP(name_size, 4); + *(vmci + size) = '\0'; + + lines = g_strsplit((char *)vmci, "\n", -1); + for (i = 0; lines[i]; i++) { + if (g_str_has_prefix(lines[i], "NUMBER(phys_base)=")) { + if (qemu_strtou64(lines[i] + 18, NULL, 16, + &phys_base) < 0) { + warn_report("Failed to read NUMBER(phys_base)="); + } else { + s->dump_info.phys_base = phys_base; + } + break; + } + } + + g_strfreev(lines); +} + static void dump_init(DumpState *s, int fd, bool has_format, DumpGuestMemoryFormat format, bool paging, bool has_filter, int64_t begin, int64_t length, Error **errp) { + VMCoreInfoState *vmci = vmcoreinfo_find(); CPUState *cpu; int nr_cpus; Error *err = NULL; @@ -1568,6 +1709,48 @@ static void dump_init(DumpState *s, int fd, bool has_format, goto cleanup; } + /* + * The goal of this block is to (a) update the previously guessed + * phys_base, (b) copy the guest note out of the guest. + * Failure to do so is not fatal for dumping. + */ + if (vmci) { + uint64_t addr, note_head_size, name_size, desc_size; + uint32_t size; + uint16_t format; + + note_head_size = s->dump_info.d_class == ELFCLASS32 ? + sizeof(Elf32_Nhdr) : sizeof(Elf64_Nhdr); + + format = le16_to_cpu(vmci->vmcoreinfo.guest_format); + size = le32_to_cpu(vmci->vmcoreinfo.size); + addr = le64_to_cpu(vmci->vmcoreinfo.paddr); + if (!vmci->has_vmcoreinfo) { + warn_report("guest note is not present"); + } else if (size < note_head_size || size > MAX_GUEST_NOTE_SIZE) { + warn_report("guest note size is invalid: %" PRIu32, size); + } else if (format != VMCOREINFO_FORMAT_ELF) { + warn_report("guest note format is unsupported: %" PRIu16, format); + } else { + s->guest_note = g_malloc(size + 1); /* +1 for adding \0 */ + cpu_physical_memory_read(addr, s->guest_note, size); + + get_note_sizes(s, s->guest_note, NULL, &name_size, &desc_size); + s->guest_note_size = ELF_NOTE_SIZE(note_head_size, name_size, + desc_size); + if (name_size > MAX_GUEST_NOTE_SIZE || + desc_size > MAX_GUEST_NOTE_SIZE || + s->guest_note_size > size) { + warn_report("Invalid guest note header"); + g_free(s->guest_note); + s->guest_note = NULL; + } else { + vmcoreinfo_update_phys_base(s); + s->note_size += s->guest_note_size; + } + } + } + /* get memory mapping */ if (paging) { qemu_get_guest_memory_mapping(&s->list, &s->guest_phys_blocks, &err); @@ -1000,6 +1000,7 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) Error *err = NULL; unsigned int c = 0; TPMPassthroughOptions *tpo; + TPMEmulatorOptions *teo; info_list = qmp_query_tpm(&err); if (err) { @@ -1029,6 +1030,10 @@ void hmp_info_tpm(Monitor *mon, const QDict *qdict) tpo->has_cancel_path ? ",cancel-path=" : "", tpo->has_cancel_path ? tpo->cancel_path : ""); break; + case TPM_TYPE_OPTIONS_KIND_EMULATOR: + teo = ti->options->u.emulator.data; + monitor_printf(mon, ",chardev=%s", teo->chardev); + break; case TPM_TYPE_OPTIONS_KIND__MAX: break; } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index f4fd5907b8..a0fb1ce037 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -718,6 +718,7 @@ static const TypeInfo piix4_pm_info = { .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } }; diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 2876d8a639..105044f666 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -124,7 +124,7 @@ void vmgenid_add_fw_cfg(VmGenIdState *vms, FWCfgState *s, GArray *guid) fw_cfg_add_file(s, VMGENID_GUID_FW_CFG_FILE, guid->data, VMGENID_FW_CFG_SIZE); /* Create a read-write fw_cfg file for Address */ - fw_cfg_add_file_callback(s, VMGENID_ADDR_FW_CFG_FILE, NULL, NULL, + fw_cfg_add_file_callback(s, VMGENID_ADDR_FW_CFG_FILE, NULL, NULL, NULL, vms->vmgenid_addr_le, ARRAY_SIZE(vms->vmgenid_addr_le), false); } diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 959c786261..337402e9c6 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1431,6 +1431,10 @@ static const TypeInfo ac97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (AC97LinkState), .class_init = ac97_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void ac97_register_types (void) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index dd7c23d185..59cf252754 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -1082,6 +1082,10 @@ static const TypeInfo es1370_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (ES1370State), .class_init = es1370_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void es1370_register_types (void) diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 18a50a8f83..a3e670c188 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -1263,6 +1263,10 @@ static const TypeInfo intel_hda_info = { .instance_size = sizeof(IntelHDAState), .class_init = intel_hda_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static const TypeInfo intel_hda_info_ich6 = { diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 9aa32692a3..441e21ed1f 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -1110,6 +1110,10 @@ static const TypeInfo nvme_info = { .instance_size = sizeof(NvmeCtrl), .class_init = nvme_class_init, .instance_init = nvme_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }; static void nvme_register_types(void) diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 303104dd19..cb0d04c1d9 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -250,6 +250,10 @@ static const TypeInfo serial_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCISerialState), .class_init = serial_pci_class_initfn, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static const TypeInfo multi_2x_serial_pci_info = { @@ -257,6 +261,10 @@ static const TypeInfo multi_2x_serial_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIMultiSerialState), .class_init = multi_2x_serial_pci_class_initfn, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static const TypeInfo multi_4x_serial_pci_info = { @@ -264,6 +272,10 @@ static const TypeInfo multi_4x_serial_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIMultiSerialState), .class_init = multi_4x_serial_pci_class_initfn, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void serial_pci_register_types(void) diff --git a/hw/core/loader.c b/hw/core/loader.c index 4593061445..91669d65aa 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1023,7 +1023,7 @@ MemoryRegion *rom_add_blob(const char *name, const void *blob, size_t len, } fw_cfg_add_file_callback(fw_cfg, fw_file_name, - fw_callback, callback_opaque, + fw_callback, NULL, callback_opaque, data, rom->datasize, read_only); } return mr; diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index afc290ab91..b4d579857a 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -3162,6 +3162,10 @@ static const TypeInfo cirrus_vga_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCICirrusVGAState), .class_init = cirrus_vga_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void cirrus_vga_register_types(void) diff --git a/hw/display/qxl.c b/hw/display/qxl.c index d92fe05f1a..99365c3e8f 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2432,6 +2432,10 @@ static const TypeInfo qxl_pci_type_info = { .instance_size = sizeof(PCIQXLDevice), .abstract = true, .class_init = qxl_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void qxl_primary_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 9aa515b3da..6eddac911e 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -1843,6 +1843,10 @@ static const TypeInfo sm501_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(SM501PCIState), .class_init = sm501_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void sm501_register_types(void) diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index ac9a76499e..7adb89fcb4 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -338,6 +338,10 @@ static const TypeInfo vga_pci_type_info = { .instance_size = sizeof(PCIVGAState), .abstract = true, .class_init = vga_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void vga_class_init(ObjectClass *klass, void *data) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 4a64b41259..cdc3fed6ca 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1350,6 +1350,10 @@ static const TypeInfo vmsvga_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(struct pci_vmsvga_state_s), .class_init = vmsvga_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void vmsvga_register_types(void) diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index ea51e09186..e47556c9d8 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -119,6 +119,10 @@ static const TypeInfo ich9_smb_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ICH9SMBState), .class_init = ich9_smb_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void ich9_smb_register(void) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 2af37a9129..73519ab3ac 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2911,7 +2911,7 @@ void acpi_setup(void) build_state->rsdp = g_memdup(tables.rsdp->data, rsdp_size); fw_cfg_add_file_callback(pcms->fw_cfg, ACPI_BUILD_RSDP_FILE, - acpi_build_update, build_state, + acpi_build_update, NULL, build_state, build_state->rsdp, rsdp_size, true); build_state->rsdp_mr = NULL; } else { diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 334938a280..ad8155ca4c 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1227,6 +1227,10 @@ static const TypeInfo amdviPCI = { .name = "AMDVI-PCI", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AMDVIPCIState), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, void *data) diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c index 33e20cb3e8..d8559d8342 100644 --- a/hw/i386/kvm/pci-assign.c +++ b/hw/i386/kvm/pci-assign.c @@ -1864,6 +1864,10 @@ static const TypeInfo assign_info = { .instance_size = sizeof(AssignedDevice), .class_init = assign_class_init, .instance_init = assigned_dev_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void assign_register_types(void) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9ff79b1fd9..f79d5cb694 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -378,8 +378,6 @@ static void pc_compat_0_13(MachineState *machine) static void pc_init_isa(MachineState *machine) { - x86_cpu_change_kvm_default("kvm-pv-eoi", NULL); - enable_compat_apic_id_mode(); pc_init1(machine, TYPE_I440FX_PCI_HOST_BRIDGE, TYPE_I440FX_PCI_DEVICE); } @@ -428,7 +426,6 @@ static void pc_i440fx_machine_options(MachineClass *m) { m->family = "pc_piix"; m->desc = "Standard PC (i440FX + PIIX, 1996)"; - m->hot_add_cpu = pc_hot_add_cpu; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; } @@ -1055,6 +1052,10 @@ static TypeInfo isa_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = isa_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pt_graphics_register_types(void) @@ -1123,7 +1124,6 @@ static void xenfv_machine_options(MachineClass *m) m->desc = "Xen Fully-virtualized PC"; m->max_cpus = HVM_MAX_VCPUS; m->default_machine_opts = "accel=xen"; - m->hot_add_cpu = pc_hot_add_cpu; } DEFINE_PC_MACHINE(xenfv, "xenfv", pc_xen_hvm_init, diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6c4ec4be4e..da3ea602e1 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -295,7 +295,6 @@ static void pc_q35_machine_options(MachineClass *m) { m->family = "pc_q35"; m->desc = "Standard PC (Q35 + ICH9, 2009)"; - m->hot_add_cpu = pc_hot_add_cpu; m->units_per_default_bus = 1; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 9ba7474566..056b87de0b 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -517,6 +517,10 @@ static const TypeInfo xen_platform_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIXenPlatformState), .class_init = xen_platform_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void xen_platform_register_types(void) diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index c093b34458..f748823658 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -127,6 +127,10 @@ static const TypeInfo xen_pv_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPVDevice), .class_init = xen_pv_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void xen_pv_register_types(void) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 9472a60cab..8dd0ced6b3 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -184,6 +184,10 @@ static const TypeInfo ich_ahci_info = { .instance_size = sizeof(AHCIPCIState), .instance_init = pci_ich9_ahci_init, .class_init = ich_ahci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void ich_ahci_register_types(void) diff --git a/hw/ide/pci.c b/hw/ide/pci.c index d53ff5341c..25f1d36f3a 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -453,6 +453,10 @@ static const TypeInfo pci_ide_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIIDEState), .abstract = true, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pci_ide_register_types(void) diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index 4dfa6b33f3..da05c8589d 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -646,6 +646,10 @@ static const TypeInfo tpci200_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(TPCI200State), .class_init = tpci200_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void tpci200_register_types(void) diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 4d29a9900c..d20ea4c2ee 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -138,6 +138,10 @@ static const TypeInfo i82378_type_info = { .instance_size = sizeof(I82378State), .instance_init = i82378_init, .class_init = i82378_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void i82378_register_types(void) diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index ac8416d42b..ec3c9f7d0b 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -402,12 +402,12 @@ void ich9_lpc_pm_init(PCIDevice *lpc_pci, bool smm_enabled) * just link them into fw_cfg here. */ fw_cfg_add_file_callback(fw_cfg, "etc/smi/requested-features", - NULL, NULL, + NULL, NULL, NULL, lpc->smi_guest_features_le, sizeof lpc->smi_guest_features_le, false); fw_cfg_add_file_callback(fw_cfg, "etc/smi/features-ok", - smi_features_ok_callback, lpc, + smi_features_ok_callback, NULL, lpc, &lpc->smi_features_ok, sizeof lpc->smi_features_ok, true); @@ -823,6 +823,7 @@ static const TypeInfo ich9_lpc_info = { .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } }; diff --git a/hw/isa/piix4.c b/hw/isa/piix4.c index f811eba59d..6b8bc3faf0 100644 --- a/hw/isa/piix4.c +++ b/hw/isa/piix4.c @@ -132,6 +132,10 @@ static const TypeInfo piix4_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PIIX4State), .class_init = piix4_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void piix4_register_types(void) diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 50dc83df77..c129985e2a 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -301,6 +301,10 @@ static const TypeInfo via_ac97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT686AC97State), .class_init = via_ac97_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void vt82c686b_mc97_realize(PCIDevice *dev, Error **errp) @@ -341,6 +345,10 @@ static const TypeInfo via_mc97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT686MC97State), .class_init = via_mc97_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; /* vt82c686 pm init */ @@ -419,6 +427,10 @@ static const TypeInfo via_pm_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT686PMState), .class_init = via_pm_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static const VMStateDescription vmstate_via = { @@ -502,6 +514,10 @@ static const TypeInfo via_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VT82C686BState), .class_init = via_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void vt82c686b_register_types(void) diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index e8b2eef688..5a9dad9aae 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -1232,6 +1232,10 @@ static const TypeInfo gt64120_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = gt64120_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void gt64120_class_init(ObjectClass *klass, void *data) diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index e8f0a02f35..19202d90cf 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -9,6 +9,7 @@ common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o common-obj-$(CONFIG_EDU) += edu.o common-obj-y += unimp.o +common-obj-y += vmcoreinfo.o obj-$(CONFIG_VMPORT) += vmport.o diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 01acacf142..34eb05d213 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -408,12 +408,17 @@ static void edu_class_init(ObjectClass *class, void *data) static void pci_edu_register_types(void) { + static InterfaceInfo interfaces[] = { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }; static const TypeInfo edu_info = { .name = "edu", .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, + .interfaces = interfaces, }; type_register_static(&edu_info); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index b3ef3ec1e3..a5a46827fe 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -1010,6 +1010,10 @@ static const TypeInfo ivshmem_common_info = { .instance_size = sizeof(IVShmemState), .abstract = true, .class_init = ivshmem_common_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static const VMStateDescription ivshmem_plain_vmsd = { diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 9aa7e7559b..44f91d1e7f 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -426,6 +426,10 @@ static const TypeInfo macio_type_info = { .instance_init = macio_instance_init, .abstract = true, .class_init = macio_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void macio_register_types(void) diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 7d5990213e..32041f535f 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -326,6 +326,10 @@ static const TypeInfo pci_testdev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCITestDevState), .class_init = pci_testdev_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pci_testdev_register_types(void) diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c new file mode 100644 index 0000000000..a618e12677 --- /dev/null +++ b/hw/misc/vmcoreinfo.c @@ -0,0 +1,96 @@ +/* + * Virtual Machine coreinfo device + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Authors: Marc-AndrĂ© Lureau <marcandre.lureau@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 "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/nvram/fw_cfg.h" +#include "hw/misc/vmcoreinfo.h" + +static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len) +{ + VMCoreInfoState *s = VMCOREINFO(dev); + + s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo) + && s->vmcoreinfo.guest_format != VMCOREINFO_FORMAT_NONE; +} + +static void vmcoreinfo_reset(void *dev) +{ + VMCoreInfoState *s = VMCOREINFO(dev); + + s->has_vmcoreinfo = false; + memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo)); + s->vmcoreinfo.host_format = cpu_to_le16(VMCOREINFO_FORMAT_ELF); +} + +static void vmcoreinfo_realize(DeviceState *dev, Error **errp) +{ + VMCoreInfoState *s = VMCOREINFO(dev); + FWCfgState *fw_cfg = fw_cfg_find(); + + /* Given that this function is executing, there is at least one VMCOREINFO + * device. Check if there are several. + */ + if (!vmcoreinfo_find()) { + error_setg(errp, "at most one %s device is permitted", + VMCOREINFO_DEVICE); + return; + } + + if (!fw_cfg || !fw_cfg->dma_enabled) { + error_setg(errp, "%s device requires fw_cfg with DMA", + VMCOREINFO_DEVICE); + return; + } + + fw_cfg_add_file_callback(fw_cfg, "etc/vmcoreinfo", + NULL, fw_cfg_vmci_write, s, + &s->vmcoreinfo, sizeof(s->vmcoreinfo), false); + + qemu_register_reset(vmcoreinfo_reset, dev); +} + +static const VMStateDescription vmstate_vmcoreinfo = { + .name = "vmcoreinfo", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(has_vmcoreinfo, VMCoreInfoState), + VMSTATE_UINT16(vmcoreinfo.host_format, VMCoreInfoState), + VMSTATE_UINT16(vmcoreinfo.guest_format, VMCoreInfoState), + VMSTATE_UINT32(vmcoreinfo.size, VMCoreInfoState), + VMSTATE_UINT64(vmcoreinfo.paddr, VMCoreInfoState), + VMSTATE_END_OF_LIST() + }, +}; + +static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_vmcoreinfo; + dc->realize = vmcoreinfo_realize; + dc->hotpluggable = false; +} + +static const TypeInfo vmcoreinfo_device_info = { + .name = VMCOREINFO_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(VMCoreInfoState), + .class_init = vmcoreinfo_device_class_init, +}; + +static void vmcoreinfo_register_types(void) +{ + type_register_static(&vmcoreinfo_device_info); +} + +type_init(vmcoreinfo_register_types) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index cae95185a5..c0abee4f7e 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1687,6 +1687,10 @@ static const TypeInfo e1000_base_info = { .instance_init = e1000_instance_init, .class_size = sizeof(E1000BaseClass), .abstract = true, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static const E1000Info e1000_devices[] = { diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index 744f0f3b91..f1af279e8d 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -710,6 +710,10 @@ static const TypeInfo e1000e_info = { .instance_size = sizeof(E1000EState), .class_init = e1000e_class_init, .instance_init = e1000e_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }; static void e1000e_register_types(void) diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index a7b9f77519..80b8f47c4b 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -2116,6 +2116,10 @@ static void eepro100_register_types(void) type_info.class_init = eepro100_class_init; type_info.instance_size = sizeof(EEPRO100State); type_info.instance_init = eepro100_instance_init; + type_info.interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }; type_register(&type_info); } diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 798d681e25..3938e6ddd8 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -786,6 +786,10 @@ static const TypeInfo ne2000_info = { .instance_size = sizeof(PCINE2000State), .class_init = ne2000_class_init, .instance_init = ne2000_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void ne2000_register_types(void) diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 0acf8a4879..0ae5ca4701 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -365,6 +365,10 @@ static const TypeInfo pcnet_info = { .instance_size = sizeof(PCIPCNetState), .class_init = pcnet_class_init, .instance_init = pcnet_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pci_pcnet_register_types(void) diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 9273473d59..823a29df03 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -1525,6 +1525,10 @@ static const TypeInfo rocker_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(Rocker), .class_init = rocker_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void rocker_register_types(void) diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index d6c8188ae1..a6b2a9f7a4 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3444,6 +3444,10 @@ static const TypeInfo rtl8139_info = { .instance_size = sizeof(RTL8139State), .class_init = rtl8139_class_init, .instance_init = rtl8139_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void rtl8139_register_types(void) diff --git a/hw/net/sungem.c b/hw/net/sungem.c index dffa0c90f3..6aa8d1117b 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -1437,6 +1437,10 @@ static const TypeInfo sungem_info = { .instance_size = sizeof(SunGEMState), .class_init = sungem_class_init, .instance_init = sungem_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + } }; static void sungem_register_types(void) diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 60277adcf1..b1efa1b88d 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -968,6 +968,10 @@ static const TypeInfo sunhme_info = { .class_init = sunhme_class_init, .instance_size = sizeof(SunHMEState), .instance_init = sunhme_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + } }; static void sunhme_register_types(void) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index b43b58be2b..8c4bae5394 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2653,6 +2653,11 @@ static const TypeInfo vmxnet3_info = { .instance_size = sizeof(VMXNET3State), .class_init = vmxnet3_class_init, .instance_init = vmxnet3_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, }; static void vmxnet3_register_types(void) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index e3bd626b8c..753ac0e4ea 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -56,6 +56,7 @@ struct FWCfgEntry { uint8_t *data; void *callback_opaque; FWCfgCallback select_cb; + FWCfgWriteCallback write_cb; }; #define JPG_FILE 0 @@ -370,6 +371,8 @@ static void fw_cfg_dma_transfer(FWCfgState *s) dma_memory_read(s->dma_as, dma.address, &e->data[s->cur_offset], len)) { dma.control |= FW_CFG_DMA_CTL_ERROR; + } else if (e->write_cb) { + e->write_cb(e->callback_opaque, s->cur_offset, len); } } @@ -570,6 +573,7 @@ static const VMStateDescription vmstate_fw_cfg = { static void fw_cfg_add_bytes_callback(FWCfgState *s, uint16_t key, FWCfgCallback select_cb, + FWCfgWriteCallback write_cb, void *callback_opaque, void *data, size_t len, bool read_only) @@ -584,6 +588,7 @@ static void fw_cfg_add_bytes_callback(FWCfgState *s, uint16_t key, s->entries[arch][key].data = data; s->entries[arch][key].len = (uint32_t)len; s->entries[arch][key].select_cb = select_cb; + s->entries[arch][key].write_cb = write_cb; s->entries[arch][key].callback_opaque = callback_opaque; s->entries[arch][key].allow_write = !read_only; } @@ -610,7 +615,7 @@ static void *fw_cfg_modify_bytes_read(FWCfgState *s, uint16_t key, void fw_cfg_add_bytes(FWCfgState *s, uint16_t key, void *data, size_t len) { - fw_cfg_add_bytes_callback(s, key, NULL, NULL, data, len, true); + fw_cfg_add_bytes_callback(s, key, NULL, NULL, NULL, data, len, true); } void fw_cfg_add_string(FWCfgState *s, uint16_t key, const char *value) @@ -737,6 +742,7 @@ static int get_fw_cfg_order(FWCfgState *s, const char *name) void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgCallback select_cb, + FWCfgWriteCallback write_cb, void *callback_opaque, void *data, size_t len, bool read_only) { @@ -800,7 +806,7 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, } fw_cfg_add_bytes_callback(s, FW_CFG_FILE_FIRST + index, - select_cb, + select_cb, write_cb, callback_opaque, data, len, read_only); @@ -815,7 +821,7 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, size_t len) { - fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len, true); + fw_cfg_add_file_callback(s, filename, NULL, NULL, NULL, data, len, true); } void *fw_cfg_modify_file(FWCfgState *s, const char *filename, @@ -838,7 +844,7 @@ void *fw_cfg_modify_file(FWCfgState *s, const char *filename, } } /* add new one */ - fw_cfg_add_file_callback(s, filename, NULL, NULL, data, len, true); + fw_cfg_add_file_callback(s, filename, NULL, NULL, NULL, data, len, true); return NULL; } diff --git a/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c index eb275e1a25..84492d5e5f 100644 --- a/hw/pci-bridge/dec.c +++ b/hw/pci-bridge/dec.c @@ -79,6 +79,10 @@ static const TypeInfo dec_21154_pci_bridge_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(PCIBridge), .class_init = dec_21154_pci_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) @@ -138,6 +142,10 @@ static const TypeInfo dec_21154_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = dec_21154_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index ed03ffc764..ad4e6aa7ff 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -85,6 +85,13 @@ static void gen_rp_realize(DeviceState *dev, Error **errp) rpc->parent_class.exit(d); return; } + + if (!grp->io_reserve) { + pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND, + PCI_COMMAND_IO); + d->wmask[PCI_IO_BASE] = 0; + d->wmask[PCI_IO_LIMIT] = 0; + } } static const VMStateDescription vmstate_rp_dev = { diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index 2c1b747b4b..cb522bf30c 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -106,6 +106,10 @@ static const TypeInfo i82801b11_bridge_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(I82801b11Bridge), .class_init = i82801b11_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void d2pbr_register(void) diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index da4e5bdf04..5f56a2feb6 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -64,15 +64,13 @@ static uint8_t ioh3420_aer_vector(const PCIDevice *d) static int ioh3420_interrupts_init(PCIDevice *d, Error **errp) { int rc; - Error *local_err = NULL; rc = msi_init(d, IOH_EP_MSI_OFFSET, IOH_EP_MSI_NR_VECTOR, IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_64BIT, IOH_EP_MSI_SUPPORTED_FLAGS & PCI_MSI_FLAGS_MASKBIT, - &local_err); + errp); if (rc < 0) { assert(rc == -ENOTSUP); - error_propagate(errp, local_err); } return rc; diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 4373f1d3e2..d56f6638c2 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -238,6 +238,7 @@ static const TypeInfo pci_bridge_dev_info = { .instance_finalize = pci_bridge_dev_instance_finalize, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } }; diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index ff59abf208..8c8ac737ad 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -316,6 +316,10 @@ static const TypeInfo pxb_dev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PXBDev), .class_init = pxb_dev_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp) @@ -350,6 +354,10 @@ static const TypeInfo pxb_pcie_dev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PXBDev), .class_init = pxb_pcie_dev_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pxb_register_types(void) diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 9aa5cc3e45..a4d827c99d 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -65,10 +65,18 @@ static void pcie_pci_bridge_realize(PCIDevice *d, Error **errp) goto aer_error; } + Error *local_err = NULL; if (pcie_br->msi != ON_OFF_AUTO_OFF) { - rc = msi_init(d, 0, 1, true, true, errp); + rc = msi_init(d, 0, 1, true, true, &local_err); if (rc < 0) { - goto msi_error; + assert(rc == -ENOTSUP); + if (pcie_br->msi != ON_OFF_AUTO_ON) { + error_free(local_err); + } else { + /* failed to satisfy user's explicit request for MSI */ + error_propagate(errp, local_err); + goto msi_error; + } } } pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | @@ -81,7 +89,7 @@ aer_error: pm_error: pcie_cap_exit(d); cap_error: - shpc_free(d); + shpc_cleanup(d, &pcie_br->shpc_bar); error: pci_bridge_exitfn(d); } @@ -98,7 +106,9 @@ static void pcie_pci_bridge_reset(DeviceState *qdev) { PCIDevice *d = PCI_DEVICE(qdev); pci_bridge_reset(qdev); - msi_reset(d); + if (msi_present(d)) { + msi_reset(d); + } shpc_reset(d); } @@ -106,12 +116,14 @@ static void pcie_pci_bridge_write_config(PCIDevice *d, uint32_t address, uint32_t val, int len) { pci_bridge_write_config(d, address, val, len); - msi_write_config(d, address, val, len); + if (msi_present(d)) { + msi_write_config(d, address, val, len); + } shpc_cap_write_config(d, address, val, len); } static Property pcie_pci_bridge_dev_properties[] = { - DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_ON), + DEFINE_PROP_ON_OFF_AUTO("msi", PCIEPCIBridge, msi, ON_OFF_AUTO_AUTO), DEFINE_PROP_END_OF_LIST(), }; @@ -180,6 +192,7 @@ static const TypeInfo pcie_pci_bridge_info = { .class_init = pcie_pci_bridge_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, + { INTERFACE_PCIE_DEVICE }, { }, } }; diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 4d588cb22e..9b6e4ce512 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -161,6 +161,10 @@ static const TypeInfo rp_info = { .class_init = rp_class_init, .abstract = true, .class_size = sizeof(PCIERootPortClass), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }; static void rp_register_types(void) diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index e706f36cb7..1e09d2afb7 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -94,6 +94,7 @@ static void xio3130_downstream_realize(PCIDevice *d, Error **errp) pcie_chassis_create(s->chassis); rc = pcie_chassis_add_slot(s); if (rc < 0) { + error_setg(errp, "Can't add chassis slot, error %d", rc); goto err_pcie_cap; } @@ -195,6 +196,10 @@ static const TypeInfo xio3130_downstream_info = { .name = "xio3130-downstream", .parent = TYPE_PCIE_SLOT, .class_init = xio3130_downstream_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }; static void xio3130_downstream_register_types(void) diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index a052224bbf..227997ce46 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -166,6 +166,10 @@ static const TypeInfo xio3130_upstream_info = { .name = "x3130-upstream", .parent = TYPE_PCIE_PORT, .class_init = xio3130_upstream_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }; static void xio3130_upstream_register_types(void) diff --git a/hw/pci-host/apb.c b/hw/pci-host/apb.c index b709456b97..1edf57f600 100644 --- a/hw/pci-host/apb.c +++ b/hw/pci-host/apb.c @@ -836,6 +836,10 @@ static const TypeInfo pbm_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = pbm_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pbm_host_class_init(ObjectClass *klass, void *data) @@ -883,6 +887,10 @@ static const TypeInfo pbm_pci_bridge_info = { .parent = TYPE_PCI_BRIDGE, .class_init = pbm_pci_bridge_class_init, .instance_size = sizeof(PBMPCIBridge), + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pbm_iommu_memory_region_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 89133a9dd3..9f61e27edc 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -833,6 +833,10 @@ static const TypeInfo bonito_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBonitoState), .class_init = bonito_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void bonito_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index be25245219..4090793cf0 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -166,6 +166,10 @@ static const TypeInfo gpex_root_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(GPEXRootState), .class_init = gpex_root_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void gpex_register(void) diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 2e281f6155..38cd279b6b 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -142,6 +142,10 @@ static const TypeInfo grackle_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = grackle_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pci_grackle_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index dec345fd24..a7e2256870 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -696,6 +696,10 @@ static const TypeInfo piix3_pci_type_info = { .instance_size = sizeof(PIIX3State), .abstract = true, .class_init = pci_piix3_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void piix3_class_init(ObjectClass *klass, void *data) @@ -750,6 +754,10 @@ static const TypeInfo i440fx_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCII440FXState), .class_init = i440fx_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; /* IGD Passthrough Host Bridge. */ diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index becc0eeb76..39cd24464d 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -516,6 +516,10 @@ static const TypeInfo e500_host_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PPCE500PCIBridgeState), .class_init = e500_host_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static Property pcihost_properties[] = { diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 8b293ba0f1..92eed0f3e1 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -372,6 +372,10 @@ static const TypeInfo raven_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RavenPCIState), .class_init = raven_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static Property raven_pcihost_properties[] = { diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 1ff648e80c..ddaa7d1b44 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -591,6 +591,10 @@ static const TypeInfo mch_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MCHPCIState), .class_init = mch_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void q35_register(void) diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 6cf5e59f86..ea5c265718 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -374,6 +374,10 @@ static const TypeInfo unin_main_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_main_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) @@ -398,6 +402,10 @@ static const TypeInfo u3_agp_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = u3_agp_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) @@ -422,6 +430,10 @@ static const TypeInfo unin_agp_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_agp_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) @@ -446,6 +458,10 @@ static const TypeInfo unin_internal_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_internal_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void pci_unin_main_class_init(ObjectClass *klass, void *data) diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index aa1fdf75fd..6394a520fc 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -487,6 +487,10 @@ static const TypeInfo versatile_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = versatile_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static Property pci_vpb_properties[] = { diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 4613dda1d2..7659253090 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -317,6 +317,10 @@ static const TypeInfo xilinx_pcie_root_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(XilinxPCIERoot), .class_init = xilinx_pcie_root_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, }; static void xilinx_pcie_register(void) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1e6fb88eba..5ed3c8dca4 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -170,6 +170,16 @@ static const TypeInfo pci_bus_info = { .class_init = pci_bus_class_init, }; +static const TypeInfo pcie_interface_info = { + .name = INTERFACE_PCIE_DEVICE, + .parent = TYPE_INTERFACE, +}; + +static const TypeInfo conventional_pci_interface_info = { + .name = INTERFACE_CONVENTIONAL_PCI_DEVICE, + .parent = TYPE_INTERFACE, +}; + static const TypeInfo pcie_bus_info = { .name = TYPE_PCIE_BUS, .parent = TYPE_PCI_BUS, @@ -2537,6 +2547,17 @@ static void pci_device_class_init(ObjectClass *klass, void *data) pc->realize = pci_default_realize; } +static void pci_device_class_base_init(ObjectClass *klass, void *data) +{ + if (!object_class_is_abstract(klass)) { + ObjectClass *conventional = + object_class_dynamic_cast(klass, INTERFACE_CONVENTIONAL_PCI_DEVICE); + ObjectClass *pcie = + object_class_dynamic_cast(klass, INTERFACE_PCIE_DEVICE); + assert(conventional || pcie); + } +} + AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) { PCIBus *bus = PCI_BUS(dev->bus); @@ -2661,12 +2682,15 @@ static const TypeInfo pci_device_type_info = { .abstract = true, .class_size = sizeof(PCIDeviceClass), .class_init = pci_device_class_init, + .class_base_init = pci_device_class_base_init, }; static void pci_register_types(void) { type_register_static(&pci_bus_info); type_register_static(&pcie_bus_info); + type_register_static(&conventional_pci_interface_info); + type_register_static(&pcie_interface_info); type_register_static(&pci_device_type_info); } diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 17feae5ed8..a47d257149 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -379,7 +379,8 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename) sec_bus->address_space_mem = &br->address_space_mem; memory_region_init(&br->address_space_mem, OBJECT(br), "pci_bridge_pci", UINT64_MAX); sec_bus->address_space_io = &br->address_space_io; - memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", 65536); + memory_region_init(&br->address_space_io, OBJECT(br), "pci_bridge_io", + UINT32_MAX); br->windows = pci_bridge_region_init(br); QLIST_INIT(&sec_bus->child); QLIST_INSERT_HEAD(&parent->child, sec_bus, sibling); diff --git a/hw/pci/pcie_host.c b/hw/pci/pcie_host.c index dcebf57ed4..553db56778 100644 --- a/hw/pci/pcie_host.c +++ b/hw/pci/pcie_host.c @@ -81,7 +81,7 @@ static uint64_t pcie_mmcfg_data_read(void *opaque, static const MemoryRegionOps pcie_mmcfg_ops = { .read = pcie_mmcfg_data_read, .write = pcie_mmcfg_data_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static void pcie_host_init(Object *obj) diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index 6953f8b9ac..4765dcecca 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -359,6 +359,10 @@ static const TypeInfo ppc4xx_host_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = ppc4xx_host_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index e295d88939..419fc668ac 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -398,6 +398,10 @@ static const TypeInfo esp_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIESPState), .class_init = esp_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; typedef struct { diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index a67ee074d9..191505df5b 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -2246,6 +2246,10 @@ static const TypeInfo lsi_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(LSIState), .class_init = lsi_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void lsi53c810_class_init(ObjectClass *klass, void *data) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 0db68aacee..d5eae6239a 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2451,6 +2451,7 @@ typedef struct MegasasInfo { int osts; const VMStateDescription *vmsd; Property *props; + InterfaceInfo *interfaces; } MegasasInfo; static struct MegasasInfo megasas_devices[] = { @@ -2467,6 +2468,10 @@ static struct MegasasInfo megasas_devices[] = { .is_express = false, .vmsd = &vmstate_megasas_gen1, .props = megasas_properties_gen1, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, },{ .name = TYPE_MEGASAS_GEN2, .desc = "LSI MegaRAID SAS 2108", @@ -2480,6 +2485,10 @@ static struct MegasasInfo megasas_devices[] = { .is_express = true, .vmsd = &vmstate_megasas_gen2, .props = megasas_properties_gen2, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { } + }, } }; @@ -2531,6 +2540,7 @@ static void megasas_register_types(void) type_info.parent = TYPE_MEGASAS_BASE; type_info.class_data = (void *)info; type_info.class_init = megasas_class_init; + type_info.interfaces = info->interfaces; type_register(&type_info); } diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index d05fa9f549..f6db1b0103 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1439,6 +1439,10 @@ static const TypeInfo mptsas_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MPTSASState), .class_init = mptsas1068_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void mptsas_register_types(void) diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index d8079158ac..d564e5caff 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1302,6 +1302,8 @@ static const TypeInfo pvscsi_info = { .class_init = pvscsi_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } }; diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 6d6a791ee9..b064a087c9 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1315,6 +1315,10 @@ static const TypeInfo sdhci_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(SDHCIState), .class_init = sdhci_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static Property sdhci_sysbus_properties[] = { diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c index 38395c082b..cbb01af57f 100644 --- a/hw/sh4/sh_pci.c +++ b/hw/sh4/sh_pci.c @@ -179,6 +179,10 @@ static const TypeInfo sh_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = sh_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void sh_pci_device_class_init(ObjectClass *klass, void *data) diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index c3280aaf38..8282651aeb 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -275,6 +275,10 @@ static const TypeInfo ebus_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EbusState), .class_init = ebus_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; #define TYPE_OPENPROM "openprom" diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 64cecc3b67..41f0b7a590 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,2 +1,3 @@ common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o tpm_util.o +common-obj-$(CONFIG_TPM_EMULATOR) += tpm_emulator.o tpm_util.o diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c new file mode 100644 index 0000000000..95e1e041cf --- /dev/null +++ b/hw/tpm/tpm_emulator.c @@ -0,0 +1,587 @@ +/* + * Emulator TPM driver + * + * Copyright (c) 2017 Intel Corporation + * Author: Amarnath Valluri <amarnath.valluri@intel.com> + * + * Copyright (c) 2010 - 2013 IBM Corporation + * Authors: + * Stefan Berger <stefanb@us.ibm.com> + * + * Copyright (C) 2011 IAIK, Graz University of Technology + * Author: Andreas Niederl + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/> + * + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/sockets.h" +#include "io/channel-socket.h" +#include "sysemu/tpm_backend.h" +#include "tpm_int.h" +#include "hw/hw.h" +#include "hw/i386/pc.h" +#include "tpm_util.h" +#include "tpm_ioctl.h" +#include "migration/blocker.h" +#include "qapi/error.h" +#include "qapi/clone-visitor.h" +#include "chardev/char-fe.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> + +#define DEBUG_TPM 0 + +#define DPRINTF(fmt, ...) do { \ + if (DEBUG_TPM) { \ + fprintf(stderr, "tpm-emulator:"fmt"\n", ## __VA_ARGS__); \ + } \ +} while (0) + +#define TYPE_TPM_EMULATOR "tpm-emulator" +#define TPM_EMULATOR(obj) \ + OBJECT_CHECK(TPMEmulator, (obj), TYPE_TPM_EMULATOR) + +#define TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(S, cap) (((S)->caps & (cap)) == (cap)) + +static const TPMDriverOps tpm_emulator_driver; + +/* data structures */ +typedef struct TPMEmulator { + TPMBackend parent; + + TPMEmulatorOptions *options; + CharBackend ctrl_chr; + QIOChannel *data_ioc; + TPMVersion tpm_version; + ptm_cap caps; /* capabilities of the TPM */ + uint8_t cur_locty_number; /* last set locality */ + Error *migration_blocker; +} TPMEmulator; + + +static int tpm_emulator_ctrlcmd(CharBackend *dev, unsigned long cmd, void *msg, + size_t msg_len_in, size_t msg_len_out) +{ + uint32_t cmd_no = cpu_to_be32(cmd); + ssize_t n = sizeof(uint32_t) + msg_len_in; + uint8_t *buf = NULL; + + buf = g_alloca(n); + memcpy(buf, &cmd_no, sizeof(cmd_no)); + memcpy(buf + sizeof(cmd_no), msg, msg_len_in); + + n = qemu_chr_fe_write_all(dev, buf, n); + if (n <= 0) { + return -1; + } + + if (msg_len_out != 0) { + n = qemu_chr_fe_read_all(dev, msg, msg_len_out); + if (n <= 0) { + return -1; + } + } + + return 0; +} + +static int tpm_emulator_unix_tx_bufs(TPMEmulator *tpm_emu, + const uint8_t *in, uint32_t in_len, + uint8_t *out, uint32_t out_len, + bool *selftest_done, + Error **err) +{ + ssize_t ret; + bool is_selftest = false; + const struct tpm_resp_hdr *hdr = NULL; + + if (selftest_done) { + *selftest_done = false; + is_selftest = tpm_util_is_selftest(in, in_len); + } + + ret = qio_channel_write_all(tpm_emu->data_ioc, (char *)in, in_len, err); + if (ret != 0) { + return -1; + } + + ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, sizeof(*hdr), + err); + if (ret != 0) { + return -1; + } + + hdr = (struct tpm_resp_hdr *)out; + out += sizeof(*hdr); + ret = qio_channel_read_all(tpm_emu->data_ioc, (char *)out, + be32_to_cpu(hdr->len) - sizeof(*hdr) , err); + if (ret != 0) { + return -1; + } + + if (is_selftest) { + *selftest_done = (be32_to_cpu(hdr->errcode) == 0); + } + + return 0; +} + +static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number) +{ + ptm_loc loc; + + DPRINTF("%s : locality: 0x%x", __func__, locty_number); + + if (tpm_emu->cur_locty_number == locty_number) { + return 0; + } + + DPRINTF("setting locality : 0x%x", locty_number); + loc.u.req.loc = locty_number; + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_LOCALITY, &loc, + sizeof(loc), sizeof(loc)) < 0) { + error_report("tpm-emulator: could not set locality : %s", + strerror(errno)); + return -1; + } + + loc.u.resp.tpm_result = be32_to_cpu(loc.u.resp.tpm_result); + if (loc.u.resp.tpm_result != 0) { + error_report("tpm-emulator: TPM result for set locality : 0x%x", + loc.u.resp.tpm_result); + return -1; + } + + tpm_emu->cur_locty_number = locty_number; + + return 0; +} + +static void tpm_emulator_handle_request(TPMBackend *tb, TPMBackendCmd cmd) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TPMLocality *locty = NULL; + bool selftest_done = false; + Error *err = NULL; + + DPRINTF("processing command type %d", cmd); + + switch (cmd) { + case TPM_BACKEND_CMD_PROCESS_CMD: + locty = tb->tpm_state->locty_data; + if (tpm_emulator_set_locality(tpm_emu, + tb->tpm_state->locty_number) < 0 || + tpm_emulator_unix_tx_bufs(tpm_emu, locty->w_buffer.buffer, + locty->w_offset, locty->r_buffer.buffer, + locty->r_buffer.size, &selftest_done, + &err) < 0) { + tpm_util_write_fatal_error_response(locty->r_buffer.buffer, + locty->r_buffer.size); + error_report_err(err); + } + + tb->recv_data_callback(tb->tpm_state, tb->tpm_state->locty_number, + selftest_done); + + break; + case TPM_BACKEND_CMD_INIT: + case TPM_BACKEND_CMD_END: + case TPM_BACKEND_CMD_TPM_RESET: + /* nothing to do */ + break; + } +} + +static int tpm_emulator_probe_caps(TPMEmulator *tpm_emu) +{ + DPRINTF("%s", __func__); + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_CAPABILITY, + &tpm_emu->caps, 0, sizeof(tpm_emu->caps)) < 0) { + error_report("tpm-emulator: probing failed : %s", strerror(errno)); + return -1; + } + + tpm_emu->caps = be64_to_cpu(tpm_emu->caps); + + DPRINTF("capabilities : 0x%"PRIx64, tpm_emu->caps); + + return 0; +} + +static int tpm_emulator_check_caps(TPMEmulator *tpm_emu) +{ + ptm_cap caps = 0; + const char *tpm = NULL; + + /* check for min. required capabilities */ + switch (tpm_emu->tpm_version) { + case TPM_VERSION_1_2: + caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | + PTM_CAP_SET_LOCALITY | PTM_CAP_SET_DATAFD; + tpm = "1.2"; + break; + case TPM_VERSION_2_0: + caps = PTM_CAP_INIT | PTM_CAP_SHUTDOWN | PTM_CAP_GET_TPMESTABLISHED | + PTM_CAP_SET_LOCALITY | PTM_CAP_RESET_TPMESTABLISHED | + PTM_CAP_SET_DATAFD; + tpm = "2"; + break; + case TPM_VERSION_UNSPEC: + error_report("tpm-emulator: TPM version has not been set"); + return -1; + } + + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, caps)) { + error_report("tpm-emulator: TPM does not implement minimum set of " + "required capabilities for TPM %s (0x%x)", tpm, (int)caps); + return -1; + } + + return 0; +} + +static int tpm_emulator_startup_tpm(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_init init; + ptm_res res; + + DPRINTF("%s", __func__); + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_INIT, &init, sizeof(init), + sizeof(init)) < 0) { + error_report("tpm-emulator: could not send INIT: %s", + strerror(errno)); + goto err_exit; + } + + res = be32_to_cpu(init.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for CMD_INIT: 0x%x", res); + goto err_exit; + } + return 0; + +err_exit: + return -1; +} + +static bool tpm_emulator_get_tpm_established_flag(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_est est; + + DPRINTF("%s", __func__); + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_GET_TPMESTABLISHED, &est, + 0, sizeof(est)) < 0) { + error_report("tpm-emulator: Could not get the TPM established flag: %s", + strerror(errno)); + return false; + } + DPRINTF("established flag: %0x", est.u.resp.bit); + + return (est.u.resp.bit != 0); +} + +static int tpm_emulator_reset_tpm_established_flag(TPMBackend *tb, + uint8_t locty) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_reset_est reset_est; + ptm_res res; + + /* only a TPM 2.0 will support this */ + if (tpm_emu->tpm_version != TPM_VERSION_2_0) { + return 0; + } + + reset_est.u.req.loc = tpm_emu->cur_locty_number; + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_RESET_TPMESTABLISHED, + &reset_est, sizeof(reset_est), + sizeof(reset_est)) < 0) { + error_report("tpm-emulator: Could not reset the establishment bit: %s", + strerror(errno)); + return -1; + } + + res = be32_to_cpu(reset_est.u.resp.tpm_result); + if (res) { + error_report("tpm-emulator: TPM result for rest establixhed flag: 0x%x", + res); + return -1; + } + + return 0; +} + +static void tpm_emulator_cancel_cmd(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + ptm_res res; + + if (!TPM_EMULATOR_IMPLEMENTS_ALL_CAPS(tpm_emu, PTM_CAP_CANCEL_TPM_CMD)) { + DPRINTF("Backend does not support CANCEL_TPM_CMD"); + return; + } + + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_CANCEL_TPM_CMD, &res, 0, + sizeof(res)) < 0) { + error_report("tpm-emulator: Could not cancel command: %s", + strerror(errno)); + } else if (res != 0) { + error_report("tpm-emulator: Failed to cancel TPM: 0x%x", + be32_to_cpu(res)); + } +} + +static TPMVersion tpm_emulator_get_tpm_version(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + + return tpm_emu->tpm_version; +} + +static int tpm_emulator_block_migration(TPMEmulator *tpm_emu) +{ + Error *err = NULL; + + error_setg(&tpm_emu->migration_blocker, + "Migration disabled: TPM emulator not yet migratable"); + migrate_add_blocker(tpm_emu->migration_blocker, &err); + if (err) { + error_report_err(err); + error_free(tpm_emu->migration_blocker); + tpm_emu->migration_blocker = NULL; + + return -1; + } + + return 0; +} + +static int tpm_emulator_prepare_data_fd(TPMEmulator *tpm_emu) +{ + ptm_res res; + Error *err = NULL; + int fds[2] = { -1, -1 }; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) { + error_report("tpm-emulator: Failed to create socketpair"); + return -1; + } + + qemu_chr_fe_set_msgfds(&tpm_emu->ctrl_chr, fds + 1, 1); + + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SET_DATAFD, &res, 0, + sizeof(res)) || res != 0) { + error_report("tpm-emulator: Failed to send CMD_SET_DATAFD: %s", + strerror(errno)); + goto err_exit; + } + + tpm_emu->data_ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fds[0], &err)); + if (err) { + error_prepend(&err, "tpm-emulator: Failed to create io channel: "); + error_report_err(err); + goto err_exit; + } + + closesocket(fds[1]); + + return 0; + +err_exit: + closesocket(fds[0]); + closesocket(fds[1]); + return -1; +} + +static int tpm_emulator_handle_device_opts(TPMEmulator *tpm_emu, QemuOpts *opts) +{ + const char *value; + + value = qemu_opt_get(opts, "chardev"); + if (value) { + Error *err = NULL; + Chardev *dev = qemu_chr_find(value); + + if (!dev) { + error_report("tpm-emulator: tpm chardev '%s' not found.", value); + goto err; + } + + if (!qemu_chr_fe_init(&tpm_emu->ctrl_chr, dev, &err)) { + error_prepend(&err, "tpm-emulator: No valid chardev found at '%s':", + value); + error_report_err(err); + goto err; + } + + tpm_emu->options->chardev = g_strdup(value); + } + + if (tpm_emulator_prepare_data_fd(tpm_emu) < 0) { + goto err; + } + + /* FIXME: tpm_util_test_tpmdev() accepts only on socket fd, as it also used + * by passthrough driver, which not yet using GIOChannel. + */ + if (tpm_util_test_tpmdev(QIO_CHANNEL_SOCKET(tpm_emu->data_ioc)->fd, + &tpm_emu->tpm_version)) { + error_report("'%s' is not emulating TPM device. Error: %s", + tpm_emu->options->chardev, strerror(errno)); + goto err; + } + + DPRINTF("TPM Version %s", tpm_emu->tpm_version == TPM_VERSION_1_2 ? "1.2" : + (tpm_emu->tpm_version == TPM_VERSION_2_0 ? "2.0" : "Unspecified")); + + if (tpm_emulator_probe_caps(tpm_emu) || + tpm_emulator_check_caps(tpm_emu)) { + goto err; + } + + return tpm_emulator_block_migration(tpm_emu); + +err: + DPRINTF("Startup error"); + return -1; +} + +static TPMBackend *tpm_emulator_create(QemuOpts *opts, const char *id) +{ + TPMBackend *tb = TPM_BACKEND(object_new(TYPE_TPM_EMULATOR)); + + tb->id = g_strdup(id); + + if (tpm_emulator_handle_device_opts(TPM_EMULATOR(tb), opts)) { + goto err_exit; + } + + return tb; + +err_exit: + object_unref(OBJECT(tb)); + + return NULL; +} + +static TpmTypeOptions *tpm_emulator_get_tpm_options(TPMBackend *tb) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(tb); + TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); + + options->type = TPM_TYPE_OPTIONS_KIND_EMULATOR; + options->u.emulator.data = QAPI_CLONE(TPMEmulatorOptions, tpm_emu->options); + + return options; +} + +static const QemuOptDesc tpm_emulator_cmdline_opts[] = { + TPM_STANDARD_CMDLINE_OPTS, + { + .name = "chardev", + .type = QEMU_OPT_STRING, + .help = "Character device to use for out-of-band control messages", + }, + { /* end of list */ }, +}; + +static const TPMDriverOps tpm_emulator_driver = { + .type = TPM_TYPE_EMULATOR, + .opts = tpm_emulator_cmdline_opts, + .desc = "TPM emulator backend driver", + + .create = tpm_emulator_create, + .startup_tpm = tpm_emulator_startup_tpm, + .cancel_cmd = tpm_emulator_cancel_cmd, + .get_tpm_established_flag = tpm_emulator_get_tpm_established_flag, + .reset_tpm_established_flag = tpm_emulator_reset_tpm_established_flag, + .get_tpm_version = tpm_emulator_get_tpm_version, + .get_tpm_options = tpm_emulator_get_tpm_options, +}; + +static void tpm_emulator_inst_init(Object *obj) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + + DPRINTF("%s", __func__); + tpm_emu->options = g_new0(TPMEmulatorOptions, 1); + tpm_emu->cur_locty_number = ~0; +} + +/* + * Gracefully shut down the external TPM + */ +static void tpm_emulator_shutdown(TPMEmulator *tpm_emu) +{ + ptm_res res; + + if (tpm_emulator_ctrlcmd(&tpm_emu->ctrl_chr, CMD_SHUTDOWN, &res, 0, + sizeof(res)) < 0) { + error_report("tpm-emulator: Could not cleanly shutdown the TPM: %s", + strerror(errno)); + } else if (res != 0) { + error_report("tpm-emulator: TPM result for sutdown: 0x%x", + be32_to_cpu(res)); + } +} + +static void tpm_emulator_inst_finalize(Object *obj) +{ + TPMEmulator *tpm_emu = TPM_EMULATOR(obj); + + tpm_emulator_shutdown(tpm_emu); + + object_unref(OBJECT(tpm_emu->data_ioc)); + + qemu_chr_fe_deinit(&tpm_emu->ctrl_chr, false); + + qapi_free_TPMEmulatorOptions(tpm_emu->options); + + if (tpm_emu->migration_blocker) { + migrate_del_blocker(tpm_emu->migration_blocker); + error_free(tpm_emu->migration_blocker); + } +} + +static void tpm_emulator_class_init(ObjectClass *klass, void *data) +{ + TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); + tbc->ops = &tpm_emulator_driver; + tbc->handle_request = tpm_emulator_handle_request; +} + +static const TypeInfo tpm_emulator_info = { + .name = TYPE_TPM_EMULATOR, + .parent = TYPE_TPM_BACKEND, + .instance_size = sizeof(TPMEmulator), + .class_init = tpm_emulator_class_init, + .instance_init = tpm_emulator_inst_init, + .instance_finalize = tpm_emulator_inst_finalize, +}; + +static void tpm_emulator_register(void) +{ + type_register_static(&tpm_emulator_info); + tpm_register_driver(&tpm_emulator_driver); +} + +type_init(tpm_emulator_register) diff --git a/hw/tpm/tpm_ioctl.h b/hw/tpm/tpm_ioctl.h new file mode 100644 index 0000000000..33564b11de --- /dev/null +++ b/hw/tpm/tpm_ioctl.h @@ -0,0 +1,246 @@ +/* + * tpm_ioctl.h + * + * (c) Copyright IBM Corporation 2014, 2015. + * + * This file is licensed under the terms of the 3-clause BSD license + */ +#ifndef _TPM_IOCTL_H_ +#define _TPM_IOCTL_H_ + +#include <stdint.h> +#include <sys/uio.h> +#include <sys/types.h> +#include <sys/ioctl.h> + +/* + * Every response from a command involving a TPM command execution must hold + * the ptm_res as the first element. + * ptm_res corresponds to the error code of a command executed by the TPM. + */ + +typedef uint32_t ptm_res; + +/* PTM_GET_TPMESTABLISHED: get the establishment bit */ +struct ptm_est { + union { + struct { + ptm_res tpm_result; + unsigned char bit; /* TPM established bit */ + } resp; /* response */ + } u; +}; + +/* PTM_RESET_TPMESTABLISHED: reset establishment bit */ +struct ptm_reset_est { + union { + struct { + uint8_t loc; /* locality to use */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_INIT */ +struct ptm_init { + union { + struct { + uint32_t init_flags; /* see definitions below */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* above init_flags */ +#define PTM_INIT_FLAG_DELETE_VOLATILE (1 << 0) + /* delete volatile state file after reading it */ + +/* PTM_SET_LOCALITY */ +struct ptm_loc { + union { + struct { + uint8_t loc; /* locality to set */ + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* PTM_HASH_DATA: hash given data */ +struct ptm_hdata { + union { + struct { + uint32_t length; + uint8_t data[4096]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * size of the TPM state blob to transfer; x86_64 can handle 8k, + * ppc64le only ~7k; keep the response below a 4k page size + */ +#define PTM_STATE_BLOB_SIZE (3 * 1024) + +/* + * The following is the data structure to get state blobs from the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple reads + * with this ioctl and with adjusted offset are necessary. All bytes + * must be transferred and the transfer is done once the last byte has been + * returned. + * It is possible to use the read() interface for reading the data; however, the + * first bytes of the state blob will be part of the response to the ioctl(); a + * subsequent read() is only necessary if the total length (totlength) exceeds + * the number of received bytes. seek() is not supported. + */ +struct ptm_getstate { + union { + struct { + uint32_t state_flags; /* may be: PTM_STATE_FLAG_DECRYPTED */ + uint32_t type; /* which blob to pull */ + uint32_t offset; /* offset from where to read */ + } req; /* request */ + struct { + ptm_res tpm_result; + uint32_t state_flags; /* may be: PTM_STATE_FLAG_ENCRYPTED */ + uint32_t totlength; /* total length that will be transferred */ + uint32_t length; /* number of bytes in following buffer */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } resp; /* response */ + } u; +}; + +/* TPM state blob types */ +#define PTM_BLOB_TYPE_PERMANENT 1 +#define PTM_BLOB_TYPE_VOLATILE 2 +#define PTM_BLOB_TYPE_SAVESTATE 3 + +/* state_flags above : */ +#define PTM_STATE_FLAG_DECRYPTED 1 /* on input: get decrypted state */ +#define PTM_STATE_FLAG_ENCRYPTED 2 /* on output: state is encrypted */ + +/* + * The following is the data structure to set state blobs in the TPM. + * If the size of the state blob exceeds the PTM_STATE_BLOB_SIZE, multiple + * 'writes' using this ioctl are necessary. The last packet is indicated + * by the length being smaller than the PTM_STATE_BLOB_SIZE. + * The very first packet may have a length indicator of '0' enabling + * a write() with all the bytes from a buffer. If the write() interface + * is used, a final ioctl with a non-full buffer must be made to indicate + * that all data were transferred (a write with 0 bytes would not work). + */ +struct ptm_setstate { + union { + struct { + uint32_t state_flags; /* may be PTM_STATE_FLAG_ENCRYPTED */ + uint32_t type; /* which blob to set */ + uint32_t length; /* length of the data; + use 0 on the first packet to + transfer using write() */ + uint8_t data[PTM_STATE_BLOB_SIZE]; + } req; /* request */ + struct { + ptm_res tpm_result; + } resp; /* response */ + } u; +}; + +/* + * PTM_GET_CONFIG: Data structure to get runtime configuration information + * such as which keys are applied. + */ +struct ptm_getconfig { + union { + struct { + ptm_res tpm_result; + uint32_t flags; + } resp; /* response */ + } u; +}; + +#define PTM_CONFIG_FLAG_FILE_KEY 0x1 +#define PTM_CONFIG_FLAG_MIGRATION_KEY 0x2 + + +typedef uint64_t ptm_cap; +typedef struct ptm_est ptm_est; +typedef struct ptm_reset_est ptm_reset_est; +typedef struct ptm_loc ptm_loc; +typedef struct ptm_hdata ptm_hdata; +typedef struct ptm_init ptm_init; +typedef struct ptm_getstate ptm_getstate; +typedef struct ptm_setstate ptm_setstate; +typedef struct ptm_getconfig ptm_getconfig; + +/* capability flags returned by PTM_GET_CAPABILITY */ +#define PTM_CAP_INIT (1) +#define PTM_CAP_SHUTDOWN (1 << 1) +#define PTM_CAP_GET_TPMESTABLISHED (1 << 2) +#define PTM_CAP_SET_LOCALITY (1 << 3) +#define PTM_CAP_HASHING (1 << 4) +#define PTM_CAP_CANCEL_TPM_CMD (1 << 5) +#define PTM_CAP_STORE_VOLATILE (1 << 6) +#define PTM_CAP_RESET_TPMESTABLISHED (1 << 7) +#define PTM_CAP_GET_STATEBLOB (1 << 8) +#define PTM_CAP_SET_STATEBLOB (1 << 9) +#define PTM_CAP_STOP (1 << 10) +#define PTM_CAP_GET_CONFIG (1 << 11) +#define PTM_CAP_SET_DATAFD (1 << 12) + +enum { + PTM_GET_CAPABILITY = _IOR('P', 0, ptm_cap), + PTM_INIT = _IOWR('P', 1, ptm_init), + PTM_SHUTDOWN = _IOR('P', 2, ptm_res), + PTM_GET_TPMESTABLISHED = _IOR('P', 3, ptm_est), + PTM_SET_LOCALITY = _IOWR('P', 4, ptm_loc), + PTM_HASH_START = _IOR('P', 5, ptm_res), + PTM_HASH_DATA = _IOWR('P', 6, ptm_hdata), + PTM_HASH_END = _IOR('P', 7, ptm_res), + PTM_CANCEL_TPM_CMD = _IOR('P', 8, ptm_res), + PTM_STORE_VOLATILE = _IOR('P', 9, ptm_res), + PTM_RESET_TPMESTABLISHED = _IOWR('P', 10, ptm_reset_est), + PTM_GET_STATEBLOB = _IOWR('P', 11, ptm_getstate), + PTM_SET_STATEBLOB = _IOWR('P', 12, ptm_setstate), + PTM_STOP = _IOR('P', 13, ptm_res), + PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig), + PTM_SET_DATAFD = _IOR('P', 15, ptm_res), +}; + +/* + * Commands used by the non-CUSE TPMs + * + * All messages container big-endian data. + * + * The return messages only contain the 'resp' part of the unions + * in the data structures above. Besides that the limits in the + * buffers above (ptm_hdata:u.req.data and ptm_get_state:u.resp.data + * and ptm_set_state:u.req.data) are 0xffffffff. + */ +enum { + CMD_GET_CAPABILITY = 1, + CMD_INIT, + CMD_SHUTDOWN, + CMD_GET_TPMESTABLISHED, + CMD_SET_LOCALITY, + CMD_HASH_START, + CMD_HASH_DATA, + CMD_HASH_END, + CMD_CANCEL_TPM_CMD, + CMD_STORE_VOLATILE, + CMD_RESET_TPMESTABLISHED, + CMD_GET_STATEBLOB, + CMD_SET_STATEBLOB, + CMD_STOP, + CMD_GET_CONFIG, + CMD_SET_DATAFD +}; + +#endif /* _TPM_IOCTL_H */ diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c index 9234eb3459..e6ace28b04 100644 --- a/hw/tpm/tpm_passthrough.c +++ b/hw/tpm/tpm_passthrough.c @@ -30,7 +30,7 @@ #include "tpm_int.h" #include "hw/hw.h" #include "hw/i386/pc.h" -#include "sysemu/tpm_backend_int.h" +#include "qapi/clone-visitor.h" #include "tpm_tis.h" #include "tpm_util.h" @@ -46,29 +46,16 @@ #define TPM_PASSTHROUGH(obj) \ OBJECT_CHECK(TPMPassthruState, (obj), TYPE_TPM_PASSTHROUGH) -static const TPMDriverOps tpm_passthrough_driver; - /* data structures */ -typedef struct TPMPassthruThreadParams { - TPMState *tpm_state; - - TPMRecvDataCB *recv_data_callback; - TPMBackend *tb; -} TPMPassthruThreadParams; - struct TPMPassthruState { TPMBackend parent; - TPMBackendThread tbt; - - TPMPassthruThreadParams tpm_thread_params; - - char *tpm_dev; + TPMPassthroughOptions *options; + const char *tpm_dev; int tpm_fd; bool tpm_executing; bool tpm_op_canceled; int cancel_fd; - bool had_startup_error; TPMVersion tpm_version; }; @@ -81,27 +68,6 @@ typedef struct TPMPassthruState TPMPassthruState; static void tpm_passthrough_cancel_cmd(TPMBackend *tb); -static int tpm_passthrough_unix_write(int fd, const uint8_t *buf, uint32_t len) -{ - int ret, remain; - - remain = len; - while (remain > 0) { - ret = write(fd, buf, remain); - if (ret < 0) { - if (errno != EINTR && errno != EAGAIN) { - return -1; - } - } else if (ret == 0) { - break; - } else { - buf += ret; - remain -= ret; - } - } - return len - remain; -} - static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) { int ret; @@ -115,45 +81,12 @@ static int tpm_passthrough_unix_read(int fd, uint8_t *buf, uint32_t len) } return ret; } - -static uint32_t tpm_passthrough_get_size_from_buffer(const uint8_t *buf) -{ - struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)buf; - - return be32_to_cpu(resp->len); -} - -/* - * Write an error message in the given output buffer. - */ -static void tpm_write_fatal_error_response(uint8_t *out, uint32_t out_len) -{ - if (out_len >= sizeof(struct tpm_resp_hdr)) { - struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; - - resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); - resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); - resp->errcode = cpu_to_be32(TPM_FAIL); - } -} - -static bool tpm_passthrough_is_selftest(const uint8_t *in, uint32_t in_len) -{ - struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; - - if (in_len >= sizeof(*hdr)) { - return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); - } - - return false; -} - static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, const uint8_t *in, uint32_t in_len, uint8_t *out, uint32_t out_len, bool *selftest_done) { - int ret; + ssize_t ret; bool is_selftest; const struct tpm_resp_hdr *hdr; @@ -161,9 +94,9 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, tpm_pt->tpm_executing = true; *selftest_done = false; - is_selftest = tpm_passthrough_is_selftest(in, in_len); + is_selftest = tpm_util_is_selftest(in, in_len); - ret = tpm_passthrough_unix_write(tpm_pt->tpm_fd, in, in_len); + ret = qemu_write_full(tpm_pt->tpm_fd, (const void *)in, (size_t)in_len); if (ret != in_len) { if (!tpm_pt->tpm_op_canceled || errno != ECANCELED) { error_report("tpm_passthrough: error while transmitting data " @@ -183,7 +116,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, strerror(errno), errno); } } else if (ret < sizeof(struct tpm_resp_hdr) || - tpm_passthrough_get_size_from_buffer(out) != ret) { + be32_to_cpu(((struct tpm_resp_hdr *)out)->len) != ret) { ret = -1; error_report("tpm_passthrough: received invalid response " "packet from TPM"); @@ -196,7 +129,7 @@ static int tpm_passthrough_unix_tx_bufs(TPMPassthruState *tpm_pt, err_exit: if (ret < 0) { - tpm_write_fatal_error_response(out, out_len); + tpm_util_write_fatal_error_response(out, out_len); } tpm_pt->tpm_executing = false; @@ -216,12 +149,9 @@ static int tpm_passthrough_unix_transfer(TPMPassthruState *tpm_pt, selftest_done); } -static void tpm_passthrough_worker_thread(gpointer data, - gpointer user_data) +static void tpm_passthrough_handle_request(TPMBackend *tb, TPMBackendCmd cmd) { - TPMPassthruThreadParams *thr_parms = user_data; - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(thr_parms->tb); - TPMBackendCmd cmd = (TPMBackendCmd)data; + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); bool selftest_done = false; DPRINTF("tpm_passthrough: processing command type %d\n", cmd); @@ -229,12 +159,12 @@ static void tpm_passthrough_worker_thread(gpointer data, switch (cmd) { case TPM_BACKEND_CMD_PROCESS_CMD: tpm_passthrough_unix_transfer(tpm_pt, - thr_parms->tpm_state->locty_data, + tb->tpm_state->locty_data, &selftest_done); - thr_parms->recv_data_callback(thr_parms->tpm_state, - thr_parms->tpm_state->locty_number, - selftest_done); + tb->recv_data_callback(tb->tpm_state, + tb->tpm_state->locty_number, + selftest_done); break; case TPM_BACKEND_CMD_INIT: case TPM_BACKEND_CMD_END: @@ -244,47 +174,11 @@ static void tpm_passthrough_worker_thread(gpointer data, } } -/* - * Start the TPM (thread). If it had been started before, then terminate - * and start it again. - */ -static int tpm_passthrough_startup_tpm(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - /* terminate a running TPM */ - tpm_backend_thread_end(&tpm_pt->tbt); - - tpm_backend_thread_create(&tpm_pt->tbt, - tpm_passthrough_worker_thread, - &tpm_pt->tpm_thread_params); - - return 0; -} - static void tpm_passthrough_reset(TPMBackend *tb) { - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - DPRINTF("tpm_passthrough: CALL TO TPM_RESET!\n"); tpm_passthrough_cancel_cmd(tb); - - tpm_backend_thread_end(&tpm_pt->tbt); - - tpm_pt->had_startup_error = false; -} - -static int tpm_passthrough_init(TPMBackend *tb, TPMState *s, - TPMRecvDataCB *recv_data_cb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_pt->tpm_thread_params.tpm_state = s; - tpm_pt->tpm_thread_params.recv_data_callback = recv_data_cb; - tpm_pt->tpm_thread_params.tb = tb; - - return 0; } static bool tpm_passthrough_get_tpm_established_flag(TPMBackend *tb) @@ -299,31 +193,6 @@ static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, return 0; } -static bool tpm_passthrough_get_startup_error(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - return tpm_pt->had_startup_error; -} - -static size_t tpm_passthrough_realloc_buffer(TPMSizedBuffer *sb) -{ - size_t wanted_size = 4096; /* Linux tpm.c buffer size */ - - if (sb->size != wanted_size) { - sb->buffer = g_realloc(sb->buffer, wanted_size); - sb->size = wanted_size; - } - return sb->size; -} - -static void tpm_passthrough_deliver_request(TPMBackend *tb) -{ - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); - - tpm_backend_thread_deliver_request(&tpm_pt->tbt); -} - static void tpm_passthrough_cancel_cmd(TPMBackend *tb) { TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); @@ -351,11 +220,6 @@ static void tpm_passthrough_cancel_cmd(TPMBackend *tb) } } -static const char *tpm_passthrough_create_desc(void) -{ - return "Passthrough TPM backend driver"; -} - static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) { TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); @@ -369,15 +233,14 @@ static TPMVersion tpm_passthrough_get_tpm_version(TPMBackend *tb) * in Documentation/ABI/stable/sysfs-class-tpm. * From /dev/tpm0 create /sys/class/misc/tpm0/device/cancel */ -static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) +static int tpm_passthrough_open_sysfs_cancel(TPMPassthruState *tpm_pt) { - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); int fd = -1; char *dev; char path[PATH_MAX]; - if (tb->cancel_path) { - fd = qemu_open(tb->cancel_path, O_WRONLY); + if (tpm_pt->options->cancel_path) { + fd = qemu_open(tpm_pt->options->cancel_path, O_WRONLY); if (fd < 0) { error_report("Could not open TPM cancel path : %s", strerror(errno)); @@ -392,7 +255,7 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) dev) < sizeof(path)) { fd = qemu_open(path, O_WRONLY); if (fd >= 0) { - tb->cancel_path = g_strdup(path); + tpm_pt->options->cancel_path = g_strdup(path); } else { error_report("tpm_passthrough: Could not open TPM cancel " "path %s : %s", path, strerror(errno)); @@ -412,17 +275,18 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) const char *value; value = qemu_opt_get(opts, "cancel-path"); - tb->cancel_path = g_strdup(value); + if (value) { + tpm_pt->options->cancel_path = g_strdup(value); + tpm_pt->options->has_cancel_path = true; + } value = qemu_opt_get(opts, "path"); - if (!value) { - value = TPM_PASSTHROUGH_DEFAULT_DEVICE; + if (value) { + tpm_pt->options->has_path = true; + tpm_pt->options->path = g_strdup(value); } - tpm_pt->tpm_dev = g_strdup(value); - - tb->path = g_strdup(tpm_pt->tpm_dev); - + tpm_pt->tpm_dev = value ? value : TPM_PASSTHROUGH_DEFAULT_DEVICE; tpm_pt->tpm_fd = qemu_open(tpm_pt->tpm_dev, O_RDWR); if (tpm_pt->tpm_fd < 0) { error_report("Cannot access TPM device using '%s': %s", @@ -443,10 +307,8 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) tpm_pt->tpm_fd = -1; err_free_parameters: - g_free(tb->path); - tb->path = NULL; - - g_free(tpm_pt->tpm_dev); + qapi_free_TPMPassthroughOptions(tpm_pt->options); + tpm_pt->options = NULL; tpm_pt->tpm_dev = NULL; return 1; @@ -459,16 +321,12 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); tb->id = g_strdup(id); - /* let frontend set the fe_model to proper value */ - tb->fe_model = -1; - - tb->ops = &tpm_passthrough_driver; if (tpm_passthrough_handle_device_opts(opts, tb)) { goto err_exit; } - tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tb); + tpm_pt->cancel_fd = tpm_passthrough_open_sysfs_cancel(tpm_pt); if (tpm_pt->cancel_fd < 0) { goto err_exit; } @@ -476,26 +334,20 @@ static TPMBackend *tpm_passthrough_create(QemuOpts *opts, const char *id) return tb; err_exit: - g_free(tb->id); + object_unref(obj); return NULL; } -static void tpm_passthrough_destroy(TPMBackend *tb) +static TpmTypeOptions *tpm_passthrough_get_tpm_options(TPMBackend *tb) { - TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); + TpmTypeOptions *options = g_new0(TpmTypeOptions, 1); - tpm_passthrough_cancel_cmd(tb); - - tpm_backend_thread_end(&tpm_pt->tbt); - - qemu_close(tpm_pt->tpm_fd); - qemu_close(tpm_pt->cancel_fd); + options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; + options->u.passthrough.data = QAPI_CLONE(TPMPassthroughOptions, + TPM_PASSTHROUGH(tb)->options); - g_free(tb->id); - g_free(tb->path); - g_free(tb->cancel_path); - g_free(tpm_pt->tpm_dev); + return options; } static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { @@ -516,27 +368,34 @@ static const QemuOptDesc tpm_passthrough_cmdline_opts[] = { static const TPMDriverOps tpm_passthrough_driver = { .type = TPM_TYPE_PASSTHROUGH, .opts = tpm_passthrough_cmdline_opts, - .desc = tpm_passthrough_create_desc, + .desc = "Passthrough TPM backend driver", .create = tpm_passthrough_create, - .destroy = tpm_passthrough_destroy, - .init = tpm_passthrough_init, - .startup_tpm = tpm_passthrough_startup_tpm, - .realloc_buffer = tpm_passthrough_realloc_buffer, .reset = tpm_passthrough_reset, - .had_startup_error = tpm_passthrough_get_startup_error, - .deliver_request = tpm_passthrough_deliver_request, .cancel_cmd = tpm_passthrough_cancel_cmd, .get_tpm_established_flag = tpm_passthrough_get_tpm_established_flag, .reset_tpm_established_flag = tpm_passthrough_reset_tpm_established_flag, .get_tpm_version = tpm_passthrough_get_tpm_version, + .get_tpm_options = tpm_passthrough_get_tpm_options, }; static void tpm_passthrough_inst_init(Object *obj) { + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); + + tpm_pt->options = g_new0(TPMPassthroughOptions, 1); + tpm_pt->tpm_fd = -1; + tpm_pt->cancel_fd = -1; } static void tpm_passthrough_inst_finalize(Object *obj) { + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(obj); + + tpm_passthrough_cancel_cmd(TPM_BACKEND(obj)); + + qemu_close(tpm_pt->tpm_fd); + qemu_close(tpm_pt->cancel_fd); + qapi_free_TPMPassthroughOptions(tpm_pt->options); } static void tpm_passthrough_class_init(ObjectClass *klass, void *data) @@ -544,6 +403,7 @@ static void tpm_passthrough_class_init(ObjectClass *klass, void *data) TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); tbc->ops = &tpm_passthrough_driver; + tbc->handle_request = tpm_passthrough_handle_request; } static const TypeInfo tpm_passthrough_info = { diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index a6440fef91..d5118e7f60 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -963,6 +963,16 @@ static int tpm_tis_do_startup_tpm(TPMState *s) return tpm_backend_startup_tpm(s->be_driver); } +static void tpm_tis_realloc_buffer(TPMSizedBuffer *sb) +{ + size_t wanted_size = 4096; /* Linux tpm.c buffer size */ + + if (sb->size != wanted_size) { + sb->buffer = g_realloc(sb->buffer, wanted_size); + sb->size = wanted_size; + } +} + /* * Get the TPMVersion of the backend device being used */ @@ -1010,9 +1020,9 @@ static void tpm_tis_reset(DeviceState *dev) tis->loc[c].state = TPM_TIS_STATE_IDLE; tis->loc[c].w_offset = 0; - tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].w_buffer); + tpm_tis_realloc_buffer(&tis->loc[c].w_buffer); tis->loc[c].r_offset = 0; - tpm_backend_realloc_buffer(s->be_driver, &tis->loc[c].r_buffer); + tpm_tis_realloc_buffer(&tis->loc[c].r_buffer); } tpm_tis_do_startup_tpm(s); diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c index 7b35429725..73d77965fd 100644 --- a/hw/tpm/tpm_util.c +++ b/hw/tpm/tpm_util.c @@ -24,6 +24,31 @@ #include "tpm_int.h" /* + * Write an error message in the given output buffer. + */ +void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len) +{ + if (out_len >= sizeof(struct tpm_resp_hdr)) { + struct tpm_resp_hdr *resp = (struct tpm_resp_hdr *)out; + + resp->tag = cpu_to_be16(TPM_TAG_RSP_COMMAND); + resp->len = cpu_to_be32(sizeof(struct tpm_resp_hdr)); + resp->errcode = cpu_to_be32(TPM_FAIL); + } +} + +bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len) +{ + struct tpm_req_hdr *hdr = (struct tpm_req_hdr *)in; + + if (in_len >= sizeof(*hdr)) { + return (be32_to_cpu(hdr->ordinal) == TPM_ORD_ContinueSelfTest); + } + + return false; +} + +/* * A basic test of a TPM device. We expect a well formatted response header * (error response is fine) within one second. */ @@ -43,10 +68,10 @@ static int tpm_util_test(int fd, n = write(fd, request, requestlen); if (n < 0) { - return errno; + return -errno; } if (n != requestlen) { - return EFAULT; + return -EFAULT; } FD_ZERO(&readfds); @@ -55,18 +80,18 @@ static int tpm_util_test(int fd, /* wait for a second */ n = select(fd + 1, &readfds, NULL, NULL, &tv); if (n != 1) { - return errno; + return -errno; } n = read(fd, &buf, sizeof(buf)); if (n < sizeof(struct tpm_resp_hdr)) { - return EFAULT; + return -EFAULT; } resp = (struct tpm_resp_hdr *)buf; /* check the header */ if (be32_to_cpu(resp->len) != n) { - return EBADMSG; + return -EMSGSIZE; } *return_tag = be16_to_cpu(resp->tag); diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h index df76245e6e..2f7c96146d 100644 --- a/hw/tpm/tpm_util.h +++ b/hw/tpm/tpm_util.h @@ -24,6 +24,10 @@ #include "sysemu/tpm_backend.h" +void tpm_util_write_fatal_error_response(uint8_t *out, uint32_t out_len); + +bool tpm_util_is_selftest(const uint8_t *in, uint32_t in_len); + int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); #endif /* TPM_TPM_UTIL_H */ diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 6dedcb8989..8c0fc53a26 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -170,6 +170,10 @@ static const TypeInfo ehci_pci_type_info = { .instance_finalize = usb_ehci_pci_finalize, .abstract = true, .class_init = ehci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void ehci_data_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 17beeddb09..d4c0293db5 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -2146,6 +2146,10 @@ static const TypeInfo ohci_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(OHCIPCIState), .class_init = ohci_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static Property ohci_sysbus_properties[] = { diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index e3562a4c60..836b11f177 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1323,6 +1323,10 @@ static const TypeInfo uhci_pci_type_info = { .class_size = sizeof(UHCIPCIDeviceClass), .abstract = true, .class_init = uhci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void uhci_data_class_init(ObjectClass *klass, void *data) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index d75c085d94..af3a9d88de 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3670,6 +3670,11 @@ static const TypeInfo xhci_info = { .instance_size = sizeof(XHCIState), .class_init = xhci_class_init, .abstract = true, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, }; static void qemu_xhci_class_init(ObjectClass *klass, void *data) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 14291c2a16..60ad5fb91a 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -1198,6 +1198,10 @@ static TypeInfo vfio_pci_igd_lpc_bridge_info = { .name = "vfio-pci-igd-lpc-bridge", .parent = TYPE_PCI_DEVICE, .class_init = vfio_pci_igd_lpc_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void vfio_pci_igd_register_types(void) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9e86db7c3b..c977ee327f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3036,6 +3036,11 @@ static const TypeInfo vfio_pci_dev_info = { .class_init = vfio_pci_dev_class_init, .instance_init = vfio_instance_init, .instance_finalize = vfio_instance_finalize, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, }; static void register_vfio_pci_dev_type(void) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 5fd69f0b2e..ddc42f0f93 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -375,8 +375,6 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) if (!log) { return; } - dev->log = NULL; - dev->log_size = 0; --log->refcnt; if (log->refcnt == 0) { @@ -396,6 +394,9 @@ static void vhost_log_put(struct vhost_dev *dev, bool sync) g_free(log); } + + dev->log = NULL; + dev->log_size = 0; } static bool vhost_dev_log_is_shared(struct vhost_dev *dev) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 8b0d6b69cd..e92837c42b 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -88,77 +88,19 @@ static void virtio_pci_save_config(DeviceState *d, QEMUFile *f) qemu_put_be16(f, vdev->config_vector); } -static void virtio_pci_load_modern_queue_state(VirtIOPCIQueue *vq, - QEMUFile *f) -{ - vq->num = qemu_get_be16(f); - vq->enabled = qemu_get_be16(f); - vq->desc[0] = qemu_get_be32(f); - vq->desc[1] = qemu_get_be32(f); - vq->avail[0] = qemu_get_be32(f); - vq->avail[1] = qemu_get_be32(f); - vq->used[0] = qemu_get_be32(f); - vq->used[1] = qemu_get_be32(f); -} - -static bool virtio_pci_has_extra_state(DeviceState *d) -{ - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - - return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA; -} - -static int get_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field) -{ - VirtIOPCIProxy *proxy = pv; - int i; - - proxy->dfselect = qemu_get_be32(f); - proxy->gfselect = qemu_get_be32(f); - proxy->guest_features[0] = qemu_get_be32(f); - proxy->guest_features[1] = qemu_get_be32(f); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - virtio_pci_load_modern_queue_state(&proxy->vqs[i], f); - } - - return 0; -} - -static void virtio_pci_save_modern_queue_state(VirtIOPCIQueue *vq, - QEMUFile *f) -{ - qemu_put_be16(f, vq->num); - qemu_put_be16(f, vq->enabled); - qemu_put_be32(f, vq->desc[0]); - qemu_put_be32(f, vq->desc[1]); - qemu_put_be32(f, vq->avail[0]); - qemu_put_be32(f, vq->avail[1]); - qemu_put_be32(f, vq->used[0]); - qemu_put_be32(f, vq->used[1]); -} - -static int put_virtio_pci_modern_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) -{ - VirtIOPCIProxy *proxy = pv; - int i; - - qemu_put_be32(f, proxy->dfselect); - qemu_put_be32(f, proxy->gfselect); - qemu_put_be32(f, proxy->guest_features[0]); - qemu_put_be32(f, proxy->guest_features[1]); - for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { - virtio_pci_save_modern_queue_state(&proxy->vqs[i], f); +static const VMStateDescription vmstate_virtio_pci_modern_queue_state = { + .name = "virtio_pci/modern_queue_state", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16(num, VirtIOPCIQueue), + VMSTATE_UNUSED(1), /* enabled was stored as be16 */ + VMSTATE_BOOL(enabled, VirtIOPCIQueue), + VMSTATE_UINT32_ARRAY(desc, VirtIOPCIQueue, 2), + VMSTATE_UINT32_ARRAY(avail, VirtIOPCIQueue, 2), + VMSTATE_UINT32_ARRAY(used, VirtIOPCIQueue, 2), + VMSTATE_END_OF_LIST() } - - return 0; -} - -static const VMStateInfo vmstate_info_virtio_pci_modern_state = { - .name = "virtqueue_state", - .get = get_virtio_pci_modern_state, - .put = put_virtio_pci_modern_state, }; static bool virtio_pci_modern_state_needed(void *opaque) @@ -168,21 +110,18 @@ static bool virtio_pci_modern_state_needed(void *opaque) return virtio_pci_modern(proxy); } -static const VMStateDescription vmstate_virtio_pci_modern_state = { +static const VMStateDescription vmstate_virtio_pci_modern_state_sub = { .name = "virtio_pci/modern_state", .version_id = 1, .minimum_version_id = 1, .needed = &virtio_pci_modern_state_needed, .fields = (VMStateField[]) { - { - .name = "modern_state", - .version_id = 0, - .field_exists = NULL, - .size = 0, - .info = &vmstate_info_virtio_pci_modern_state, - .flags = VMS_SINGLE, - .offset = 0, - }, + VMSTATE_UINT32(dfselect, VirtIOPCIProxy), + VMSTATE_UINT32(gfselect, VirtIOPCIProxy), + VMSTATE_UINT32_ARRAY(guest_features, VirtIOPCIProxy, 2), + VMSTATE_STRUCT_ARRAY(vqs, VirtIOPCIProxy, VIRTIO_QUEUE_MAX, 0, + vmstate_virtio_pci_modern_queue_state, + VirtIOPCIQueue), VMSTATE_END_OF_LIST() } }; @@ -196,11 +135,18 @@ static const VMStateDescription vmstate_virtio_pci = { VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription*[]) { - &vmstate_virtio_pci_modern_state, + &vmstate_virtio_pci_modern_state_sub, NULL } }; +static bool virtio_pci_has_extra_state(DeviceState *d) +{ + VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); + + return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA; +} + static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); @@ -545,6 +491,24 @@ static const MemoryRegionOps virtio_pci_config_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static MemoryRegion *virtio_address_space_lookup(VirtIOPCIProxy *proxy, + hwaddr *off, int len) +{ + int i; + VirtIOPCIRegion *reg; + + for (i = 0; i < ARRAY_SIZE(proxy->regs); ++i) { + reg = &proxy->regs[i]; + if (*off >= reg->offset && + *off + len <= reg->offset + reg->size) { + *off -= reg->offset; + return ®->mr; + } + } + + return NULL; +} + /* Below are generic functions to do memcpy from/to an address space, * without byteswaps, with input validation. * @@ -558,63 +522,72 @@ static const MemoryRegionOps virtio_pci_config_ops = { * Note: host pointer must be aligned. */ static -void virtio_address_space_write(AddressSpace *as, hwaddr addr, +void virtio_address_space_write(VirtIOPCIProxy *proxy, hwaddr addr, const uint8_t *buf, int len) { - uint32_t val; + uint64_t val; + MemoryRegion *mr; /* address_space_* APIs assume an aligned address. * As address is under guest control, handle illegal values. */ addr &= ~(len - 1); + mr = virtio_address_space_lookup(proxy, &addr, len); + if (!mr) { + return; + } + /* Make sure caller aligned buf properly */ assert(!(((uintptr_t)buf) & (len - 1))); switch (len) { case 1: val = pci_get_byte(buf); - address_space_stb(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); break; case 2: - val = pci_get_word(buf); - address_space_stw_le(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); + val = cpu_to_le16(pci_get_word(buf)); break; case 4: - val = pci_get_long(buf); - address_space_stl_le(as, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); + val = cpu_to_le32(pci_get_long(buf)); break; default: /* As length is under guest control, handle illegal values. */ - break; + return; } + memory_region_dispatch_write(mr, addr, val, len, MEMTXATTRS_UNSPECIFIED); } static void -virtio_address_space_read(AddressSpace *as, hwaddr addr, uint8_t *buf, int len) +virtio_address_space_read(VirtIOPCIProxy *proxy, hwaddr addr, + uint8_t *buf, int len) { - uint32_t val; + uint64_t val; + MemoryRegion *mr; /* address_space_* APIs assume an aligned address. * As address is under guest control, handle illegal values. */ addr &= ~(len - 1); + mr = virtio_address_space_lookup(proxy, &addr, len); + if (!mr) { + return; + } + /* Make sure caller aligned buf properly */ assert(!(((uintptr_t)buf) & (len - 1))); + memory_region_dispatch_read(mr, addr, &val, len, MEMTXATTRS_UNSPECIFIED); switch (len) { case 1: - val = address_space_ldub(as, addr, MEMTXATTRS_UNSPECIFIED, NULL); pci_set_byte(buf, val); break; case 2: - val = address_space_lduw_le(as, addr, MEMTXATTRS_UNSPECIFIED, NULL); - pci_set_word(buf, val); + pci_set_word(buf, le16_to_cpu(val)); break; case 4: - val = address_space_ldl_le(as, addr, MEMTXATTRS_UNSPECIFIED, NULL); - pci_set_long(buf, val); + pci_set_long(buf, le32_to_cpu(val)); break; default: /* As length is under guest control, handle illegal values. */ @@ -650,8 +623,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, if (len == 1 || len == 2 || len == 4) { assert(len <= sizeof cfg->pci_cfg_data); - virtio_address_space_write(&proxy->modern_as, off, - cfg->pci_cfg_data, len); + virtio_address_space_write(proxy, off, cfg->pci_cfg_data, len); } } } @@ -675,8 +647,7 @@ static uint32_t virtio_read_config(PCIDevice *pci_dev, if (len == 1 || len == 2 || len == 4) { assert(len <= sizeof cfg->pci_cfg_data); - virtio_address_space_read(&proxy->modern_as, off, - cfg->pci_cfg_data, len); + virtio_address_space_read(proxy, off, cfg->pci_cfg_data, len); } } @@ -1783,15 +1754,6 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) /* PCI BAR regions must be powers of 2 */ pow2ceil(proxy->notify.offset + proxy->notify.size)); - memory_region_init_alias(&proxy->modern_cfg, - OBJECT(proxy), - "virtio-pci-cfg", - &proxy->modern_bar, - 0, - memory_region_size(&proxy->modern_bar)); - - address_space_init(&proxy->modern_as, &proxy->modern_cfg, "virtio-pci-cfg-as"); - if (proxy->disable_legacy == ON_OFF_AUTO_AUTO) { proxy->disable_legacy = pcie_port ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } @@ -1860,10 +1822,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) static void virtio_pci_exit(PCIDevice *pci_dev) { - VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); - msix_uninit_exclusive_bar(pci_dev); - address_space_destroy(&proxy->modern_as); } static void virtio_pci_reset(DeviceState *qdev) @@ -1958,6 +1917,11 @@ static const TypeInfo virtio_pci_info = { .class_init = virtio_pci_class_init, .class_size = sizeof(VirtioPCIClass), .abstract = true, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, }; /* virtio-blk-pci */ diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 69f5959623..12d3a90686 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -155,15 +155,18 @@ typedef struct VirtIOPCIQueue { struct VirtIOPCIProxy { PCIDevice pci_dev; MemoryRegion bar; - VirtIOPCIRegion common; - VirtIOPCIRegion isr; - VirtIOPCIRegion device; - VirtIOPCIRegion notify; - VirtIOPCIRegion notify_pio; + union { + struct { + VirtIOPCIRegion common; + VirtIOPCIRegion isr; + VirtIOPCIRegion device; + VirtIOPCIRegion notify; + VirtIOPCIRegion notify_pio; + }; + VirtIOPCIRegion regs[5]; + }; MemoryRegion modern_bar; MemoryRegion io_bar; - MemoryRegion modern_cfg; - AddressSpace modern_as; uint32_t legacy_io_bar_idx; uint32_t msix_bar_idx; uint32_t modern_io_bar_idx; diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 311929e9df..5884ce3480 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -834,7 +834,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) int64_t len; VirtIODevice *vdev = vq->vdev; VirtQueueElement *elem = NULL; - unsigned out_num, in_num; + unsigned out_num, in_num, elem_entries; hwaddr addr[VIRTQUEUE_MAX_SIZE]; struct iovec iov[VIRTQUEUE_MAX_SIZE]; VRingDesc desc; @@ -852,7 +852,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) smp_rmb(); /* When we start there are none of either input nor output. */ - out_num = in_num = 0; + out_num = in_num = elem_entries = 0; max = vq->vring.num; @@ -922,7 +922,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) } /* If we've got too many, that implies a descriptor loop. */ - if ((in_num + out_num) > max) { + if (++elem_entries > max) { virtio_error(vdev, "Looped descriptor"); goto err_undo_map; } diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 49b3cd188a..e596b0804d 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -463,6 +463,10 @@ static const TypeInfo i6300esb_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(I6300State), .class_init = i6300esb_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, }; static void i6300esb_register_types(void) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 375efa68f6..9bba717708 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -964,6 +964,11 @@ static const TypeInfo xen_pci_passthrough_info = { .instance_size = sizeof(XenPCIPassthroughState), .instance_finalize = xen_pci_passthrough_finalize, .class_init = xen_pci_passthrough_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { INTERFACE_PCIE_DEVICE }, + { }, + }, }; static void xen_pci_passthrough_register_types(void) diff --git a/include/block/block_int.h b/include/block/block_int.h index 7e8a206239..885c08e989 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -354,10 +354,17 @@ struct BlockDriver { int (*bdrv_probe_geometry)(BlockDriverState *bs, HDGeometry *geo); /** - * Drain and stop any internal sources of requests in the driver, and - * remain so until next I/O callback (e.g. bdrv_co_writev) is called. + * bdrv_co_drain_begin is called if implemented in the beginning of a + * drain operation to drain and stop any internal sources of requests in + * the driver. + * bdrv_co_drain_end is called if implemented at the end of the drain. + * + * They should be used by the driver to e.g. manage scheduled I/O + * requests, or toggle an internal state. After the end of the drain new + * requests will continue normally. */ - void coroutine_fn (*bdrv_co_drain)(BlockDriverState *bs); + void coroutine_fn (*bdrv_co_drain_begin)(BlockDriverState *bs); + void coroutine_fn (*bdrv_co_drain_end)(BlockDriverState *bs); void (*bdrv_add_child)(BlockDriverState *parent, BlockDriverState *child, Error **errp); diff --git a/include/block/nbd.h b/include/block/nbd.h index 707fd37575..a6df5ce8b5 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -63,15 +63,22 @@ struct NBDReply { }; typedef struct NBDReply NBDReply; +typedef struct NBDSimpleReply { + uint32_t magic; /* NBD_SIMPLE_REPLY_MAGIC */ + uint32_t error; + uint64_t handle; +} QEMU_PACKED NBDSimpleReply; + /* Transmission (export) flags: sent from server to client during handshake, but describe what will happen during transmission */ -#define NBD_FLAG_HAS_FLAGS (1 << 0) /* Flags are there */ -#define NBD_FLAG_READ_ONLY (1 << 1) /* Device is read-only */ -#define NBD_FLAG_SEND_FLUSH (1 << 2) /* Send FLUSH */ -#define NBD_FLAG_SEND_FUA (1 << 3) /* Send FUA (Force Unit Access) */ -#define NBD_FLAG_ROTATIONAL (1 << 4) /* Use elevator algorithm - rotational media */ -#define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ -#define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ +#define NBD_FLAG_HAS_FLAGS (1 << 0) /* Flags are there */ +#define NBD_FLAG_READ_ONLY (1 << 1) /* Device is read-only */ +#define NBD_FLAG_SEND_FLUSH (1 << 2) /* Send FLUSH */ +#define NBD_FLAG_SEND_FUA (1 << 3) /* Send FUA (Force Unit Access) */ +#define NBD_FLAG_ROTATIONAL (1 << 4) /* Use elevator algorithm - + rotational media */ +#define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ +#define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ diff --git a/include/hw/misc/vmcoreinfo.h b/include/hw/misc/vmcoreinfo.h new file mode 100644 index 0000000000..c3aa856545 --- /dev/null +++ b/include/hw/misc/vmcoreinfo.h @@ -0,0 +1,46 @@ +/* + * Virtual Machine coreinfo device + * + * Copyright (C) 2017 Red Hat, Inc. + * + * Authors: Marc-AndrĂ© Lureau <marcandre.lureau@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 VMCOREINFO_H +#define VMCOREINFO_H + +#include "hw/qdev.h" + +#define VMCOREINFO_DEVICE "vmcoreinfo" +#define VMCOREINFO(obj) OBJECT_CHECK(VMCoreInfoState, (obj), VMCOREINFO_DEVICE) + +#define VMCOREINFO_FORMAT_NONE 0x0 +#define VMCOREINFO_FORMAT_ELF 0x1 + +/* all fields are little-endian */ +typedef struct FWCfgVMCoreInfo { + uint16_t host_format; /* set on reset */ + uint16_t guest_format; + uint32_t size; + uint64_t paddr; +} QEMU_PACKED FWCfgVMCoreInfo; + +typedef struct VMCoreInfoState { + DeviceClass parent_obj; + + bool has_vmcoreinfo; + FWCfgVMCoreInfo vmcoreinfo; +} VMCoreInfoState; + +/* returns NULL unless there is exactly one device */ +static inline VMCoreInfoState *vmcoreinfo_find(void) +{ + Object *o = object_resolve_path_type("", VMCOREINFO_DEVICE, NULL); + + return o ? VMCOREINFO(o) : NULL; +} + +#endif diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index f50d068563..7ccbae5fba 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -45,6 +45,7 @@ typedef struct FWCfgDmaAccess { } QEMU_PACKED FWCfgDmaAccess; typedef void (*FWCfgCallback)(void *opaque); +typedef void (*FWCfgWriteCallback)(void *opaque, off_t start, size_t len); struct FWCfgState { /*< private >*/ @@ -183,6 +184,7 @@ void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, * @s: fw_cfg device being modified * @filename: name of new fw_cfg file item * @select_cb: callback function when selecting + * @write_cb: callback function after a write * @callback_opaque: argument to be passed into callback function * @data: pointer to start of item data * @len: size of item data @@ -202,6 +204,7 @@ void fw_cfg_add_file(FWCfgState *s, const char *filename, void *data, */ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgCallback select_cb, + FWCfgWriteCallback write_cb, void *callback_opaque, void *data, size_t len, bool read_only); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index aa7ef9cf69..8d02a0a383 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -198,6 +198,12 @@ enum { #define PCI_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(PCIDeviceClass, (obj), TYPE_PCI_DEVICE) +/* Implemented by devices that can be plugged on PCI Express buses */ +#define INTERFACE_PCIE_DEVICE "pci-express-device" + +/* Implemented by devices that can be plugged on Conventional PCI buses */ +#define INTERFACE_CONVENTIONAL_PCI_DEVICE "conventional-pci-device" + typedef struct PCIINTxRoute { enum { PCI_INTX_ENABLED, diff --git a/include/sysemu/dump.h b/include/sysemu/dump.h index 2672a15f8b..df43bd0e07 100644 --- a/include/sysemu/dump.h +++ b/include/sysemu/dump.h @@ -192,6 +192,8 @@ typedef struct DumpState { * this could be used to calculate * how much work we have * finished. */ + uint8_t *guest_note; /* ELF note content */ + size_t guest_note_size; } DumpState; uint16_t cpu_to_dump16(DumpState *s, uint16_t val); diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h index b0a9731aee..2c798a1eb4 100644 --- a/include/sysemu/tpm_backend.h +++ b/include/sysemu/tpm_backend.h @@ -29,33 +29,42 @@ typedef struct TPMBackendClass TPMBackendClass; typedef struct TPMBackend TPMBackend; - typedef struct TPMDriverOps TPMDriverOps; +typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done); -struct TPMBackendClass { - ObjectClass parent_class; - - const TPMDriverOps *ops; - - void (*opened)(TPMBackend *s, Error **errp); -}; +typedef enum TPMBackendCmd { + TPM_BACKEND_CMD_INIT = 1, + TPM_BACKEND_CMD_PROCESS_CMD, + TPM_BACKEND_CMD_END, + TPM_BACKEND_CMD_TPM_RESET, +} TPMBackendCmd; struct TPMBackend { Object parent; /*< protected >*/ bool opened; + TPMState *tpm_state; + GThreadPool *thread_pool; + TPMRecvDataCB *recv_data_callback; + bool had_startup_error; + /* <public> */ char *id; enum TpmModel fe_model; - char *path; - char *cancel_path; - const TPMDriverOps *ops; QLIST_ENTRY(TPMBackend) list; }; -typedef void (TPMRecvDataCB)(TPMState *, uint8_t locty, bool selftest_done); +struct TPMBackendClass { + ObjectClass parent_class; + + const TPMDriverOps *ops; + + void (*opened)(TPMBackend *s, Error **errp); + + void (*handle_request)(TPMBackend *s, TPMBackendCmd cmd); +}; typedef struct TPMSizedBuffer { uint32_t size; @@ -66,21 +75,14 @@ struct TPMDriverOps { enum TpmType type; const QemuOptDesc *opts; /* get a descriptive text of the backend to display to the user */ - const char *(*desc)(void); + const char *desc; TPMBackend *(*create)(QemuOpts *opts, const char *id); - void (*destroy)(TPMBackend *t); /* initialize the backend */ - int (*init)(TPMBackend *t, TPMState *s, TPMRecvDataCB *datacb); + int (*init)(TPMBackend *t); /* start up the TPM on the backend */ int (*startup_tpm)(TPMBackend *t); - /* returns true if nothing will ever answer TPM requests */ - bool (*had_startup_error)(TPMBackend *t); - - size_t (*realloc_buffer)(TPMSizedBuffer *sb); - - void (*deliver_request)(TPMBackend *t); void (*reset)(TPMBackend *t); @@ -91,6 +93,8 @@ struct TPMDriverOps { int (*reset_tpm_established_flag)(TPMBackend *t, uint8_t locty); TPMVersion (*get_tpm_version)(TPMBackend *t); + + TpmTypeOptions *(*get_tpm_options)(TPMBackend *t); }; @@ -103,20 +107,6 @@ struct TPMDriverOps { enum TpmType tpm_backend_get_type(TPMBackend *s); /** - * tpm_backend_get_desc: - * @s: the backend - * - * Returns a human readable description of the backend. - */ -const char *tpm_backend_get_desc(TPMBackend *s); - -/** - * tpm_backend_destroy: - * @s: the backend to destroy - */ -void tpm_backend_destroy(TPMBackend *s); - -/** * tpm_backend_init: * @s: the backend to initialized * @state: TPMState @@ -148,16 +138,6 @@ int tpm_backend_startup_tpm(TPMBackend *s); bool tpm_backend_had_startup_error(TPMBackend *s); /** - * tpm_backend_realloc_buffer: - * @s: the backend - * @sb: the TPMSizedBuffer to re-allocated to the size suitable for the - * backend. - * - * This function returns the size of the allocated buffer - */ -size_t tpm_backend_realloc_buffer(TPMBackend *s, TPMSizedBuffer *sb); - -/** * tpm_backend_deliver_request: * @s: the backend to send the request to * @@ -223,6 +203,16 @@ void tpm_backend_open(TPMBackend *s, Error **errp); */ TPMVersion tpm_backend_get_tpm_version(TPMBackend *s); +/** + * tpm_backend_query_tpm: + * @s: the backend + * + * Query backend tpm info + * + * Returns newly allocated TPMInfo + */ +TPMInfo *tpm_backend_query_tpm(TPMBackend *s); + TPMBackend *qemu_find_tpm(const char *id); const TPMDriverOps *tpm_get_backend_driver(const char *type); diff --git a/include/sysemu/tpm_backend_int.h b/include/sysemu/tpm_backend_int.h deleted file mode 100644 index 00639dd7de..0000000000 --- a/include/sysemu/tpm_backend_int.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * common TPM backend driver functions - * - * Copyright (c) 2012-2013 IBM Corporation - * Authors: - * Stefan Berger <stefanb@us.ibm.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - */ - -#ifndef TPM_BACKEND_INT_H -#define TPM_BACKEND_INT_H - -typedef struct TPMBackendThread { - GThreadPool *pool; -} TPMBackendThread; - -void tpm_backend_thread_deliver_request(TPMBackendThread *tbt); -void tpm_backend_thread_create(TPMBackendThread *tbt, - GFunc func, gpointer user_data); -void tpm_backend_thread_end(TPMBackendThread *tbt); - -typedef enum TPMBackendCmd { - TPM_BACKEND_CMD_INIT = 1, - TPM_BACKEND_CMD_PROCESS_CMD, - TPM_BACKEND_CMD_END, - TPM_BACKEND_CMD_TPM_RESET, -} TPMBackendCmd; - -#endif /* TPM_BACKEND_INT_H */ diff --git a/nbd/client.c b/nbd/client.c index 68a0bc1ffc..cd5a2c80ac 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -931,7 +931,7 @@ int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) } /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) + [ 0 .. 3] magic (NBD_SIMPLE_REPLY_MAGIC) [ 4 .. 7] error (0 == no error) [ 7 .. 15] handle */ @@ -949,7 +949,7 @@ int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) } trace_nbd_receive_reply(magic, reply->error, reply->handle); - if (magic != NBD_REPLY_MAGIC) { + if (magic != NBD_SIMPLE_REPLY_MAGIC) { error_setg(errp, "invalid magic (got 0x%" PRIx32 ")", magic); return -EINVAL; } diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index 8a609a227f..11a130d050 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -46,23 +46,23 @@ /* Size of oldstyle negotiation */ #define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) -#define NBD_REQUEST_MAGIC 0x25609513 -#define NBD_REPLY_MAGIC 0x67446698 -#define NBD_OPTS_MAGIC 0x49484156454F5054LL -#define NBD_CLIENT_MAGIC 0x0000420281861253LL -#define NBD_REP_MAGIC 0x0003e889045565a9LL - -#define NBD_SET_SOCK _IO(0xab, 0) -#define NBD_SET_BLKSIZE _IO(0xab, 1) -#define NBD_SET_SIZE _IO(0xab, 2) -#define NBD_DO_IT _IO(0xab, 3) -#define NBD_CLEAR_SOCK _IO(0xab, 4) -#define NBD_CLEAR_QUE _IO(0xab, 5) -#define NBD_PRINT_DEBUG _IO(0xab, 6) -#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) -#define NBD_DISCONNECT _IO(0xab, 8) -#define NBD_SET_TIMEOUT _IO(0xab, 9) -#define NBD_SET_FLAGS _IO(0xab, 10) +#define NBD_REQUEST_MAGIC 0x25609513 +#define NBD_SIMPLE_REPLY_MAGIC 0x67446698 +#define NBD_OPTS_MAGIC 0x49484156454F5054LL +#define NBD_CLIENT_MAGIC 0x0000420281861253LL +#define NBD_REP_MAGIC 0x0003e889045565a9LL + +#define NBD_SET_SOCK _IO(0xab, 0) +#define NBD_SET_BLKSIZE _IO(0xab, 1) +#define NBD_SET_SIZE _IO(0xab, 2) +#define NBD_DO_IT _IO(0xab, 3) +#define NBD_CLEAR_SOCK _IO(0xab, 4) +#define NBD_CLEAR_QUE _IO(0xab, 5) +#define NBD_PRINT_DEBUG _IO(0xab, 6) +#define NBD_SET_SIZE_BLOCKS _IO(0xab, 7) +#define NBD_DISCONNECT _IO(0xab, 8) +#define NBD_SET_TIMEOUT _IO(0xab, 9) +#define NBD_SET_FLAGS _IO(0xab, 10) /* NBD errors are based on errno numbers, so there is a 1:1 mapping, * but only a limited set of errno values is specified in the protocol. diff --git a/nbd/server.c b/nbd/server.c index 993ade30bb..3df3548d6d 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -902,26 +902,6 @@ static int nbd_receive_request(QIOChannel *ioc, NBDRequest *request, return 0; } -static int nbd_send_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) -{ - uint8_t buf[NBD_REPLY_SIZE]; - - reply->error = system_errno_to_nbd_errno(reply->error); - - trace_nbd_send_reply(reply->error, reply->handle); - - /* Reply - [ 0 .. 3] magic (NBD_REPLY_MAGIC) - [ 4 .. 7] error (0 == no error) - [ 7 .. 15] handle - */ - stl_be_p(buf, NBD_REPLY_MAGIC); - stl_be_p(buf + 4, reply->error); - stq_be_p(buf + 8, reply->handle); - - return nbd_write(ioc, buf, sizeof(buf), errp); -} - #define MAX_NBD_REQUESTS 16 void nbd_client_get(NBDClient *client) @@ -1047,7 +1027,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, { AioContext *ctx; BlockBackend *blk; - NBDExport *exp = g_malloc0(sizeof(NBDExport)); + NBDExport *exp = g_new0(NBDExport, 1); uint64_t perm; int ret; @@ -1208,38 +1188,51 @@ void nbd_export_close_all(void) } } -static int nbd_co_send_reply(NBDRequestData *req, NBDReply *reply, int len, - Error **errp) +static int coroutine_fn nbd_co_send_iov(NBDClient *client, struct iovec *iov, + unsigned niov, Error **errp) { - NBDClient *client = req->client; int ret; g_assert(qemu_in_coroutine()); - - trace_nbd_co_send_reply(reply->handle, reply->error, len); - qemu_co_mutex_lock(&client->send_lock); client->send_coroutine = qemu_coroutine_self(); - if (!len) { - ret = nbd_send_reply(client->ioc, reply, errp); - } else { - qio_channel_set_cork(client->ioc, true); - ret = nbd_send_reply(client->ioc, reply, errp); - if (ret == 0) { - ret = nbd_write(client->ioc, req->data, len, errp); - if (ret < 0) { - ret = -EIO; - } - } - qio_channel_set_cork(client->ioc, false); - } + ret = qio_channel_writev_all(client->ioc, iov, niov, errp) < 0 ? -EIO : 0; client->send_coroutine = NULL; qemu_co_mutex_unlock(&client->send_lock); + return ret; } +static inline void set_be_simple_reply(NBDSimpleReply *reply, uint64_t error, + uint64_t handle) +{ + stl_be_p(&reply->magic, NBD_SIMPLE_REPLY_MAGIC); + stl_be_p(&reply->error, error); + stq_be_p(&reply->handle, handle); +} + +static int nbd_co_send_simple_reply(NBDClient *client, + uint64_t handle, + uint32_t error, + void *data, + size_t len, + Error **errp) +{ + NBDSimpleReply reply; + int nbd_err = system_errno_to_nbd_errno(error); + struct iovec iov[] = { + {.iov_base = &reply, .iov_len = sizeof(reply)}, + {.iov_base = data, .iov_len = len} + }; + + trace_nbd_co_send_simple_reply(handle, nbd_err, len); + set_be_simple_reply(&reply, nbd_err, handle); + + return nbd_co_send_iov(client, iov, len ? 2 : 1, errp); +} + /* nbd_co_receive_request * Collect a client request. Return 0 if request looks valid, -EIO to drop * connection right away, and any other negative value to report an error to @@ -1331,7 +1324,6 @@ static coroutine_fn void nbd_trip(void *opaque) NBDExport *exp = client->exp; NBDRequestData *req; NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */ - NBDReply reply; int ret; int flags; int reply_data_len = 0; @@ -1351,11 +1343,7 @@ static coroutine_fn void nbd_trip(void *opaque) goto disconnect; } - reply.handle = request.handle; - reply.error = 0; - if (ret < 0) { - reply.error = -ret; goto reply; } @@ -1374,7 +1362,6 @@ static coroutine_fn void nbd_trip(void *opaque) ret = blk_co_flush(exp->blk); if (ret < 0) { error_setg_errno(&local_err, -ret, "flush failed"); - reply.error = -ret; break; } } @@ -1383,7 +1370,6 @@ static coroutine_fn void nbd_trip(void *opaque) req->data, request.len); if (ret < 0) { error_setg_errno(&local_err, -ret, "reading from file failed"); - reply.error = -ret; break; } @@ -1392,7 +1378,7 @@ static coroutine_fn void nbd_trip(void *opaque) break; case NBD_CMD_WRITE: if (exp->nbdflags & NBD_FLAG_READ_ONLY) { - reply.error = EROFS; + ret = -EROFS; break; } @@ -1404,14 +1390,13 @@ static coroutine_fn void nbd_trip(void *opaque) req->data, request.len, flags); if (ret < 0) { error_setg_errno(&local_err, -ret, "writing to file failed"); - reply.error = -ret; } break; case NBD_CMD_WRITE_ZEROES: if (exp->nbdflags & NBD_FLAG_READ_ONLY) { error_setg(&local_err, "Server is read-only, return error"); - reply.error = EROFS; + ret = -EROFS; break; } @@ -1426,7 +1411,6 @@ static coroutine_fn void nbd_trip(void *opaque) request.len, flags); if (ret < 0) { error_setg_errno(&local_err, -ret, "writing to file failed"); - reply.error = -ret; } break; @@ -1438,7 +1422,6 @@ static coroutine_fn void nbd_trip(void *opaque) ret = blk_co_flush(exp->blk); if (ret < 0) { error_setg_errno(&local_err, -ret, "flush failed"); - reply.error = -ret; } break; @@ -1447,25 +1430,27 @@ static coroutine_fn void nbd_trip(void *opaque) request.len); if (ret < 0) { error_setg_errno(&local_err, -ret, "discard failed"); - reply.error = -ret; } break; default: error_setg(&local_err, "invalid request type (%" PRIu32 ") received", request.type); - reply.error = EINVAL; + ret = -EINVAL; } reply: if (local_err) { - /* If we are here local_err is not fatal error, already stored in - * reply.error */ + /* If we get here, local_err was not a fatal error, and should be sent + * to the client. */ error_report_err(local_err); local_err = NULL; } - if (nbd_co_send_reply(req, &reply, reply_data_len, &local_err) < 0) { + if (nbd_co_send_simple_reply(req->client, request.handle, + ret < 0 ? -ret : 0, + req->data, reply_data_len, &local_err) < 0) + { error_prepend(&local_err, "Failed to send reply: "); goto disconnect; } @@ -1539,7 +1524,7 @@ void nbd_client_new(NBDExport *exp, NBDClient *client; Coroutine *co; - client = g_malloc0(sizeof(NBDClient)); + client = g_new0(NBDClient, 1); client->refcount = 1; client->exp = exp; client->tlscreds = tlscreds; diff --git a/nbd/trace-events b/nbd/trace-events index 48a4f27682..e27614f050 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -51,10 +51,9 @@ nbd_negotiate_old_style(uint64_t size, unsigned flags) "advertising size %" PRIu nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags 0x%x" nbd_negotiate_success(void) "Negotiation succeeded" nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint32_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu32 " }" -nbd_send_reply(int32_t error, uint64_t handle) "Sending response to client: { .error = %" PRId32 ", handle = %" PRIu64 " }" nbd_blk_aio_attached(const char *name, void *ctx) "Export %s: Attaching clients to AIO context %p\n" nbd_blk_aio_detach(const char *name, void *ctx) "Export %s: Detaching clients from AIO context %p\n" -nbd_co_send_reply(uint64_t handle, uint32_t error, int len) "Send reply: handle = %" PRIu64 ", error = %" PRIu32 ", len = %d" +nbd_co_send_simple_reply(uint64_t handle, uint32_t error, int len) "Send simple reply: handle = %" PRIu64 ", error = %" PRIu32 ", len = %d" nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 nbd_co_receive_request_cmd_write(uint32_t len) "Reading %" PRIu32 " byte(s)" diff --git a/qapi/tpm.json b/qapi/tpm.json index e8b2d8dcb7..7093f268fb 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -39,10 +39,12 @@ # An enumeration of TPM types # # @passthrough: TPM passthrough type +# @emulator: Software Emulator TPM type +# Since: 2.11 # # Since: 1.5 ## -{ 'enum': 'TpmType', 'data': [ 'passthrough' ] } +{ 'enum': 'TpmType', 'data': [ 'passthrough', 'emulator' ] } ## # @query-tpm-types: @@ -56,7 +58,7 @@ # Example: # # -> { "execute": "query-tpm-types" } -# <- { "return": [ "passthrough" ] } +# <- { "return": [ "passthrough", "emulator" ] } # ## { 'command': 'query-tpm-types', 'returns': ['TpmType'] } @@ -77,16 +79,29 @@ '*cancel-path' : 'str'} } ## +# @TPMEmulatorOptions: +# +# Information about the TPM emulator type +# +# @chardev: Name of a unix socket chardev +# +# Since: 2.11 +## +{ 'struct': 'TPMEmulatorOptions', 'data': { 'chardev' : 'str' } } + +## # @TpmTypeOptions: # # A union referencing different TPM backend types' configuration options # # @type: 'passthrough' The configuration options for the TPM passthrough type +# 'emulator' The configuration options for TPM emulator backend type # # Since: 1.5 ## { 'union': 'TpmTypeOptions', - 'data': { 'passthrough' : 'TPMPassthroughOptions' } } + 'data': { 'passthrough' : 'TPMPassthroughOptions', + 'emulator': 'TPMEmulatorOptions' } } ## # @TPMInfo: diff --git a/qemu-options.hx b/qemu-options.hx index 981742d191..3728e9b4dd 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -3121,7 +3121,9 @@ DEF("tpmdev", HAS_ARG, QEMU_OPTION_tpmdev, \ "-tpmdev passthrough,id=id[,path=path][,cancel-path=path]\n" " use path to provide path to a character device; default is /dev/tpm0\n" " use cancel-path to provide path to TPM's cancel sysfs entry; if\n" - " not provided it will be searched for in /sys/class/misc/tpm?/device\n", + " not provided it will be searched for in /sys/class/misc/tpm?/device\n" + "-tpmdev emulator,id=id,chardev=dev\n" + " configure the TPM device using chardev backend\n", QEMU_ARCH_ALL) STEXI @@ -3130,8 +3132,8 @@ The general form of a TPM device option is: @item -tpmdev @var{backend} ,id=@var{id} [,@var{options}] @findex -tpmdev -Backend type must be: -@option{passthrough}. +Backend type must be either one of the following: +@option{passthrough}, @option{emulator}. The specific backend type will determine the applicable options. The @code{-tpmdev} option creates the TPM backend and requires a @@ -3181,6 +3183,20 @@ To create a passthrough TPM use the following two options: Note that the @code{-tpmdev} id is @code{tpm0} and is referenced by @code{tpmdev=tpm0} in the device option. +@item -tpmdev emulator, id=@var{id}, chardev=@var{dev} + +(Linux-host only) Enable access to a TPM emulator using Unix domain socket based +chardev backend. + +@option{chardev} specifies the unique ID of a character device backend that provides connection to the software TPM server. + +To create a TPM emulator backend device with chardev socket backend: +@example + +-chardev socket,id=chrtpm,path=/tmp/swtpm-sock -tpmdev emulator,id=tpm0,chardev=chrtpm -device tpm-tis,tpmdev=tpm0 + +@end example + @end table ETEXI diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index f7c6635f15..69dd5efadf 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -14,6 +14,7 @@ the COPYING file in the top-level directory. """ import ctypes +import struct UINTPTR_T = gdb.lookup_type("uintptr_t") @@ -45,6 +46,17 @@ EM_S390 = 22 EM_AARCH = 183 EM_X86_64 = 62 +VMCOREINFO_FORMAT_ELF = 1 + +def le16_to_cpu(val): + return struct.unpack("<H", struct.pack("=H", val))[0] + +def le32_to_cpu(val): + return struct.unpack("<I", struct.pack("=I", val))[0] + +def le64_to_cpu(val): + return struct.unpack("<Q", struct.pack("=Q", val))[0] + class ELF(object): """Representation of a ELF file.""" @@ -120,6 +132,25 @@ class ELF(object): self.segments[0].p_filesz += ctypes.sizeof(note) self.segments[0].p_memsz += ctypes.sizeof(note) + + def add_vmcoreinfo_note(self, vmcoreinfo): + """Adds a vmcoreinfo note to the ELF dump.""" + # compute the header size, and copy that many bytes from the note + header = get_arch_note(self.endianness, 0, 0) + ctypes.memmove(ctypes.pointer(header), + vmcoreinfo, ctypes.sizeof(header)) + if header.n_descsz > 1 << 20: + print('warning: invalid vmcoreinfo size') + return + # now get the full note + note = get_arch_note(self.endianness, + header.n_namesz - 1, header.n_descsz) + ctypes.memmove(ctypes.pointer(note), vmcoreinfo, ctypes.sizeof(note)) + + self.notes.append(note) + self.segments[0].p_filesz += ctypes.sizeof(note) + self.segments[0].p_memsz += ctypes.sizeof(note) + def add_segment(self, p_type, p_paddr, p_size): """Adds a segment to the elf.""" @@ -505,6 +536,35 @@ shape and this command should mostly work.""" cur += chunk_size left -= chunk_size + def phys_memory_read(self, addr, size): + qemu_core = gdb.inferiors()[0] + for block in self.guest_phys_blocks: + if block["target_start"] <= addr \ + and addr + size <= block["target_end"]: + haddr = block["host_addr"] + (addr - block["target_start"]) + return qemu_core.read_memory(haddr, size) + return None + + def add_vmcoreinfo(self): + if not gdb.parse_and_eval("vmcoreinfo_find()") \ + or not gdb.parse_and_eval("vmcoreinfo_find()->has_vmcoreinfo"): + return + + fmt = gdb.parse_and_eval("vmcoreinfo_find()->vmcoreinfo.guest_format") + addr = gdb.parse_and_eval("vmcoreinfo_find()->vmcoreinfo.paddr") + size = gdb.parse_and_eval("vmcoreinfo_find()->vmcoreinfo.size") + + fmt = le16_to_cpu(fmt) + addr = le64_to_cpu(addr) + size = le32_to_cpu(size) + + if fmt != VMCOREINFO_FORMAT_ELF: + return + + vmcoreinfo = self.phys_memory_read(addr, size) + if vmcoreinfo: + self.elf.add_vmcoreinfo_note(vmcoreinfo.tobytes()) + def invoke(self, args, from_tty): """Handles command invocation from gdb.""" @@ -518,6 +578,7 @@ shape and this command should mostly work.""" self.elf = ELF(argv[1]) self.guest_phys_blocks = get_guest_phys_blocks() + self.add_vmcoreinfo() with open(argv[0], "wb") as vmcore: self.dump_init(vmcore) diff --git a/tests/pxe-test.c b/tests/pxe-test.c index 0d70afccd6..937f29e631 100644 --- a/tests/pxe-test.c +++ b/tests/pxe-test.c @@ -1,11 +1,12 @@ /* * PXE test cases. * - * Copyright (c) 2016 Red Hat Inc. + * Copyright (c) 2016, 2017 Red Hat Inc. * * Authors: * Michael S. Tsirkin <mst@redhat.com>, * Victor Kaplansky <victork@redhat.com> + * Thomas Huth <thuth@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. @@ -36,14 +37,14 @@ static void test_pxe_one(const char *params, bool ipv6) g_free(args); } -static void test_pxe_e1000(void) +static void test_pxe_ipv4(gconstpointer data) { - test_pxe_one("-device e1000,netdev=" NETNAME, false); -} + const char *model = data; + char *dev_arg; -static void test_pxe_virtio_pci(void) -{ - test_pxe_one("-device virtio-net-pci,netdev=" NETNAME, false); + dev_arg = g_strdup_printf("-device %s,netdev=" NETNAME, model); + test_pxe_one(dev_arg, false); + g_free(dev_arg); } static void test_pxe_spapr_vlan(void) @@ -68,11 +69,21 @@ int main(int argc, char *argv[]) g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("pxe/e1000", test_pxe_e1000); - qtest_add_func("pxe/virtio", test_pxe_virtio_pci); + qtest_add_data_func("pxe/e1000", "e1000", test_pxe_ipv4); + qtest_add_data_func("pxe/virtio", "virtio-net-pci", test_pxe_ipv4); + if (g_test_slow()) { + qtest_add_data_func("pxe/ne2000", "ne2k_pci", test_pxe_ipv4); + qtest_add_data_func("pxe/eepro100", "i82550", test_pxe_ipv4); + qtest_add_data_func("pxe/pcnet", "pcnet", test_pxe_ipv4); + qtest_add_data_func("pxe/rtl8139", "rtl8139", test_pxe_ipv4); + qtest_add_data_func("pxe/vmxnet3", "vmxnet3", test_pxe_ipv4); + } } else if (strcmp(arch, "ppc64") == 0) { - qtest_add_func("pxe/virtio", test_pxe_virtio_pci); qtest_add_func("pxe/spapr-vlan", test_pxe_spapr_vlan); + if (g_test_slow()) { + qtest_add_data_func("pxe/virtio", "virtio-net-pci", test_pxe_ipv4); + qtest_add_data_func("pxe/e1000", "e1000", test_pxe_ipv4); + } } else if (g_str_equal(arch, "s390x")) { qtest_add_func("pxe/virtio-ccw", test_pxe_virtio_ccw); } diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py index 1c10dcb51c..8a04d979aa 100755 --- a/tests/qemu-iotests/nbd-fault-injector.py +++ b/tests/qemu-iotests/nbd-fault-injector.py @@ -56,7 +56,7 @@ NBD_CMD_READ = 0 NBD_CMD_WRITE = 1 NBD_CMD_DISC = 2 NBD_REQUEST_MAGIC = 0x25609513 -NBD_REPLY_MAGIC = 0x67446698 +NBD_SIMPLE_REPLY_MAGIC = 0x67446698 NBD_PASSWD = 0x4e42444d41474943 NBD_OPTS_MAGIC = 0x49484156454F5054 NBD_CLIENT_MAGIC = 0x0000420281861253 @@ -166,7 +166,7 @@ def read_request(conn): return req def write_reply(conn, error, handle): - buf = reply_struct.pack(NBD_REPLY_MAGIC, error, handle) + buf = reply_struct.pack(NBD_SIMPLE_REPLY_MAGIC, error, handle) conn.send(buf, event='reply') def handle_connection(conn, use_export): diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index f922cc75ae..d820033a72 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -277,6 +277,7 @@ vubr_backend_recv_cb(int sock, void *ctx) DPRINT(" hdrlen = %d\n", hdrlen); if (!vu_queue_enabled(dev, vq) || + !vu_queue_started(dev, vq) || !vu_queue_avail_bytes(dev, vq, hdrlen, 0)) { DPRINT("Got UDP packet, but no available descriptors on RX virtq.\n"); return; @@ -62,7 +62,7 @@ static void tpm_display_backend_drivers(void) continue; } fprintf(stderr, "%12s %s\n", - TpmType_str(i), be_drivers[i]->desc()); + TpmType_str(i), be_drivers[i]->desc); } fprintf(stderr, "\n"); } @@ -157,7 +157,7 @@ void tpm_cleanup(void) QLIST_FOREACH_SAFE(drv, &tpm_backends, list, next) { QLIST_REMOVE(drv, list); - tpm_backend_destroy(drv); + object_unref(OBJECT(drv)); } } @@ -172,7 +172,6 @@ int tpm_init(void) return -1; } - atexit(tpm_cleanup); return 0; } @@ -202,36 +201,6 @@ static const TPMDriverOps *tpm_driver_find_by_type(enum TpmType type) return be_drivers[type]; } -static TPMInfo *qmp_query_tpm_inst(TPMBackend *drv) -{ - TPMInfo *res = g_new0(TPMInfo, 1); - TPMPassthroughOptions *tpo; - - res->id = g_strdup(drv->id); - res->model = drv->fe_model; - res->options = g_new0(TpmTypeOptions, 1); - - switch (drv->ops->type) { - case TPM_TYPE_PASSTHROUGH: - res->options->type = TPM_TYPE_OPTIONS_KIND_PASSTHROUGH; - tpo = g_new0(TPMPassthroughOptions, 1); - res->options->u.passthrough.data = tpo; - if (drv->path) { - tpo->path = g_strdup(drv->path); - tpo->has_path = true; - } - if (drv->cancel_path) { - tpo->cancel_path = g_strdup(drv->cancel_path); - tpo->has_cancel_path = true; - } - break; - case TPM_TYPE__MAX: - break; - } - - return res; -} - /* * Walk the list of active TPM backends and collect information about them * following the schema description in qapi-schema.json. @@ -246,7 +215,7 @@ TPMInfoList *qmp_query_tpm(Error **errp) continue; } info = g_new0(TPMInfoList, 1); - info->value = qmp_query_tpm_inst(drv); + info->value = tpm_backend_query_tpm(drv); if (!cur_item) { head = cur_item = info; @@ -4905,6 +4905,7 @@ int main(int argc, char **argv, char **envp) res_free(); /* vhost-user must be cleaned up before chardevs. */ + tpm_cleanup(); net_cleanup(); audio_cleanup(); monitor_cleanup(); |