diff options
235 files changed, 4777 insertions, 1870 deletions
@@ -234,9 +234,9 @@ util/module.o-cflags = -D'CONFIG_BLOCK_MODULES=$(block-modules)' qemu-img.o: qemu-img-cmds.h -qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a -qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a -qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a +qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) libqemuutil.a libqemustub.a qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o diff --git a/block/nbd-client.c b/block/nbd-client.c index 568c56cbb6..6a9b4c73d7 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "nbd-client.h" -#include "qemu/sockets.h" #define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs)) #define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs)) @@ -48,13 +47,21 @@ static void nbd_teardown_connection(BlockDriverState *bs) { NbdClientSession *client = nbd_get_client_session(bs); + if (!client->ioc) { /* Already closed */ + return; + } + /* finish any pending coroutines */ - shutdown(client->sock, 2); + qio_channel_shutdown(client->ioc, + QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); nbd_recv_coroutines_enter_all(client); nbd_client_detach_aio_context(bs); - closesocket(client->sock); - client->sock = -1; + object_unref(OBJECT(client->sioc)); + client->sioc = NULL; + object_unref(OBJECT(client->ioc)); + client->ioc = NULL; } static void nbd_reply_ready(void *opaque) @@ -64,12 +71,16 @@ static void nbd_reply_ready(void *opaque) uint64_t i; int ret; + if (!s->ioc) { /* Already closed */ + return; + } + if (s->reply.handle == 0) { /* No reply already in flight. Fetch a header. It is possible * that another thread has done the same thing in parallel, so * the socket is not readable anymore. */ - ret = nbd_receive_reply(s->sock, &s->reply); + ret = nbd_receive_reply(s->ioc, &s->reply); if (ret == -EAGAIN) { return; } @@ -120,32 +131,35 @@ static int nbd_co_send_request(BlockDriverState *bs, } } + g_assert(qemu_in_coroutine()); assert(i < MAX_NBD_REQUESTS); request->handle = INDEX_TO_HANDLE(s, i); + + if (!s->ioc) { + qemu_co_mutex_unlock(&s->send_mutex); + return -EPIPE; + } + s->send_coroutine = qemu_coroutine_self(); aio_context = bdrv_get_aio_context(bs); - aio_set_fd_handler(aio_context, s->sock, false, + aio_set_fd_handler(aio_context, s->sioc->fd, false, nbd_reply_ready, nbd_restart_write, bs); if (qiov) { - if (!s->is_unix) { - socket_set_cork(s->sock, 1); - } - rc = nbd_send_request(s->sock, request); + qio_channel_set_cork(s->ioc, true); + rc = nbd_send_request(s->ioc, request); if (rc >= 0) { - ret = qemu_co_sendv(s->sock, qiov->iov, qiov->niov, - offset, request->len); + ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, + offset, request->len, 0); if (ret != request->len) { rc = -EIO; } } - if (!s->is_unix) { - socket_set_cork(s->sock, 0); - } + qio_channel_set_cork(s->ioc, false); } else { - rc = nbd_send_request(s->sock, request); + rc = nbd_send_request(s->ioc, request); } - aio_set_fd_handler(aio_context, s->sock, false, + aio_set_fd_handler(aio_context, s->sioc->fd, false, nbd_reply_ready, NULL, bs); s->send_coroutine = NULL; qemu_co_mutex_unlock(&s->send_mutex); @@ -162,12 +176,13 @@ static void nbd_co_receive_reply(NbdClientSession *s, * peek at the next reply and avoid yielding if it's ours? */ qemu_coroutine_yield(); *reply = s->reply; - if (reply->handle != request->handle) { + if (reply->handle != request->handle || + !s->ioc) { reply->error = EIO; } else { if (qiov && reply->error == 0) { - ret = qemu_co_recvv(s->sock, qiov->iov, qiov->niov, - offset, request->len); + ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, + offset, request->len, 1); if (ret != request->len) { reply->error = EIO; } @@ -350,14 +365,14 @@ int nbd_client_co_discard(BlockDriverState *bs, int64_t sector_num, void nbd_client_detach_aio_context(BlockDriverState *bs) { aio_set_fd_handler(bdrv_get_aio_context(bs), - nbd_get_client_session(bs)->sock, + nbd_get_client_session(bs)->sioc->fd, false, NULL, NULL, NULL); } void nbd_client_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { - aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sock, + aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd, false, nbd_reply_ready, NULL, bs); } @@ -370,16 +385,20 @@ void nbd_client_close(BlockDriverState *bs) .len = 0 }; - if (client->sock == -1) { + if (client->ioc == NULL) { return; } - nbd_send_request(client->sock, &request); + nbd_send_request(client->ioc, &request); nbd_teardown_connection(bs); } -int nbd_client_init(BlockDriverState *bs, int sock, const char *export, +int nbd_client_init(BlockDriverState *bs, + QIOChannelSocket *sioc, + const char *export, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp) { NbdClientSession *client = nbd_get_client_session(bs); @@ -387,22 +406,32 @@ int nbd_client_init(BlockDriverState *bs, int sock, const char *export, /* NBD handshake */ logout("session init %s\n", export); - qemu_set_block(sock); - ret = nbd_receive_negotiate(sock, export, - &client->nbdflags, &client->size, errp); + qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); + + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, + &client->nbdflags, + tlscreds, hostname, + &client->ioc, + &client->size, errp); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); - closesocket(sock); return ret; } qemu_co_mutex_init(&client->send_mutex); qemu_co_mutex_init(&client->free_sema); - client->sock = sock; + client->sioc = sioc; + object_ref(OBJECT(client->sioc)); + + if (!client->ioc) { + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); + } /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ - qemu_set_nonblock(sock); + qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); + nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); logout("Established connection with NBD server\n"); diff --git a/block/nbd-client.h b/block/nbd-client.h index e8413408b5..53f116d017 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -4,6 +4,7 @@ #include "qemu-common.h" #include "block/nbd.h" #include "block/block_int.h" +#include "io/channel-socket.h" /* #define DEBUG_NBD */ @@ -17,7 +18,8 @@ #define MAX_NBD_REQUESTS 16 typedef struct NbdClientSession { - int sock; + QIOChannelSocket *sioc; /* The master data channel */ + QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ uint32_t nbdflags; off_t size; @@ -34,7 +36,11 @@ typedef struct NbdClientSession { NbdClientSession *nbd_get_client_session(BlockDriverState *bs); -int nbd_client_init(BlockDriverState *bs, int sock, const char *export_name, +int nbd_client_init(BlockDriverState *bs, + QIOChannelSocket *sock, + const char *export_name, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp); void nbd_client_close(BlockDriverState *bs); diff --git a/block/nbd.c b/block/nbd.c index 1a90bc7855..db57b4951c 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -31,7 +31,6 @@ #include "qemu/uri.h" #include "block/block_int.h" #include "qemu/module.h" -#include "qemu/sockets.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qint.h" @@ -238,55 +237,113 @@ NbdClientSession *nbd_get_client_session(BlockDriverState *bs) return &s->client; } -static int nbd_establish_connection(BlockDriverState *bs, - SocketAddress *saddr, - Error **errp) +static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, + Error **errp) { - BDRVNBDState *s = bs->opaque; - int sock; + QIOChannelSocket *sioc; + Error *local_err = NULL; - sock = socket_connect(saddr, errp, NULL, NULL); + sioc = qio_channel_socket_new(); - if (sock < 0) { - logout("Failed to establish connection to NBD server\n"); - return -EIO; + qio_channel_socket_connect_sync(sioc, + saddr, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; } - if (!s->client.is_unix) { - socket_set_nodelay(sock); + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + + return sioc; +} + + +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return NULL; + } + creds = (QCryptoTLSCreds *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); + if (!creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return NULL; } - return sock; + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "Expecting TLS credentials with a client endpoint"); + return NULL; + } + object_ref(obj); + return creds; } + static int nbd_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVNBDState *s = bs->opaque; char *export = NULL; - int result, sock; + QIOChannelSocket *sioc = NULL; SocketAddress *saddr; + const char *tlscredsid; + QCryptoTLSCreds *tlscreds = NULL; + const char *hostname = NULL; + int ret = -EINVAL; /* Pop the config into our state object. Exit if invalid. */ saddr = nbd_config(s, options, &export, errp); if (!saddr) { - return -EINVAL; + goto error; + } + + tlscredsid = g_strdup(qdict_get_try_str(options, "tls-creds")); + if (tlscredsid) { + qdict_del(options, "tls-creds"); + tlscreds = nbd_get_tls_creds(tlscredsid, errp); + if (!tlscreds) { + goto error; + } + + if (saddr->type != SOCKET_ADDRESS_KIND_INET) { + error_setg(errp, "TLS only supported over IP sockets"); + goto error; + } + hostname = saddr->u.inet->host; } /* establish TCP connection, return error if it fails * TODO: Configurable retry-until-timeout behaviour. */ - sock = nbd_establish_connection(bs, saddr, errp); - qapi_free_SocketAddress(saddr); - if (sock < 0) { - g_free(export); - return sock; + sioc = nbd_establish_connection(saddr, errp); + if (!sioc) { + ret = -ECONNREFUSED; + goto error; } /* NBD handshake */ - result = nbd_client_init(bs, sock, export, errp); + ret = nbd_client_init(bs, sioc, export, + tlscreds, hostname, errp); + error: + if (sioc) { + object_unref(OBJECT(sioc)); + } + if (tlscreds) { + object_unref(OBJECT(tlscreds)); + } + qapi_free_SocketAddress(saddr); g_free(export); - return result; + return ret; } static int nbd_co_readv(BlockDriverState *bs, int64_t sector_num, @@ -348,6 +405,7 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) const char *host = qdict_get_try_str(options, "host"); const char *port = qdict_get_try_str(options, "port"); const char *export = qdict_get_try_str(options, "export"); + const char *tlscreds = qdict_get_try_str(options, "tls-creds"); qdict_put_obj(opts, "driver", QOBJECT(qstring_from_str("nbd"))); @@ -382,6 +440,9 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) if (export) { qdict_put_obj(opts, "export", QOBJECT(qstring_from_str(export))); } + if (tlscreds) { + qdict_put_obj(opts, "tls-creds", QOBJECT(qstring_from_str(tlscreds))); + } bs->full_open_options = opts; } diff --git a/blockdev-nbd.c b/blockdev-nbd.c index efc31a462c..12cae0ea72 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -18,32 +18,128 @@ #include "qmp-commands.h" #include "trace.h" #include "block/nbd.h" -#include "qemu/sockets.h" +#include "io/channel-socket.h" -static int server_fd = -1; +typedef struct NBDServerData { + QIOChannelSocket *listen_ioc; + int watch; + QCryptoTLSCreds *tlscreds; +} NBDServerData; -static void nbd_accept(void *opaque) +static NBDServerData *nbd_server; + + +static gboolean nbd_accept(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) +{ + QIOChannelSocket *cioc; + + if (!nbd_server) { + return FALSE; + } + + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; + } + + nbd_client_new(NULL, cioc, + nbd_server->tlscreds, NULL, + nbd_client_put); + object_unref(OBJECT(cioc)); + return TRUE; +} + + +static void nbd_server_free(NBDServerData *server) +{ + if (!server) { + return; + } + + if (server->watch != -1) { + g_source_remove(server->watch); + } + object_unref(OBJECT(server->listen_ioc)); + if (server->tlscreds) { + object_unref(OBJECT(server->tlscreds)); + } + + g_free(server); +} + +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + Object *obj; + QCryptoTLSCreds *creds; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return NULL; + } + creds = (QCryptoTLSCreds *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); + if (!creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return NULL; + } - int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - if (fd >= 0) { - nbd_client_new(NULL, fd, nbd_client_put); + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + return NULL; } + object_ref(obj); + return creds; } -void qmp_nbd_server_start(SocketAddress *addr, Error **errp) + +void qmp_nbd_server_start(SocketAddress *addr, + bool has_tls_creds, const char *tls_creds, + Error **errp) { - if (server_fd != -1) { + if (nbd_server) { error_setg(errp, "NBD server already running"); return; } - server_fd = socket_listen(addr, errp); - if (server_fd != -1) { - qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL); + nbd_server = g_new0(NBDServerData, 1); + nbd_server->watch = -1; + nbd_server->listen_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync( + nbd_server->listen_ioc, addr, errp) < 0) { + goto error; } + + if (has_tls_creds) { + nbd_server->tlscreds = nbd_get_tls_creds(tls_creds, errp); + if (!nbd_server->tlscreds) { + goto error; + } + + if (addr->type != SOCKET_ADDRESS_KIND_INET) { + error_setg(errp, "TLS is only supported with IPv4/IPv6"); + goto error; + } + } + + nbd_server->watch = qio_channel_add_watch( + QIO_CHANNEL(nbd_server->listen_ioc), + G_IO_IN, + nbd_accept, + NULL, + NULL); + + return; + + error: + nbd_server_free(nbd_server); + nbd_server = NULL; } void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, @@ -52,7 +148,7 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, BlockBackend *blk; NBDExport *exp; - if (server_fd == -1) { + if (!nbd_server) { error_setg(errp, "NBD server not running"); return; } @@ -98,9 +194,6 @@ void qmp_nbd_server_stop(Error **errp) { nbd_export_close_all(); - if (server_fd != -1) { - qemu_set_fd_handler(server_fd, NULL, NULL, NULL); - close(server_fd); - server_fd = -1; - } + nbd_server_free(nbd_server); + nbd_server = NULL; } @@ -30,6 +30,7 @@ #include "qapi/string-output-visitor.h" #include "qapi/util.h" #include "qapi-visit.h" +#include "qom/object_interfaces.h" #include "ui/console.h" #include "block/qapi.h" #include "qemu-io.h" @@ -1655,58 +1656,27 @@ void hmp_netdev_del(Monitor *mon, const QDict *qdict) void hmp_object_add(Monitor *mon, const QDict *qdict) { Error *err = NULL; - Error *err_end = NULL; QemuOpts *opts; - char *type = NULL; - char *id = NULL; OptsVisitor *ov; - QDict *pdict; - Visitor *v; + Object *obj = NULL; opts = qemu_opts_from_qdict(qemu_find_opts("object"), qdict, &err); if (err) { - goto out; + hmp_handle_error(mon, &err); + return; } ov = opts_visitor_new(opts); - pdict = qdict_clone_shallow(qdict); - v = opts_get_visitor(ov); - - visit_start_struct(v, NULL, NULL, 0, &err); - if (err) { - goto out_clean; - } - - qdict_del(pdict, "qom-type"); - visit_type_str(v, "qom-type", &type, &err); - if (err) { - goto out_end; - } + obj = user_creatable_add(qdict, opts_get_visitor(ov), &err); + opts_visitor_cleanup(ov); + qemu_opts_del(opts); - qdict_del(pdict, "id"); - visit_type_str(v, "id", &id, &err); if (err) { - goto out_end; + hmp_handle_error(mon, &err); } - - object_add(type, id, pdict, v, &err); - -out_end: - visit_end_struct(v, &err_end); - if (!err && err_end) { - qmp_object_del(id, NULL); + if (obj) { + object_unref(obj); } - error_propagate(&err, err_end); -out_clean: - opts_visitor_cleanup(ov); - - QDECREF(pdict); - qemu_opts_del(opts); - g_free(id); - g_free(type); - -out: - hmp_handle_error(mon, &err); } void hmp_getfd(Monitor *mon, const QDict *qdict) @@ -1823,7 +1793,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - qmp_nbd_server_start(addr, &local_err); + qmp_nbd_server_start(addr, false, NULL, &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; @@ -1934,7 +1904,7 @@ void hmp_object_del(Monitor *mon, const QDict *qdict) const char *id = qdict_get_str(qdict, "id"); Error *err = NULL; - qmp_object_del(id, &err); + user_creatable_del(id, &err); hmp_handle_error(mon, &err); } diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index e4fb48b803..72467fd907 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -8,6 +8,7 @@ * This code is licensed under the GNU GPLv2 and later. */ +#include "qemu/osdep.h" #include "hw/arm/bcm2835_peripherals.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "hw/arm/raspi_platform.h" diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 8a4d13c7d9..15c7622ad1 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -8,6 +8,7 @@ * This code is licensed under the GNU GPLv2 and later. */ +#include "qemu/osdep.h" #include "hw/arm/bcm2836.h" #include "hw/arm/raspi_platform.h" #include "hw/sysbus.h" diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index 7d3d21ab32..48d014c8d3 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -8,6 +8,7 @@ * This code is licensed under the GNU GPLv2 and later. */ +#include "qemu/osdep.h" #include "hw/arm/bcm2836.h" #include "qemu/error-report.h" #include "hw/boards.h" diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 66e7f27ace..a35983a9ec 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -28,6 +28,7 @@ #include "hw/misc/zynq-xadc.h" #include "hw/ssi/ssi.h" #include "qemu/error-report.h" +#include "hw/sd/sd.h" #define NUM_SPI_FLASHES 4 #define NUM_QSPI_FLASHES 2 @@ -154,8 +155,10 @@ static void zynq_init(MachineState *machine) MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ext_ram = g_new(MemoryRegion, 1); MemoryRegion *ocm_ram = g_new(MemoryRegion, 1); - DeviceState *dev; + DeviceState *dev, *carddev; SysBusDevice *busdev; + DriveInfo *di; + BlockBackend *blk; qemu_irq pic[64]; int n; @@ -245,11 +248,23 @@ static void zynq_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0100000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[56-IRQ_OFFSET]); + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + dev = qdev_create(NULL, "generic-sdhci"); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xE0101000); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic[79-IRQ_OFFSET]); + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", &error_fatal); + dev = qdev_create(NULL, TYPE_ZYNQ_XADC); qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8007100); diff --git a/hw/arm/xlnx-ep108.c b/hw/arm/xlnx-ep108.c index 0de132abd2..a1bd283a52 100644 --- a/hw/arm/xlnx-ep108.c +++ b/hw/arm/xlnx-ep108.c @@ -59,6 +59,27 @@ static void xlnx_ep108_init(MachineState *machine) object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + /* Create and plug in the SD cards */ + for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + BusState *bus; + DriveInfo *di = drive_get_next(IF_SD); + BlockBackend *blk = di ? blk_by_legacy_dinfo(di) : NULL; + DeviceState *carddev; + char *bus_name; + + bus_name = g_strdup_printf("sd-bus%d", i); + bus = qdev_get_child_bus(DEVICE(&s->soc), bus_name); + g_free(bus_name); + if (!bus) { + error_report("No SD bus found for SD card %d", i); + exit(1); + } + carddev = qdev_create(bus, TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &error_fatal); + object_property_set_bool(OBJECT(carddev), true, "realized", + &error_fatal); + } + for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { SSIBus *spi_bus; DeviceState *flash_dev; diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 1508d0867d..4fbb63550b 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -327,6 +327,8 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->sata), 0, gic_spi[SATA_INTR]); for (i = 0; i < XLNX_ZYNQMP_NUM_SDHCI; i++) { + char *bus_name; + object_property_set_bool(OBJECT(&s->sdhci[i]), true, "realized", &err); if (err) { @@ -337,6 +339,12 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sdhci_addr[i]); sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci[i]), 0, gic_spi[sdhci_intr[i]]); + /* Alias controller SD bus to the SoC itself */ + bus_name = g_strdup_printf("sd-bus%d", i); + object_property_add_alias(OBJECT(s), bus_name, + OBJECT(&s->sdhci[i]), "sd-bus", + &error_abort); + g_free(bus_name); } for (i = 0; i < XLNX_ZYNQMP_NUM_SPIS; i++) { diff --git a/hw/block/nand.c b/hw/block/nand.c index 478e1a6b3f..f51e13fcac 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -18,8 +18,9 @@ #ifndef NAND_IO -# include "hw/hw.h" -# include "hw/block/flash.h" +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/block/flash.h" #include "sysemu/block-backend.h" #include "hw/qdev.h" #include "qemu/error-report.h" diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index e5a696ef43..5ece8b068e 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -56,7 +56,6 @@ typedef struct PL061State { uint32_t slr; uint32_t den; uint32_t cr; - uint32_t float_high; uint32_t amsel; qemu_irq irq; qemu_irq out[8]; @@ -65,8 +64,8 @@ typedef struct PL061State { static const VMStateDescription vmstate_pl061 = { .name = "pl061", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (VMStateField[]) { VMSTATE_UINT32(locked, PL061State), VMSTATE_UINT32(data, PL061State), @@ -88,7 +87,6 @@ static const VMStateDescription vmstate_pl061 = { VMSTATE_UINT32(slr, PL061State), VMSTATE_UINT32(den, PL061State), VMSTATE_UINT32(cr, PL061State), - VMSTATE_UINT32(float_high, PL061State), VMSTATE_UINT32_V(amsel, PL061State, 2), VMSTATE_END_OF_LIST() } @@ -282,10 +280,32 @@ static void pl061_write(void *opaque, hwaddr offset, pl061_update(s); } -static void pl061_reset(PL061State *s) +static void pl061_reset(DeviceState *dev) { - s->locked = 1; - s->cr = 0xff; + PL061State *s = PL061(dev); + + /* reset values from PL061 TRM, Stellaris LM3S5P31 & LM3S8962 Data Sheet */ + s->data = 0; + s->old_out_data = 0; + s->old_in_data = 0; + s->dir = 0; + s->isense = 0; + s->ibe = 0; + s->iev = 0; + s->im = 0; + s->istate = 0; + s->afsel = 0; + s->dr2r = 0xff; + s->dr4r = 0; + s->dr8r = 0; + s->odr = 0; + s->pur = 0; + s->pdr = 0; + s->slr = 0; + s->den = 0; + s->locked = 1; + s->cr = 0xff; + s->amsel = 0; } static void pl061_set_irq(void * opaque, int irq, int level) @@ -318,7 +338,7 @@ static int pl061_initfn(SysBusDevice *sbd) sysbus_init_irq(sbd, &s->irq); qdev_init_gpio_in(dev, pl061_set_irq, 8); qdev_init_gpio_out(dev, s->out, 8); - pl061_reset(s); + return 0; } @@ -343,6 +363,7 @@ static void pl061_class_init(ObjectClass *klass, void *data) k->init = pl061_initfn; dc->vmsd = &vmstate_pl061; + dc->reset = &pl061_reset; } static const TypeInfo pl061_info = { diff --git a/hw/input/adb.c b/hw/input/adb.c index c384856c13..f0ad0d4471 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -89,7 +89,7 @@ int adb_request(ADBBusState *s, uint8_t *obuf, const uint8_t *buf, int len) } /* XXX: move that to cuda ? */ -int adb_poll(ADBBusState *s, uint8_t *obuf) +int adb_poll(ADBBusState *s, uint8_t *obuf, uint16_t poll_mask) { ADBDevice *d; int olen, i; @@ -100,13 +100,15 @@ int adb_poll(ADBBusState *s, uint8_t *obuf) if (s->poll_index >= s->nb_devices) s->poll_index = 0; d = s->devices[s->poll_index]; - buf[0] = ADB_READREG | (d->devaddr << 4); - olen = adb_request(s, obuf + 1, buf, 1); - /* if there is data, we poll again the same device */ - if (olen > 0) { - obuf[0] = buf[0]; - olen++; - break; + if ((1 << d->devaddr) & poll_mask) { + buf[0] = ADB_READREG | (d->devaddr << 4); + olen = adb_request(s, obuf + 1, buf, 1); + /* if there is data, we poll again the same device */ + if (olen > 0) { + obuf[0] = buf[0]; + olen++; + break; + } } s->poll_index++; } diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 005a72b1e2..80513b28fd 100644 --- a/hw/intc/bcm2835_ic.c +++ b/hw/intc/bcm2835_ic.c @@ -12,6 +12,7 @@ * This code is licensed under the GPL. */ +#include "qemu/osdep.h" #include "hw/intc/bcm2835_ic.h" #define GPU_IRQS 64 diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index ad622aa99f..d0271810cc 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -13,6 +13,7 @@ * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf */ +#include "qemu/osdep.h" #include "hw/intc/bcm2836_control.h" #define REG_GPU_ROUTE 0x0c diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index f8b21761a2..51d234aa1b 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -534,7 +534,7 @@ static void ipmi_init_sensors_from_sdrs(IPMIBmcSim *s) continue; /* Not a sensor SDR we set from */ } - if (sdr->sensor_owner_number > MAX_SENSORS) { + if (sdr->sensor_owner_number >= MAX_SENSORS) { continue; } sens = s->sensors + sdr->sensor_owner_number; @@ -1448,7 +1448,7 @@ static void set_sensor_evt_enable(IPMIBmcSim *ibs, IPMISensor *sens; IPMI_CHECK_CMD_LEN(4); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; @@ -1500,7 +1500,7 @@ static void get_sensor_evt_enable(IPMIBmcSim *ibs, IPMISensor *sens; IPMI_CHECK_CMD_LEN(3); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; @@ -1521,7 +1521,7 @@ static void rearm_sensor_evts(IPMIBmcSim *ibs, IPMISensor *sens; IPMI_CHECK_CMD_LEN(4); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; @@ -1543,7 +1543,7 @@ static void get_sensor_evt_status(IPMIBmcSim *ibs, IPMISensor *sens; IPMI_CHECK_CMD_LEN(3); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; @@ -1565,7 +1565,7 @@ static void get_sensor_reading(IPMIBmcSim *ibs, IPMISensor *sens; IPMI_CHECK_CMD_LEN(3); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; @@ -1588,7 +1588,7 @@ static void set_sensor_type(IPMIBmcSim *ibs, IPMI_CHECK_CMD_LEN(5); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; @@ -1607,7 +1607,7 @@ static void get_sensor_type(IPMIBmcSim *ibs, IPMI_CHECK_CMD_LEN(3); - if ((cmd[2] > MAX_SENSORS) || + if ((cmd[2] >= MAX_SENSORS) || !IPMI_SENSOR_GET_PRESENT(ibs->sensors + cmd[2])) { rsp[2] = IPMI_CC_REQ_ENTRY_NOT_PRESENT; return; diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index df1d6e6ad6..500baba7c0 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -8,6 +8,7 @@ * https://github.com/raspberrypi/firmware/wiki/Accessing-mailboxes */ +#include "qemu/osdep.h" #include "hw/misc/bcm2835_mbox.h" #define MAIL0_PEEK 0x90 diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 45bd6c18ce..581922abd7 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -3,6 +3,7 @@ * This code is licensed under the GNU GPLv2 and later. */ +#include "qemu/osdep.h" #include "hw/misc/bcm2835_property.h" #include "hw/misc/bcm2835_mbox_defs.h" #include "sysemu/dma.h" diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 316c1ac8da..481abdb754 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -106,7 +106,6 @@ #define CUDA_COMBINED_FORMAT_IIC 0x25 #define CUDA_TIMER_FREQ (4700000 / 6) -#define CUDA_ADB_POLL_FREQ 50 /* CUDA returns time_t's offset from Jan 1, 1904, not 1970 */ #define RTC_OFFSET 2082844800 @@ -524,7 +523,7 @@ static void cuda_adb_poll(void *opaque) uint8_t obuf[ADB_MAX_OUT_LEN + 2]; int olen; - olen = adb_poll(&s->adb_bus, obuf + 2); + olen = adb_poll(&s->adb_bus, obuf + 2, s->adb_poll_mask); if (olen > 0) { obuf[0] = ADB_PACKET; obuf[1] = 0x40; /* polled data */ @@ -532,87 +531,213 @@ static void cuda_adb_poll(void *opaque) } timer_mod(s->adb_poll_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); + (get_ticks_per_sec() / (1000 / s->autopoll_rate_ms))); } -static void cuda_receive_packet(CUDAState *s, - const uint8_t *data, int len) +/* description of commands */ +typedef struct CudaCommand { + uint8_t command; + const char *name; + bool (*handler)(CUDAState *s, + const uint8_t *in_args, int in_len, + uint8_t *out_args, int *out_len); +} CudaCommand; + +static bool cuda_cmd_autopoll(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) { - uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] }; int autopoll; + + if (in_len != 1) { + return false; + } + + autopoll = (in_data[0] != 0); + if (autopoll != s->autopoll) { + s->autopoll = autopoll; + if (autopoll) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (get_ticks_per_sec() / (1000 / s->autopoll_rate_ms))); + } else { + timer_del(s->adb_poll_timer); + } + } + return true; +} + +static bool cuda_cmd_set_autorate(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 1) { + return false; + } + + /* we don't want a period of 0 ms */ + /* FIXME: check what real hardware does */ + if (in_data[0] == 0) { + return false; + } + + s->autopoll_rate_ms = in_data[0]; + if (s->autopoll) { + timer_mod(s->adb_poll_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (get_ticks_per_sec() / (1000 / s->autopoll_rate_ms))); + } + return true; +} + +static bool cuda_cmd_set_device_list(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 2) { + return false; + } + + s->adb_poll_mask = (((uint16_t)in_data[0]) << 8) | in_data[1]; + return true; +} + +static bool cuda_cmd_powerdown(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 0) { + return false; + } + + qemu_system_shutdown_request(); + return true; +} + +static bool cuda_cmd_reset_system(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 0) { + return false; + } + + qemu_system_reset_request(); + return true; +} + +static bool cuda_cmd_set_file_server_flag(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 1) { + return false; + } + + qemu_log_mask(LOG_UNIMP, + "CUDA: unimplemented command FILE_SERVER_FLAG %d\n", + in_data[0]); + return true; +} + +static bool cuda_cmd_set_power_message(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + if (in_len != 1) { + return false; + } + + qemu_log_mask(LOG_UNIMP, + "CUDA: unimplemented command SET_POWER_MESSAGE %d\n", + in_data[0]); + return true; +} + +static bool cuda_cmd_get_time(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ uint32_t ti; - switch(data[0]) { - case CUDA_AUTOPOLL: - autopoll = (data[1] != 0); - if (autopoll != s->autopoll) { - s->autopoll = autopoll; - if (autopoll) { - timer_mod(s->adb_poll_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (get_ticks_per_sec() / CUDA_ADB_POLL_FREQ)); + if (in_len != 0) { + return false; + } + + ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / get_ticks_per_sec()); + out_data[0] = ti >> 24; + out_data[1] = ti >> 16; + out_data[2] = ti >> 8; + out_data[3] = ti; + *out_len = 4; + return true; +} + +static bool cuda_cmd_set_time(CUDAState *s, + const uint8_t *in_data, int in_len, + uint8_t *out_data, int *out_len) +{ + uint32_t ti; + + if (in_len != 4) { + return false; + } + + ti = (((uint32_t)in_data[1]) << 24) + (((uint32_t)in_data[2]) << 16) + + (((uint32_t)in_data[3]) << 8) + in_data[4]; + s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + / get_ticks_per_sec()); + return true; +} + +static const CudaCommand handlers[] = { + { CUDA_AUTOPOLL, "AUTOPOLL", cuda_cmd_autopoll }, + { CUDA_SET_AUTO_RATE, "SET_AUTO_RATE", cuda_cmd_set_autorate }, + { CUDA_SET_DEVICE_LIST, "SET_DEVICE_LIST", cuda_cmd_set_device_list }, + { CUDA_POWERDOWN, "POWERDOWN", cuda_cmd_powerdown }, + { CUDA_RESET_SYSTEM, "RESET_SYSTEM", cuda_cmd_reset_system }, + { CUDA_FILE_SERVER_FLAG, "FILE_SERVER_FLAG", + cuda_cmd_set_file_server_flag }, + { CUDA_SET_POWER_MESSAGES, "SET_POWER_MESSAGES", + cuda_cmd_set_power_message }, + { CUDA_GET_TIME, "GET_TIME", cuda_cmd_get_time }, + { CUDA_SET_TIME, "SET_TIME", cuda_cmd_set_time }, +}; + +static void cuda_receive_packet(CUDAState *s, + const uint8_t *data, int len) +{ + uint8_t obuf[16] = { CUDA_PACKET, 0, data[0] }; + int i, out_len = 0; + + for (i = 0; i < ARRAY_SIZE(handlers); i++) { + const CudaCommand *desc = &handlers[i]; + if (desc->command == data[0]) { + CUDA_DPRINTF("handling command %s\n", desc->name); + out_len = 0; + if (desc->handler(s, data + 1, len - 1, obuf + 3, &out_len)) { + cuda_send_packet_to_host(s, obuf, 3 + out_len); } else { - timer_del(s->adb_poll_timer); + qemu_log_mask(LOG_GUEST_ERROR, + "CUDA: %s: wrong parameters %d\n", + desc->name, len); + obuf[0] = ERROR_PACKET; + obuf[1] = 0x5; /* bad parameters */ + obuf[2] = CUDA_PACKET; + obuf[3] = data[0]; + cuda_send_packet_to_host(s, obuf, 4); } + return; } - cuda_send_packet_to_host(s, obuf, 3); - break; - case CUDA_GET_6805_ADDR: - cuda_send_packet_to_host(s, obuf, 3); - break; - case CUDA_SET_TIME: - ti = (((uint32_t)data[1]) << 24) + (((uint32_t)data[2]) << 16) + (((uint32_t)data[3]) << 8) + data[4]; - s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec()); - cuda_send_packet_to_host(s, obuf, 3); - break; - case CUDA_GET_TIME: - ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / get_ticks_per_sec()); - obuf[3] = ti >> 24; - obuf[4] = ti >> 16; - obuf[5] = ti >> 8; - obuf[6] = ti; - cuda_send_packet_to_host(s, obuf, 7); - break; - case CUDA_FILE_SERVER_FLAG: - case CUDA_SET_DEVICE_LIST: - case CUDA_SET_AUTO_RATE: - case CUDA_SET_POWER_MESSAGES: - cuda_send_packet_to_host(s, obuf, 3); - break; - case CUDA_POWERDOWN: - cuda_send_packet_to_host(s, obuf, 3); - qemu_system_shutdown_request(); - break; - case CUDA_RESET_SYSTEM: - cuda_send_packet_to_host(s, obuf, 3); - qemu_system_reset_request(); - break; - case CUDA_COMBINED_FORMAT_IIC: - obuf[0] = ERROR_PACKET; - obuf[1] = 0x5; - obuf[2] = CUDA_PACKET; - obuf[3] = data[0]; - cuda_send_packet_to_host(s, obuf, 4); - break; - case CUDA_GET_SET_IIC: - if (len == 4) { - cuda_send_packet_to_host(s, obuf, 3); - } else { - obuf[0] = ERROR_PACKET; - obuf[1] = 0x2; - obuf[2] = CUDA_PACKET; - obuf[3] = data[0]; - cuda_send_packet_to_host(s, obuf, 4); - } - break; - default: - obuf[0] = ERROR_PACKET; - obuf[1] = 0x2; - obuf[2] = CUDA_PACKET; - obuf[3] = data[0]; - cuda_send_packet_to_host(s, obuf, 4); - break; } + + qemu_log_mask(LOG_GUEST_ERROR, "CUDA: unknown command 0x%02x\n", data[0]); + obuf[0] = ERROR_PACKET; + obuf[1] = 0x2; /* unknown command */ + obuf[2] = CUDA_PACKET; + obuf[3] = data[0]; + cuda_send_packet_to_host(s, obuf, 4); } static void cuda_receive_packet_from_host(CUDAState *s, @@ -710,8 +835,8 @@ static const VMStateDescription vmstate_cuda_timer = { static const VMStateDescription vmstate_cuda = { .name = "cuda", - .version_id = 3, - .minimum_version_id = 3, + .version_id = 4, + .minimum_version_id = 4, .fields = (VMStateField[]) { VMSTATE_UINT8(a, CUDAState), VMSTATE_UINT8(b, CUDAState), @@ -729,6 +854,8 @@ static const VMStateDescription vmstate_cuda = { VMSTATE_INT32(data_in_index, CUDAState), VMSTATE_INT32(data_out_index, CUDAState), VMSTATE_UINT8(autopoll, CUDAState), + VMSTATE_UINT8(autopoll_rate_ms, CUDAState), + VMSTATE_UINT16(adb_poll_mask, CUDAState), VMSTATE_BUFFER(data_in, CUDAState), VMSTATE_BUFFER(data_out, CUDAState), VMSTATE_UINT32(tick_offset, CUDAState), @@ -782,6 +909,8 @@ static void cuda_realizefn(DeviceState *dev, Error **errp) s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); + s->autopoll_rate_ms = 20; + s->adb_poll_mask = 0xffff; } static void cuda_initfn(Object *obj) diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 564ef936d1..1671f4686e 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -49,7 +49,8 @@ static void macio_nvram_writeb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); s->data[addr] = value; - NVR_DPRINTF("writeb addr %04" PHYS_PRIx " val %" PRIx64 "\n", addr, value); + NVR_DPRINTF("writeb addr %04" HWADDR_PRIx " val %" PRIx64 "\n", + addr, value); } static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, @@ -60,7 +61,8 @@ static uint64_t macio_nvram_readb(void *opaque, hwaddr addr, addr = (addr >> s->it_shift) & (s->size - 1); value = s->data[addr]; - NVR_DPRINTF("readb addr %04x val %x\n", (int)addr, value); + NVR_DPRINTF("readb addr %04" HWADDR_PRIx " val %" PRIx32 "\n", + addr, value); return value; } diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 40a2e3e6b4..15b1054232 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -120,7 +120,7 @@ static void unin_data_write(void *opaque, hwaddr addr, { UNINState *s = opaque; PCIHostState *phb = PCI_HOST_BRIDGE(s); - UNIN_DPRINTF("write addr %" TARGET_FMT_plx " len %d val %"PRIx64"\n", + UNIN_DPRINTF("write addr " TARGET_FMT_plx " len %d val %"PRIx64"\n", addr, len, val); pci_data_write(phb->bus, unin_get_config_reg(phb->config_reg, addr), @@ -137,7 +137,7 @@ static uint64_t unin_data_read(void *opaque, hwaddr addr, val = pci_data_read(phb->bus, unin_get_config_reg(phb->config_reg, addr), len); - UNIN_DPRINTF("read addr %" TARGET_FMT_plx " len %d val %x\n", + UNIN_DPRINTF("read addr " TARGET_FMT_plx " len %d val %x\n", addr, len, val); return val; } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index ecf7792bc9..5764b86c28 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -111,6 +111,8 @@ typedef struct CUDAState { int data_out_index; qemu_irq irq; + uint16_t adb_poll_mask; + uint8_t autopoll_rate_ms; uint8_t autopoll; uint8_t data_in[128]; uint8_t data_out[16]; diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 5bd8fd3ef8..e214a34257 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1024,83 +1024,93 @@ static void emulate_spapr_hypercall(PowerPCCPU *cpu) #define CLEAN_HPTE(_hpte) ((*(uint64_t *)(_hpte)) &= tswap64(~HPTE64_V_HPTE_DIRTY)) #define DIRTY_HPTE(_hpte) ((*(uint64_t *)(_hpte)) |= tswap64(HPTE64_V_HPTE_DIRTY)) -static void spapr_alloc_htab(sPAPRMachineState *spapr) +/* + * Get the fd to access the kernel htab, re-opening it if necessary + */ +static int get_htab_fd(sPAPRMachineState *spapr) { - long shift; - int index; + if (spapr->htab_fd >= 0) { + return spapr->htab_fd; + } - /* allocate hash page table. For now we always make this 16mb, - * later we should probably make it scale to the size of guest - * RAM */ + spapr->htab_fd = kvmppc_get_htab_fd(false); + if (spapr->htab_fd < 0) { + error_report("Unable to open fd for reading hash table from KVM: %s", + strerror(errno)); + } - shift = kvmppc_reset_htab(spapr->htab_shift); - if (shift < 0) { - /* - * For HV KVM, host kernel will return -ENOMEM when requested - * HTAB size can't be allocated. - */ - error_setg(&error_abort, "Failed to allocate HTAB of requested size, try with smaller maxmem"); - } else if (shift > 0) { - /* - * Kernel handles htab, we don't need to allocate one - * - * Older kernels can fall back to lower HTAB shift values, - * but we don't allow booting of such guests. - */ - if (shift != spapr->htab_shift) { - error_setg(&error_abort, "Failed to allocate HTAB of requested size, try with smaller maxmem"); - } + return spapr->htab_fd; +} - spapr->htab_shift = shift; - kvmppc_kern_htab = true; - } else { - /* Allocate htab */ - spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr)); +static void close_htab_fd(sPAPRMachineState *spapr) +{ + if (spapr->htab_fd >= 0) { + close(spapr->htab_fd); + } + spapr->htab_fd = -1; +} - /* And clear it */ - memset(spapr->htab, 0, HTAB_SIZE(spapr)); +static int spapr_hpt_shift_for_ramsize(uint64_t ramsize) +{ + int shift; - for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { - DIRTY_HPTE(HPTE(spapr->htab, index)); - } - } + /* We aim for a hash table of size 1/128 the size of RAM (rounded + * up). The PAPR recommendation is actually 1/64 of RAM size, but + * that's much more than is needed for Linux guests */ + shift = ctz64(pow2ceil(ramsize)) - 7; + shift = MAX(shift, 18); /* Minimum architected size */ + shift = MIN(shift, 46); /* Maximum architected size */ + return shift; } -/* - * Clear HTAB entries during reset. - * - * If host kernel has allocated HTAB, KVM_PPC_ALLOCATE_HTAB ioctl is - * used to clear HTAB. Otherwise QEMU-allocated HTAB is cleared manually. - */ -static void spapr_reset_htab(sPAPRMachineState *spapr) +static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, + Error **errp) { - long shift; - int index; + long rc; - shift = kvmppc_reset_htab(spapr->htab_shift); - if (shift < 0) { - error_setg(&error_abort, "Failed to reset HTAB"); - } else if (shift > 0) { - if (shift != spapr->htab_shift) { - error_setg(&error_abort, "Requested HTAB allocation failed during reset"); - } + /* Clean up any HPT info from a previous boot */ + g_free(spapr->htab); + spapr->htab = NULL; + spapr->htab_shift = 0; + close_htab_fd(spapr); - /* Tell readers to update their file descriptor */ - if (spapr->htab_fd >= 0) { - spapr->htab_fd_stale = true; + rc = kvmppc_reset_htab(shift); + if (rc < 0) { + /* kernel-side HPT needed, but couldn't allocate one */ + error_setg_errno(errp, errno, + "Failed to allocate KVM HPT of order %d (try smaller maxmem?)", + shift); + /* This is almost certainly fatal, but if the caller really + * wants to carry on with shift == 0, it's welcome to try */ + } else if (rc > 0) { + /* kernel-side HPT allocated */ + if (rc != shift) { + error_setg(errp, + "Requested order %d HPT, but kernel allocated order %ld (try smaller maxmem?)", + shift, rc); } + + spapr->htab_shift = shift; + kvmppc_kern_htab = true; } else { - memset(spapr->htab, 0, HTAB_SIZE(spapr)); + /* kernel-side HPT not needed, allocate in userspace instead */ + size_t size = 1ULL << shift; + int i; - for (index = 0; index < HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; index++) { - DIRTY_HPTE(HPTE(spapr->htab, index)); + spapr->htab = qemu_memalign(size, size); + if (!spapr->htab) { + error_setg_errno(errp, errno, + "Could not allocate HPT of order %d", shift); + return; } - } - /* Update the RMA size if necessary */ - if (spapr->vrma_adjust) { - spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), - spapr->htab_shift); + memset(spapr->htab, 0, size); + spapr->htab_shift = shift; + kvmppc_kern_htab = false; + + for (i = 0; i < size / HASH_PTE_SIZE_64; i++) { + DIRTY_HPTE(HPTE(spapr->htab, i)); + } } } @@ -1121,39 +1131,26 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) return 0; } -/* - * A guest reset will cause spapr->htab_fd to become stale if being used. - * Reopen the file descriptor to make sure the whole HTAB is properly read. - */ -static int spapr_check_htab_fd(sPAPRMachineState *spapr) -{ - int rc = 0; - - if (spapr->htab_fd_stale) { - close(spapr->htab_fd); - spapr->htab_fd = kvmppc_get_htab_fd(false); - if (spapr->htab_fd < 0) { - error_report("Unable to open fd for reading hash table from KVM: " - "%s", strerror(errno)); - rc = -1; - } - spapr->htab_fd_stale = false; - } - - return rc; -} - static void ppc_spapr_reset(void) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + MachineState *machine = MACHINE(qdev_get_machine()); + sPAPRMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; /* Check for unknown sysbus devices */ foreach_dynamic_sysbus_device(find_unknown_sysbus_device, NULL); - /* Reset the hash table & recalc the RMA */ - spapr_reset_htab(spapr); + /* Allocate and/or reset the hash page table */ + spapr_reallocate_hpt(spapr, + spapr_hpt_shift_for_ramsize(machine->maxram_size), + &error_fatal); + + /* Update the RMA size if necessary */ + if (spapr->vrma_adjust) { + spapr->rma_size = kvmppc_rma_size(spapr_node0_size(), + spapr->htab_shift); + } qemu_devices_reset(); @@ -1200,13 +1197,6 @@ static void spapr_cpu_reset(void *opaque) env->spr[SPR_HIOR] = 0; env->external_htab = (uint8_t *)spapr->htab; - if (kvm_enabled() && !env->external_htab) { - /* - * HV KVM, set external_htab to 1 so our ppc_hash64_load_hpte* - * functions do the right thing. - */ - env->external_htab = (void *)1; - } env->htab_base = -1; /* * htab_mask is the mask used to normalize hash value to PTEG index. @@ -1313,14 +1303,6 @@ static int htab_save_setup(QEMUFile *f, void *opaque) spapr->htab_first_pass = true; } else { assert(kvm_enabled()); - - spapr->htab_fd = kvmppc_get_htab_fd(false); - spapr->htab_fd_stale = false; - if (spapr->htab_fd < 0) { - fprintf(stderr, "Unable to open fd for reading hash table from KVM: %s\n", - strerror(errno)); - return -1; - } } @@ -1330,6 +1312,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque) static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, int64_t max_ns) { + bool has_timeout = max_ns != -1; int htabslots = HTAB_SIZE(spapr) / HASH_PTE_SIZE_64; int index = spapr->htab_save_index; int64_t starttime = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -1363,7 +1346,8 @@ static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, qemu_put_buffer(f, HPTE(spapr->htab, chunkstart), HASH_PTE_SIZE_64 * n_valid); - if ((qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { + if (has_timeout && + (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - starttime) > max_ns) { break; } } @@ -1460,6 +1444,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr, static int htab_save_iterate(QEMUFile *f, void *opaque) { sPAPRMachineState *spapr = opaque; + int fd; int rc = 0; /* Iteration header */ @@ -1468,13 +1453,12 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) if (!spapr->htab) { assert(kvm_enabled()); - rc = spapr_check_htab_fd(spapr); - if (rc < 0) { - return rc; + fd = get_htab_fd(spapr); + if (fd < 0) { + return fd; } - rc = kvmppc_save_htab(f, spapr->htab_fd, - MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); + rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, MAX_ITERATION_NS); if (rc < 0) { return rc; } @@ -1495,6 +1479,7 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) static int htab_save_complete(QEMUFile *f, void *opaque) { sPAPRMachineState *spapr = opaque; + int fd; /* Iteration header */ qemu_put_be32(f, 0); @@ -1504,18 +1489,20 @@ static int htab_save_complete(QEMUFile *f, void *opaque) assert(kvm_enabled()); - rc = spapr_check_htab_fd(spapr); - if (rc < 0) { - return rc; + fd = get_htab_fd(spapr); + if (fd < 0) { + return fd; } - rc = kvmppc_save_htab(f, spapr->htab_fd, MAX_KVM_BUF_SIZE, -1); + rc = kvmppc_save_htab(f, fd, MAX_KVM_BUF_SIZE, -1); if (rc < 0) { return rc; } - close(spapr->htab_fd); - spapr->htab_fd = -1; + close_htab_fd(spapr); } else { + if (spapr->htab_first_pass) { + htab_save_first_pass(f, spapr, -1); + } htab_save_later_pass(f, spapr, -1); } @@ -1541,10 +1528,12 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) section_hdr = qemu_get_be32(f); if (section_hdr) { - /* First section, just the hash shift */ - if (spapr->htab_shift != section_hdr) { - error_report("htab_shift mismatch: source %d target %d", - section_hdr, spapr->htab_shift); + Error *local_err; + + /* First section gives the htab size */ + spapr_reallocate_hpt(spapr, section_hdr, &local_err); + if (local_err) { + error_report_err(local_err); return -EINVAL; } return 0; @@ -1797,18 +1786,6 @@ static void ppc_spapr_init(MachineState *machine) /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; - /* We aim for a hash table of size 1/128 the size of RAM. The - * normal rule of thumb is 1/64 the size of RAM, but that's much - * more than needed for the Linux guests we support. */ - spapr->htab_shift = 18; /* Minimum architected size */ - while (spapr->htab_shift <= 46) { - if ((1ULL << (spapr->htab_shift + 7)) >= machine->maxram_size) { - break; - } - spapr->htab_shift++; - } - spapr_alloc_htab(spapr); - /* Set up Interrupt Controller before we create the VCPUs */ spapr->icp = xics_system_init(machine, DIV_ROUND_UP(max_cpus * kvmppc_smt_threads(), @@ -2125,6 +2102,9 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) static void spapr_machine_initfn(Object *obj) { + sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + + spapr->htab_fd = -1; object_property_add_str(obj, "kvm-type", spapr_get_kvm_type, spapr_set_kvm_type, NULL); object_property_set_description(obj, "kvm-type", @@ -2411,6 +2391,7 @@ DEFINE_SPAPR_MACHINE(2_5, "2.5", false); * pseries-2.4 */ #define SPAPR_COMPAT_2_4 \ + SPAPR_COMPAT_2_5 \ HW_COMPAT_2_4 static void spapr_machine_2_4_instance_options(MachineState *machine) diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 12f8c33db7..6e9b6be58c 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -38,6 +38,12 @@ static void set_spr(CPUState *cs, int spr, target_ulong value, run_on_cpu(cs, do_spr_sync, &s); } +static bool has_spr(PowerPCCPU *cpu, int spr) +{ + /* We can test whether the SPR is defined by checking for a valid name */ + return cpu->env.spr_cb[spr].name != NULL; +} + static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index) { /* @@ -332,11 +338,52 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } +static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + cpu_synchronize_state(CPU(cpu)); + cpu->env.spr[SPR_SPRG0] = args[0]; + + return H_SUCCESS; +} + static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong opcode, target_ulong *args) { - /* FIXME: actually implement this */ - return H_HARDWARE; + if (!has_spr(cpu, SPR_DABR)) { + return H_HARDWARE; /* DABR register not available */ + } + cpu_synchronize_state(CPU(cpu)); + + if (has_spr(cpu, SPR_DABRX)) { + cpu->env.spr[SPR_DABRX] = 0x3; /* Use Problem and Privileged state */ + } else if (!(args[0] & 0x4)) { /* Breakpoint Translation set? */ + return H_RESERVED_DABR; + } + + cpu->env.spr[SPR_DABR] = args[0]; + return H_SUCCESS; +} + +static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong dabrx = args[1]; + + if (!has_spr(cpu, SPR_DABR) || !has_spr(cpu, SPR_DABRX)) { + return H_HARDWARE; + } + + if ((dabrx & ~0xfULL) != 0 || (dabrx & H_DABRX_HYPERVISOR) != 0 + || (dabrx & (H_DABRX_KERNEL | H_DABRX_USER)) == 0) { + return H_PARAMETER; + } + + cpu_synchronize_state(CPU(cpu)); + cpu->env.spr[SPR_DABRX] = dabrx; + cpu->env.spr[SPR_DABR] = args[0]; + + return H_SUCCESS; } #define FLAGS_REGISTER_VPA 0x0000200000000000ULL @@ -990,13 +1037,16 @@ static void hypercall_register_types(void) /* hcall-bulk */ spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); - /* hcall-dabr */ - spapr_register_hypercall(H_SET_DABR, h_set_dabr); - /* hcall-splpar */ spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); spapr_register_hypercall(H_CEDE, h_cede); + /* processor register resource access h-calls */ + spapr_register_hypercall(H_SET_SPRG0, h_set_sprg0); + spapr_register_hypercall(H_SET_DABR, h_set_dabr); + spapr_register_hypercall(H_SET_XDABR, h_set_xdabr); + spapr_register_hypercall(H_SET_MODE, h_set_mode); + /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate * here between the "CI" and the "CACHE" variants, they will use whatever * mapping attributes qemu is using. When using KVM, the kernel will @@ -1013,8 +1063,6 @@ static void hypercall_register_types(void) /* qemu/KVM-PPC specific hcalls */ spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); - spapr_register_hypercall(H_SET_MODE, h_set_mode); - /* ibm,client-architecture-support support */ spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 07ad672e5a..b7c5ebde40 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -113,6 +113,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } qemu_system_shutdown_request(); + cpu_stop_current(); rtas_st(rets, 0, RTAS_OUT_SUCCESS); } diff --git a/hw/scsi/mptconfig.c b/hw/scsi/mptconfig.c index d04982513a..707185469e 100644 --- a/hw/scsi/mptconfig.c +++ b/hw/scsi/mptconfig.c @@ -123,6 +123,7 @@ static size_t vpack(uint8_t **p_data, const char *fmt, va_list ap1) va_copy(ap2, ap1); size = vfill(NULL, 0, fmt, ap2); *p_data = data = g_malloc(size); + va_end(ap2); } return vfill(data, size, fmt, ap1); } diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 333cc1fb97..499c1465ae 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -504,6 +504,7 @@ reply_maybe_async: reply_async->IOCLogInfo = count; return; } + g_free(reply_async); reply.TerminationCount = count; break; @@ -823,7 +824,7 @@ static uint32_t mptsas_doorbell_read(MPTSASState *s) { uint32_t ret; - ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_SHIFT; + ret = (s->who_init << MPI_DOORBELL_WHO_INIT_SHIFT) & MPI_DOORBELL_WHO_INIT_MASK; ret |= s->state; switch (s->doorbell_state) { case DOORBELL_NONE: diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index f1aed83d9d..31c83308f2 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -1,6 +1,6 @@ common-obj-$(CONFIG_PL181) += pl181.o common-obj-$(CONFIG_SSI_SD) += ssi-sd.o -common-obj-$(CONFIG_SD) += sd.o +common-obj-$(CONFIG_SD) += sd.o core.o common-obj-$(CONFIG_SDHCI) += sdhci.o obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o diff --git a/hw/sd/core.c b/hw/sd/core.c new file mode 100644 index 0000000000..14c2bdf27b --- /dev/null +++ b/hw/sd/core.c @@ -0,0 +1,146 @@ +/* + * SD card bus interface code. + * + * Copyright (c) 2015 Linaro Limited + * + * Author: + * Peter Maydell <peter.maydell@linaro.org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "hw/qdev-core.h" +#include "sysemu/block-backend.h" +#include "hw/sd/sd.h" + +static SDState *get_card(SDBus *sdbus) +{ + /* We only ever have one child on the bus so just return it */ + BusChild *kid = QTAILQ_FIRST(&sdbus->qbus.children); + + if (!kid) { + return NULL; + } + return SD_CARD(kid->child); +} + +int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->do_command(card, req, response); + } + + return 0; +} + +void sdbus_write_data(SDBus *sdbus, uint8_t value) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + sc->write_data(card, value); + } +} + +uint8_t sdbus_read_data(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->read_data(card); + } + + return 0; +} + +bool sdbus_data_ready(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->data_ready(card); + } + + return false; +} + +bool sdbus_get_inserted(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->get_inserted(card); + } + + return false; +} + +bool sdbus_get_readonly(SDBus *sdbus) +{ + SDState *card = get_card(sdbus); + + if (card) { + SDCardClass *sc = SD_CARD_GET_CLASS(card); + + return sc->get_readonly(card); + } + + return false; +} + +void sdbus_set_inserted(SDBus *sdbus, bool inserted) +{ + SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); + BusState *qbus = BUS(sdbus); + + if (sbc->set_inserted) { + sbc->set_inserted(qbus->parent, inserted); + } +} + +void sdbus_set_readonly(SDBus *sdbus, bool readonly) +{ + SDBusClass *sbc = SD_BUS_GET_CLASS(sdbus); + BusState *qbus = BUS(sdbus); + + if (sbc->set_readonly) { + sbc->set_readonly(qbus->parent, readonly); + } +} + +static const TypeInfo sd_bus_info = { + .name = TYPE_SD_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(SDBus), + .class_size = sizeof(SDBusClass), +}; + +static void sd_bus_register_types(void) +{ + type_register_static(&sd_bus_info); +} + +type_init(sd_bus_register_types) diff --git a/hw/sd/pxa2xx_mmci.c b/hw/sd/pxa2xx_mmci.c index 81da7b7bbd..9c3679b5db 100644 --- a/hw/sd/pxa2xx_mmci.c +++ b/hw/sd/pxa2xx_mmci.c @@ -12,17 +12,31 @@ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/sysbus.h" #include "hw/arm/pxa.h" #include "hw/sd/sd.h" #include "hw/qdev.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" + +#define TYPE_PXA2XX_MMCI "pxa2xx-mmci" +#define PXA2XX_MMCI(obj) OBJECT_CHECK(PXA2xxMMCIState, (obj), TYPE_PXA2XX_MMCI) + +#define TYPE_PXA2XX_MMCI_BUS "pxa2xx-mmci-bus" +#define PXA2XX_MMCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_PXA2XX_MMCI_BUS) struct PXA2xxMMCIState { + SysBusDevice parent_obj; + MemoryRegion iomem; qemu_irq irq; qemu_irq rx_dma; qemu_irq tx_dma; + qemu_irq inserted; + qemu_irq readonly; - SDState *card; + BlockBackend *blk; + SDBus sdbus; uint32_t status; uint32_t clkrt; @@ -30,25 +44,70 @@ struct PXA2xxMMCIState { uint32_t cmdat; uint32_t resp_tout; uint32_t read_tout; - int blklen; - int numblk; + int32_t blklen; + int32_t numblk; uint32_t intmask; uint32_t intreq; - int cmd; + int32_t cmd; uint32_t arg; - int active; - int bytesleft; + int32_t active; + int32_t bytesleft; uint8_t tx_fifo[64]; - int tx_start; - int tx_len; + uint32_t tx_start; + uint32_t tx_len; uint8_t rx_fifo[32]; - int rx_start; - int rx_len; + uint32_t rx_start; + uint32_t rx_len; uint16_t resp_fifo[9]; - int resp_len; + uint32_t resp_len; - int cmdreq; + int32_t cmdreq; +}; + +static bool pxa2xx_mmci_vmstate_validate(void *opaque, int version_id) +{ + PXA2xxMMCIState *s = opaque; + + return s->tx_start < ARRAY_SIZE(s->tx_fifo) + && s->rx_start < ARRAY_SIZE(s->rx_fifo) + && s->tx_len <= ARRAY_SIZE(s->tx_fifo) + && s->rx_len <= ARRAY_SIZE(s->rx_fifo) + && s->resp_len <= ARRAY_SIZE(s->resp_fifo); +} + + +static const VMStateDescription vmstate_pxa2xx_mmci = { + .name = "pxa2xx-mmci", + .version_id = 2, + .minimum_version_id = 2, + .fields = (VMStateField[]) { + VMSTATE_UINT32(status, PXA2xxMMCIState), + VMSTATE_UINT32(clkrt, PXA2xxMMCIState), + VMSTATE_UINT32(spi, PXA2xxMMCIState), + VMSTATE_UINT32(cmdat, PXA2xxMMCIState), + VMSTATE_UINT32(resp_tout, PXA2xxMMCIState), + VMSTATE_UINT32(read_tout, PXA2xxMMCIState), + VMSTATE_INT32(blklen, PXA2xxMMCIState), + VMSTATE_INT32(numblk, PXA2xxMMCIState), + VMSTATE_UINT32(intmask, PXA2xxMMCIState), + VMSTATE_UINT32(intreq, PXA2xxMMCIState), + VMSTATE_INT32(cmd, PXA2xxMMCIState), + VMSTATE_UINT32(arg, PXA2xxMMCIState), + VMSTATE_INT32(cmdreq, PXA2xxMMCIState), + VMSTATE_INT32(active, PXA2xxMMCIState), + VMSTATE_INT32(bytesleft, PXA2xxMMCIState), + VMSTATE_UINT32(tx_start, PXA2xxMMCIState), + VMSTATE_UINT32(tx_len, PXA2xxMMCIState), + VMSTATE_UINT32(rx_start, PXA2xxMMCIState), + VMSTATE_UINT32(rx_len, PXA2xxMMCIState), + VMSTATE_UINT32(resp_len, PXA2xxMMCIState), + VMSTATE_VALIDATE("fifo size incorrect", pxa2xx_mmci_vmstate_validate), + VMSTATE_UINT8_ARRAY(tx_fifo, PXA2xxMMCIState, 64), + VMSTATE_UINT8_ARRAY(rx_fifo, PXA2xxMMCIState, 32), + VMSTATE_UINT16_ARRAY(resp_fifo, PXA2xxMMCIState, 9), + VMSTATE_END_OF_LIST() + } }; #define MMC_STRPCL 0x00 /* MMC Clock Start/Stop register */ @@ -122,7 +181,7 @@ static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s) if (s->cmdat & CMDAT_WR_RD) { while (s->bytesleft && s->tx_len) { - sd_write_data(s->card, s->tx_fifo[s->tx_start ++]); + sdbus_write_data(&s->sdbus, s->tx_fifo[s->tx_start++]); s->tx_start &= 0x1f; s->tx_len --; s->bytesleft --; @@ -132,7 +191,7 @@ static void pxa2xx_mmci_fifo_update(PXA2xxMMCIState *s) } else while (s->bytesleft && s->rx_len < 32) { s->rx_fifo[(s->rx_start + (s->rx_len ++)) & 0x1f] = - sd_read_data(s->card); + sdbus_read_data(&s->sdbus); s->bytesleft --; s->intreq |= INT_RXFIFO_REQ; } @@ -166,7 +225,7 @@ static void pxa2xx_mmci_wakequeues(PXA2xxMMCIState *s) request.arg = s->arg; request.crc = 0; /* FIXME */ - rsplen = sd_do_command(s->card, &request, response); + rsplen = sdbus_do_command(&s->sdbus, &request, response); s->intreq |= INT_END_CMD; memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); @@ -392,114 +451,147 @@ static const MemoryRegionOps pxa2xx_mmci_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void pxa2xx_mmci_save(QEMUFile *f, void *opaque) +PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, + hwaddr base, + BlockBackend *blk, qemu_irq irq, + qemu_irq rx_dma, qemu_irq tx_dma) { - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - int i; - - qemu_put_be32s(f, &s->status); - qemu_put_be32s(f, &s->clkrt); - qemu_put_be32s(f, &s->spi); - qemu_put_be32s(f, &s->cmdat); - qemu_put_be32s(f, &s->resp_tout); - qemu_put_be32s(f, &s->read_tout); - qemu_put_be32(f, s->blklen); - qemu_put_be32(f, s->numblk); - qemu_put_be32s(f, &s->intmask); - qemu_put_be32s(f, &s->intreq); - qemu_put_be32(f, s->cmd); - qemu_put_be32s(f, &s->arg); - qemu_put_be32(f, s->cmdreq); - qemu_put_be32(f, s->active); - qemu_put_be32(f, s->bytesleft); - - qemu_put_byte(f, s->tx_len); - for (i = 0; i < s->tx_len; i ++) - qemu_put_byte(f, s->tx_fifo[(s->tx_start + i) & 63]); - - qemu_put_byte(f, s->rx_len); - for (i = 0; i < s->rx_len; i ++) - qemu_put_byte(f, s->rx_fifo[(s->rx_start + i) & 31]); - - qemu_put_byte(f, s->resp_len); - for (i = s->resp_len; i < 9; i ++) - qemu_put_be16s(f, &s->resp_fifo[i]); + DeviceState *dev, *carddev; + SysBusDevice *sbd; + PXA2xxMMCIState *s; + Error *err = NULL; + + dev = qdev_create(NULL, TYPE_PXA2XX_MMCI); + s = PXA2XX_MMCI(dev); + sbd = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(sbd, 0, base); + sysbus_connect_irq(sbd, 0, irq); + qdev_connect_gpio_out_named(dev, "rx-dma", 0, rx_dma); + qdev_connect_gpio_out_named(dev, "tx-dma", 0, tx_dma); + + /* Create and plug in the sd card */ + carddev = qdev_create(qdev_get_child_bus(dev, "sd-bus"), TYPE_SD_CARD); + qdev_prop_set_drive(carddev, "drive", blk, &err); + if (err) { + error_report("failed to init SD card: %s", error_get_pretty(err)); + return NULL; + } + object_property_set_bool(OBJECT(carddev), true, "realized", &err); + if (err) { + error_report("failed to init SD card: %s", error_get_pretty(err)); + return NULL; + } + + return s; } -static int pxa2xx_mmci_load(QEMUFile *f, void *opaque, int version_id) +static void pxa2xx_mmci_set_inserted(DeviceState *dev, bool inserted) { - PXA2xxMMCIState *s = (PXA2xxMMCIState *) opaque; - int i; - - qemu_get_be32s(f, &s->status); - qemu_get_be32s(f, &s->clkrt); - qemu_get_be32s(f, &s->spi); - qemu_get_be32s(f, &s->cmdat); - qemu_get_be32s(f, &s->resp_tout); - qemu_get_be32s(f, &s->read_tout); - s->blklen = qemu_get_be32(f); - s->numblk = qemu_get_be32(f); - qemu_get_be32s(f, &s->intmask); - qemu_get_be32s(f, &s->intreq); - s->cmd = qemu_get_be32(f); - qemu_get_be32s(f, &s->arg); - s->cmdreq = qemu_get_be32(f); - s->active = qemu_get_be32(f); - s->bytesleft = qemu_get_be32(f); - - s->tx_len = qemu_get_byte(f); - s->tx_start = 0; - if (s->tx_len >= sizeof(s->tx_fifo) || s->tx_len < 0) - return -EINVAL; - for (i = 0; i < s->tx_len; i ++) - s->tx_fifo[i] = qemu_get_byte(f); + PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - s->rx_len = qemu_get_byte(f); - s->rx_start = 0; - if (s->rx_len >= sizeof(s->rx_fifo) || s->rx_len < 0) - return -EINVAL; - for (i = 0; i < s->rx_len; i ++) - s->rx_fifo[i] = qemu_get_byte(f); + qemu_set_irq(s->inserted, inserted); +} - s->resp_len = qemu_get_byte(f); - if (s->resp_len > 9 || s->resp_len < 0) - return -EINVAL; - for (i = s->resp_len; i < 9; i ++) - qemu_get_be16s(f, &s->resp_fifo[i]); +static void pxa2xx_mmci_set_readonly(DeviceState *dev, bool readonly) +{ + PXA2xxMMCIState *s = PXA2XX_MMCI(dev); - return 0; + qemu_set_irq(s->readonly, readonly); } -PXA2xxMMCIState *pxa2xx_mmci_init(MemoryRegion *sysmem, - hwaddr base, - BlockBackend *blk, qemu_irq irq, - qemu_irq rx_dma, qemu_irq tx_dma) +void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, + qemu_irq coverswitch) { - PXA2xxMMCIState *s; + DeviceState *dev = DEVICE(s); + + s->readonly = readonly; + s->inserted = coverswitch; + + pxa2xx_mmci_set_inserted(dev, sdbus_get_inserted(&s->sdbus)); + pxa2xx_mmci_set_readonly(dev, sdbus_get_readonly(&s->sdbus)); +} + +static void pxa2xx_mmci_reset(DeviceState *d) +{ + PXA2xxMMCIState *s = PXA2XX_MMCI(d); + + s->status = 0; + s->clkrt = 0; + s->spi = 0; + s->cmdat = 0; + s->resp_tout = 0; + s->read_tout = 0; + s->blklen = 0; + s->numblk = 0; + s->intmask = 0; + s->intreq = 0; + s->cmd = 0; + s->arg = 0; + s->active = 0; + s->bytesleft = 0; + s->tx_start = 0; + s->tx_len = 0; + s->rx_start = 0; + s->rx_len = 0; + s->resp_len = 0; + s->cmdreq = 0; + memset(s->tx_fifo, 0, sizeof(s->tx_fifo)); + memset(s->rx_fifo, 0, sizeof(s->rx_fifo)); + memset(s->resp_fifo, 0, sizeof(s->resp_fifo)); +} - s = (PXA2xxMMCIState *) g_malloc0(sizeof(PXA2xxMMCIState)); - s->irq = irq; - s->rx_dma = rx_dma; - s->tx_dma = tx_dma; +static void pxa2xx_mmci_instance_init(Object *obj) +{ + PXA2xxMMCIState *s = PXA2XX_MMCI(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + DeviceState *dev = DEVICE(obj); - memory_region_init_io(&s->iomem, NULL, &pxa2xx_mmci_ops, s, + memory_region_init_io(&s->iomem, obj, &pxa2xx_mmci_ops, s, "pxa2xx-mmci", 0x00100000); - memory_region_add_subregion(sysmem, base, &s->iomem); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + qdev_init_gpio_out_named(dev, &s->rx_dma, "rx-dma", 1); + qdev_init_gpio_out_named(dev, &s->tx_dma, "tx-dma", 1); - /* Instantiate the actual storage */ - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_PXA2XX_MMCI_BUS, DEVICE(obj), "sd-bus"); +} - register_savevm(NULL, "pxa2xx_mmci", 0, 0, - pxa2xx_mmci_save, pxa2xx_mmci_load, s); +static void pxa2xx_mmci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); - return s; + dc->vmsd = &vmstate_pxa2xx_mmci; + dc->reset = pxa2xx_mmci_reset; } -void pxa2xx_mmci_handlers(PXA2xxMMCIState *s, qemu_irq readonly, - qemu_irq coverswitch) +static void pxa2xx_mmci_bus_class_init(ObjectClass *klass, void *data) { - sd_set_cb(s->card, readonly, coverswitch); + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = pxa2xx_mmci_set_inserted; + sbc->set_readonly = pxa2xx_mmci_set_readonly; } + +static const TypeInfo pxa2xx_mmci_info = { + .name = TYPE_PXA2XX_MMCI, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(PXA2xxMMCIState), + .instance_init = pxa2xx_mmci_instance_init, + .class_init = pxa2xx_mmci_class_init, +}; + +static const TypeInfo pxa2xx_mmci_bus_info = { + .name = TYPE_PXA2XX_MMCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = pxa2xx_mmci_bus_class_init, +}; + +static void pxa2xx_mmci_register_types(void) +{ + type_register_static(&pxa2xx_mmci_info); + type_register_static(&pxa2xx_mmci_bus_info); +} + +type_init(pxa2xx_mmci_register_types) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index dd614b0890..edb6b32690 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -30,10 +30,14 @@ */ #include "qemu/osdep.h" +#include "hw/qdev.h" #include "hw/hw.h" #include "sysemu/block-backend.h" #include "hw/sd/sd.h" #include "qemu/bitmap.h" +#include "hw/qdev-properties.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" //#define DEBUG_SD 1 @@ -44,7 +48,9 @@ do { fprintf(stderr, "SD: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif -#define ACMD41_ENQUIRY_MASK 0x00ffffff +#define ACMD41_ENQUIRY_MASK 0x00ffffff +#define OCR_POWER_UP 0x80000000 +#define OCR_POWER_DELAY_NS 500000 /* 0.5ms */ typedef enum { sd_r0 = 0, /* no response */ @@ -78,9 +84,12 @@ enum SDCardStates { }; struct SDState { + DeviceState parent_obj; + uint32_t mode; /* current card mode, one of SDCardModes */ int32_t state; /* current card state, one of SDCardStates */ uint32_t ocr; + QEMUTimer *ocr_power_timer; uint8_t scr[8]; uint8_t cid[16]; uint8_t csd[16]; @@ -93,6 +102,7 @@ struct SDState { int32_t wpgrps_size; uint64_t size; uint32_t blk_len; + uint32_t multi_blk_cnt; uint32_t erase_start; uint32_t erase_end; uint8_t pwd[16]; @@ -194,8 +204,17 @@ static uint16_t sd_crc16(void *message, size_t width) static void sd_set_ocr(SDState *sd) { - /* All voltages OK, card power-up OK, Standard Capacity SD Memory Card */ - sd->ocr = 0x80ffff00; + /* All voltages OK, Standard Capacity SD Memory Card, not yet powered up */ + sd->ocr = 0x00ffff00; +} + +static void sd_ocr_powerup(void *opaque) +{ + SDState *sd = opaque; + + /* Set powered up bit in OCR */ + assert(!(sd->ocr & OCR_POWER_UP)); + sd->ocr |= OCR_POWER_UP; } static void sd_set_scr(SDState *sd) @@ -390,8 +409,9 @@ static inline uint64_t sd_addr_to_wpnum(uint64_t addr) return addr >> (HWBLOCK_SHIFT + SECTOR_SHIFT + WPGROUP_SHIFT); } -static void sd_reset(SDState *sd) +static void sd_reset(DeviceState *dev) { + SDState *sd = SD_CARD(dev); uint64_t size; uint64_t sect; @@ -424,16 +444,44 @@ static void sd_reset(SDState *sd) sd->blk_len = 0x200; sd->pwd_len = 0; sd->expecting_acmd = false; + sd->multi_blk_cnt = 0; +} + +static bool sd_get_inserted(SDState *sd) +{ + return blk_is_inserted(sd->blk); +} + +static bool sd_get_readonly(SDState *sd) +{ + return sd->wp_switch; } static void sd_cardchange(void *opaque, bool load) { SDState *sd = opaque; + DeviceState *dev = DEVICE(sd); + SDBus *sdbus = SD_BUS(qdev_get_parent_bus(dev)); + bool inserted = sd_get_inserted(sd); + bool readonly = sd_get_readonly(sd); - qemu_set_irq(sd->inserted_cb, blk_is_inserted(sd->blk)); - if (blk_is_inserted(sd->blk)) { - sd_reset(sd); - qemu_set_irq(sd->readonly_cb, sd->wp_switch); + if (inserted) { + sd_reset(dev); + } + + /* The IRQ notification is for legacy non-QOM SD controller devices; + * QOMified controllers use the SDBus APIs. + */ + if (sdbus) { + sdbus_set_inserted(sdbus, inserted); + if (inserted) { + sdbus_set_readonly(sdbus, readonly); + } + } else { + qemu_set_irq(sd->inserted_cb, inserted); + if (inserted) { + qemu_set_irq(sd->readonly_cb, readonly); + } } } @@ -441,10 +489,44 @@ static const BlockDevOps sd_block_ops = { .change_media_cb = sd_cardchange, }; +static bool sd_ocr_vmstate_needed(void *opaque) +{ + SDState *sd = opaque; + + /* Include the OCR state (and timer) if it is not yet powered up */ + return !(sd->ocr & OCR_POWER_UP); +} + +static const VMStateDescription sd_ocr_vmstate = { + .name = "sd-card/ocr-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = sd_ocr_vmstate_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ocr, SDState), + VMSTATE_TIMER_PTR(ocr_power_timer, SDState), + VMSTATE_END_OF_LIST() + }, +}; + +static int sd_vmstate_pre_load(void *opaque) +{ + SDState *sd = opaque; + + /* If the OCR state is not included (prior versions, or not + * needed), then the OCR must be set as powered up. If the OCR state + * is included, this will be replaced by the state restore. + */ + sd_ocr_powerup(sd); + + return 0; +} + static const VMStateDescription sd_vmstate = { .name = "sd-card", .version_id = 1, .minimum_version_id = 1, + .pre_load = sd_vmstate_pre_load, .fields = (VMStateField[]) { VMSTATE_UINT32(mode, SDState), VMSTATE_INT32(state, SDState), @@ -456,6 +538,7 @@ static const VMStateDescription sd_vmstate = { VMSTATE_UINT32(vhs, SDState), VMSTATE_BITMAP(wp_groups, SDState, 0, wpgrps_size), VMSTATE_UINT32(blk_len, SDState), + VMSTATE_UINT32(multi_blk_cnt, SDState), VMSTATE_UINT32(erase_start, SDState), VMSTATE_UINT32(erase_end, SDState), VMSTATE_UINT8_ARRAY(pwd, SDState, 16), @@ -470,37 +553,33 @@ static const VMStateDescription sd_vmstate = { VMSTATE_BUFFER_POINTER_UNSAFE(buf, SDState, 1, 512), VMSTATE_BOOL(enable, SDState), VMSTATE_END_OF_LIST() - } + }, + .subsections = (const VMStateDescription*[]) { + &sd_ocr_vmstate, + NULL + }, }; -/* We do not model the chip select pin, so allow the board to select - whether card should be in SSI or MMC/SD mode. It is also up to the - board to ensure that ssi transfers only occur when the chip select - is asserted. */ +/* Legacy initialization function for use by non-qdevified callers */ SDState *sd_init(BlockBackend *blk, bool is_spi) { - SDState *sd; + DeviceState *dev; + Error *err = NULL; - if (blk && blk_is_read_only(blk)) { - fprintf(stderr, "sd_init: Cannot use read-only drive\n"); + dev = qdev_create(NULL, TYPE_SD_CARD); + qdev_prop_set_drive(dev, "drive", blk, &err); + if (err) { + error_report("sd_init failed: %s", error_get_pretty(err)); return NULL; } - - sd = (SDState *) g_malloc0(sizeof(SDState)); - sd->buf = blk_blockalign(blk, 512); - sd->spi = is_spi; - sd->enable = true; - sd->blk = blk; - sd_reset(sd); - if (sd->blk) { - /* Attach dev if not already attached. (This call ignores an - * error return code if sd->blk is already attached.) */ - /* FIXME ignoring blk_attach_dev() failure is dangerously brittle */ - blk_attach_dev(sd->blk, sd); - blk_set_dev_ops(sd->blk, &sd_block_ops, sd); + qdev_prop_set_bit(dev, "spi", is_spi); + object_property_set_bool(OBJECT(dev), true, "realized", &err); + if (err) { + error_report("sd_init failed: %s", error_get_pretty(err)); + return NULL; } - vmstate_register(NULL, -1, &sd_vmstate, sd); - return sd; + + return SD_CARD(dev); } void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert) @@ -674,6 +753,12 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, rca = req.arg >> 16; } + /* CMD23 (set block count) must be immediately followed by CMD18 or CMD25 + * if not, its effects are cancelled */ + if (sd->multi_blk_cnt != 0 && !(req.cmd == 18 || req.cmd == 25)) { + sd->multi_blk_cnt = 0; + } + DPRINTF("CMD%d 0x%08x state %d\n", req.cmd, req.arg, sd->state); switch (req.cmd) { /* Basic commands (Class 0 and Class 1) */ @@ -684,7 +769,7 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, default: sd->state = sd_idle_state; - sd_reset(sd); + sd_reset(DEVICE(sd)); return sd->spi ? sd_r1 : sd_r0; } break; @@ -969,6 +1054,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, } break; + case 23: /* CMD23: SET_BLOCK_COUNT */ + switch (sd->state) { + case sd_transfer_state: + sd->multi_blk_cnt = req.arg; + return sd_r1; + + default: + break; + } + break; + /* Block write commands (Class 4) */ case 24: /* CMD24: WRITE_SINGLE_BLOCK */ if (sd->spi) @@ -1201,16 +1297,17 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, default: bad_cmd: - fprintf(stderr, "SD: Unknown CMD%i\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: Unknown CMD%i\n", req.cmd); return sd_illegal; unimplemented_cmd: /* Commands that are recognised but not yet implemented in SPI mode. */ - fprintf(stderr, "SD: CMD%i not implemented in SPI mode\n", req.cmd); + qemu_log_mask(LOG_UNIMP, "SD: CMD%i not implemented in SPI mode\n", + req.cmd); return sd_illegal; } - fprintf(stderr, "SD: CMD%i in a wrong state\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: CMD%i in a wrong state\n", req.cmd); return sd_illegal; } @@ -1278,9 +1375,28 @@ static sd_rsp_type_t sd_app_command(SDState *sd, } switch (sd->state) { case sd_idle_state: + /* If it's the first ACMD41 since reset, we need to decide + * whether to power up. If this is not an enquiry ACMD41, + * we immediately report power on and proceed below to the + * ready state, but if it is, we set a timer to model a + * delay for power up. This works around a bug in EDK2 + * UEFI, which sends an initial enquiry ACMD41, but + * assumes that the card is in ready state as soon as it + * sees the power up bit set. */ + if (!(sd->ocr & OCR_POWER_UP)) { + if ((req.arg & ACMD41_ENQUIRY_MASK) != 0) { + timer_del(sd->ocr_power_timer); + sd_ocr_powerup(sd); + } else if (!timer_pending(sd->ocr_power_timer)) { + timer_mod_ns(sd->ocr_power_timer, + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + OCR_POWER_DELAY_NS)); + } + } + /* We accept any voltage. 10000 V is nothing. * - * We don't model init delay so just advance straight to ready state + * Once we're powered up, we advance straight to ready state * unless it's an enquiry ACMD41 (bits 23:0 == 0). */ if (req.arg & ACMD41_ENQUIRY_MASK) { @@ -1323,7 +1439,7 @@ static sd_rsp_type_t sd_app_command(SDState *sd, return sd_normal_command(sd, req); } - fprintf(stderr, "SD: ACMD%i in a wrong state\n", req.cmd); + qemu_log_mask(LOG_GUEST_ERROR, "SD: ACMD%i in a wrong state\n", req.cmd); return sd_illegal; } @@ -1367,7 +1483,7 @@ int sd_do_command(SDState *sd, SDRequest *req, if (!cmd_valid_while_locked(sd, req)) { sd->card_status |= ILLEGAL_COMMAND; sd->expecting_acmd = false; - fprintf(stderr, "SD: Card is locked\n"); + qemu_log_mask(LOG_GUEST_ERROR, "SD: Card is locked\n"); rtype = sd_illegal; goto send_response; } @@ -1525,7 +1641,8 @@ void sd_write_data(SDState *sd, uint8_t value) return; if (sd->state != sd_receivingdata_state) { - fprintf(stderr, "sd_write_data: not in Receiving-Data state\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "sd_write_data: not in Receiving-Data state\n"); return; } @@ -1569,6 +1686,14 @@ void sd_write_data(SDState *sd, uint8_t value) sd->csd[14] |= 0x40; /* Bzzzzzzztt .... Operation complete. */ + if (sd->multi_blk_cnt != 0) { + if (--sd->multi_blk_cnt == 0) { + /* Stop! */ + sd->state = sd_transfer_state; + break; + } + } + sd->state = sd_receivingdata_state; } break; @@ -1636,7 +1761,7 @@ void sd_write_data(SDState *sd, uint8_t value) break; default: - fprintf(stderr, "sd_write_data: unknown command\n"); + qemu_log_mask(LOG_GUEST_ERROR, "sd_write_data: unknown command\n"); break; } } @@ -1651,7 +1776,8 @@ uint8_t sd_read_data(SDState *sd) return 0x00; if (sd->state != sd_sendingdata_state) { - fprintf(stderr, "sd_read_data: not in Sending-Data state\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "sd_read_data: not in Sending-Data state\n"); return 0x00; } @@ -1715,6 +1841,15 @@ uint8_t sd_read_data(SDState *sd) if (sd->data_offset >= io_len) { sd->data_start += io_len; sd->data_offset = 0; + + if (sd->multi_blk_cnt != 0) { + if (--sd->multi_blk_cnt == 0) { + /* Stop! */ + sd->state = sd_transfer_state; + break; + } + } + if (sd->data_start + io_len > sd->size) { sd->card_status |= ADDRESS_ERROR; break; @@ -1753,7 +1888,7 @@ uint8_t sd_read_data(SDState *sd) break; default: - fprintf(stderr, "sd_read_data: unknown command\n"); + qemu_log_mask(LOG_GUEST_ERROR, "sd_read_data: unknown command\n"); return 0x00; } @@ -1769,3 +1904,73 @@ void sd_enable(SDState *sd, bool enable) { sd->enable = enable; } + +static void sd_instance_init(Object *obj) +{ + SDState *sd = SD_CARD(obj); + + sd->enable = true; + sd->ocr_power_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sd_ocr_powerup, sd); +} + +static void sd_realize(DeviceState *dev, Error **errp) +{ + SDState *sd = SD_CARD(dev); + + if (sd->blk && blk_is_read_only(sd->blk)) { + error_setg(errp, "Cannot use read-only drive as SD card"); + return; + } + + sd->buf = blk_blockalign(sd->blk, 512); + + if (sd->blk) { + blk_set_dev_ops(sd->blk, &sd_block_ops, sd); + } +} + +static Property sd_properties[] = { + DEFINE_PROP_DRIVE("drive", SDState, blk), + /* We do not model the chip select pin, so allow the board to select + * whether card should be in SSI or MMC/SD mode. It is also up to the + * board to ensure that ssi transfers only occur when the chip select + * is asserted. */ + DEFINE_PROP_BOOL("spi", SDState, spi, false), + DEFINE_PROP_END_OF_LIST() +}; + +static void sd_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SDCardClass *sc = SD_CARD_CLASS(klass); + + dc->realize = sd_realize; + dc->props = sd_properties; + dc->vmsd = &sd_vmstate; + dc->reset = sd_reset; + dc->bus_type = TYPE_SD_BUS; + + sc->do_command = sd_do_command; + sc->write_data = sd_write_data; + sc->read_data = sd_read_data; + sc->data_ready = sd_data_ready; + sc->enable = sd_enable; + sc->get_inserted = sd_get_inserted; + sc->get_readonly = sd_get_readonly; +} + +static const TypeInfo sd_info = { + .name = TYPE_SD_CARD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SDState), + .class_size = sizeof(SDCardClass), + .class_init = sd_class_init, + .instance_init = sd_instance_init, +}; + +static void sd_register_types(void) +{ + type_register_static(&sd_info); +} + +type_init(sd_register_types) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 30e3bf4f3c..73e7c87fbf 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -55,6 +55,9 @@ } \ } while (0) +#define TYPE_SDHCI_BUS "sdhci-bus" +#define SDHCI_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SDHCI_BUS) + /* Default SD/MMC host controller features information, which will be * presented in CAPABILITIES register of generic SD host controller at reset. * If not stated otherwise: @@ -145,9 +148,9 @@ static void sdhci_raise_insertion_irq(void *opaque) } } -static void sdhci_insert_eject_cb(void *opaque, int irq, int level) +static void sdhci_set_inserted(DeviceState *dev, bool level) { - SDHCIState *s = (SDHCIState *)opaque; + SDHCIState *s = (SDHCIState *)dev; DPRINT_L1("Card state changed: %s!\n", level ? "insert" : "eject"); if ((s->norintsts & SDHC_NIS_REMOVE) && level) { @@ -172,9 +175,9 @@ static void sdhci_insert_eject_cb(void *opaque, int irq, int level) } } -static void sdhci_card_readonly_cb(void *opaque, int irq, int level) +static void sdhci_set_readonly(DeviceState *dev, bool level) { - SDHCIState *s = (SDHCIState *)opaque; + SDHCIState *s = (SDHCIState *)dev; if (level) { s->prnsts &= ~SDHC_WRITE_PROTECT; @@ -186,6 +189,8 @@ static void sdhci_card_readonly_cb(void *opaque, int irq, int level) static void sdhci_reset(SDHCIState *s) { + DeviceState *dev = DEVICE(s); + timer_del(s->insert_timer); timer_del(s->transfer_timer); /* Set all registers to 0. Capabilities registers are not cleared @@ -194,8 +199,11 @@ static void sdhci_reset(SDHCIState *s) memset(&s->sdmasysad, 0, (uintptr_t)&s->capareg - (uintptr_t)&s->sdmasysad); if (!s->noeject_quirk) { - sd_set_cb(s->card, s->ro_cb, s->eject_cb); + /* Reset other state based on current card insertion/readonly status */ + sdhci_set_inserted(dev, sdbus_get_inserted(&s->sdbus)); + sdhci_set_readonly(dev, sdbus_get_readonly(&s->sdbus)); } + s->data_count = 0; s->stopped_state = sdhc_not_stopped; } @@ -213,7 +221,7 @@ static void sdhci_send_command(SDHCIState *s) request.cmd = s->cmdreg >> 8; request.arg = s->argument; DPRINT_L1("sending CMD%u ARG[0x%08x]\n", request.cmd, request.arg); - rlen = sd_do_command(s->card, &request, response); + rlen = sdbus_do_command(&s->sdbus, &request, response); if (s->cmdreg & SDHC_CMD_RESPONSE) { if (rlen == 4) { @@ -269,7 +277,7 @@ static void sdhci_end_transfer(SDHCIState *s) request.cmd = 0x0C; request.arg = 0; DPRINT_L1("Automatically issue CMD%d %08x\n", request.cmd, request.arg); - sd_do_command(s->card, &request, response); + sdbus_do_command(&s->sdbus, &request, response); /* Auto CMD12 response goes to the upper Response register */ s->rspreg[3] = (response[0] << 24) | (response[1] << 16) | (response[2] << 8) | response[3]; @@ -301,7 +309,7 @@ static void sdhci_read_block_from_card(SDHCIState *s) } for (index = 0; index < (s->blksize & 0x0fff); index++) { - s->fifo_buffer[index] = sd_read_data(s->card); + s->fifo_buffer[index] = sdbus_read_data(&s->sdbus); } /* New data now available for READ through Buffer Port Register */ @@ -394,7 +402,7 @@ static void sdhci_write_block_to_card(SDHCIState *s) } for (index = 0; index < (s->blksize & 0x0fff); index++) { - sd_write_data(s->card, s->fifo_buffer[index]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[index]); } /* Next data can be written through BUFFER DATORT register */ @@ -476,7 +484,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) while (s->blkcnt) { if (s->data_count == 0) { for (n = 0; n < block_size; n++) { - s->fifo_buffer[n] = sd_read_data(s->card); + s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); } } begin = s->data_count; @@ -517,7 +525,7 @@ static void sdhci_sdma_transfer_multi_blocks(SDHCIState *s) s->sdmasysad += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { - sd_write_data(s->card, s->fifo_buffer[n]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); } s->data_count = 0; if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { @@ -549,7 +557,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) if (s->trnmod & SDHC_TRNS_READ) { for (n = 0; n < datacnt; n++) { - s->fifo_buffer[n] = sd_read_data(s->card); + s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); } dma_memory_write(&address_space_memory, s->sdmasysad, s->fifo_buffer, datacnt); @@ -557,7 +565,7 @@ static void sdhci_sdma_transfer_single_block(SDHCIState *s) dma_memory_read(&address_space_memory, s->sdmasysad, s->fifo_buffer, datacnt); for (n = 0; n < datacnt; n++) { - sd_write_data(s->card, s->fifo_buffer[n]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); } } @@ -661,7 +669,7 @@ static void sdhci_do_adma(SDHCIState *s) while (length) { if (s->data_count == 0) { for (n = 0; n < block_size; n++) { - s->fifo_buffer[n] = sd_read_data(s->card); + s->fifo_buffer[n] = sdbus_read_data(&s->sdbus); } } begin = s->data_count; @@ -702,7 +710,7 @@ static void sdhci_do_adma(SDHCIState *s) dscr.addr += s->data_count - begin; if (s->data_count == block_size) { for (n = 0; n < block_size; n++) { - sd_write_data(s->card, s->fifo_buffer[n]); + sdbus_write_data(&s->sdbus, s->fifo_buffer[n]); } s->data_count = 0; if (s->trnmod & SDHC_TRNS_BLK_CNT_EN) { @@ -816,7 +824,7 @@ static void sdhci_data_transfer(void *opaque) break; } } else { - if ((s->trnmod & SDHC_TRNS_READ) && sd_data_ready(s->card)) { + if ((s->trnmod & SDHC_TRNS_READ) && sdbus_data_ready(&s->sdbus)) { s->prnsts |= SDHC_DOING_READ | SDHC_DATA_INHIBIT | SDHC_DAT_LINE_ACTIVE; sdhci_read_block_from_card(s); @@ -1153,15 +1161,10 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s) } } -static void sdhci_initfn(SDHCIState *s, BlockBackend *blk) +static void sdhci_initfn(SDHCIState *s) { - s->card = sd_init(blk, false); - if (s->card == NULL) { - exit(1); - } - s->eject_cb = qemu_allocate_irq(sdhci_insert_eject_cb, s, 0); - s->ro_cb = qemu_allocate_irq(sdhci_card_readonly_cb, s, 0); - sd_set_cb(s->card, s->ro_cb, s->eject_cb); + qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), + TYPE_SDHCI_BUS, DEVICE(s), "sd-bus"); s->insert_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_raise_insertion_irq, s); s->transfer_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, sdhci_data_transfer, s); @@ -1220,12 +1223,6 @@ const VMStateDescription sdhci_vmstate = { /* Capabilities registers provide information on supported features of this * specific host controller implementation */ static Property sdhci_pci_properties[] = { - /* - * We currently fuse controller and card into a single device - * model, but we intend to separate them. For that purpose, the - * properties that belong to the card are marked as experimental. - */ - DEFINE_PROP_DRIVE("x-drive", SDHCIState, blk), DEFINE_PROP_UINT32("capareg", SDHCIState, capareg, SDHC_CAPAB_REG_DEFAULT), DEFINE_PROP_UINT32("maxcurr", SDHCIState, maxcurr, 0), @@ -1237,7 +1234,7 @@ static void sdhci_pci_realize(PCIDevice *dev, Error **errp) SDHCIState *s = PCI_SDHCI(dev); dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */ dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ - sdhci_initfn(s, s->blk); + sdhci_initfn(s); s->buf_maxsz = sdhci_get_fifolen(s); s->fifo_buffer = g_malloc0(s->buf_maxsz); s->irq = pci_allocate_irq(dev); @@ -1285,11 +1282,8 @@ static Property sdhci_sysbus_properties[] = { static void sdhci_sysbus_init(Object *obj) { SDHCIState *s = SYSBUS_SDHCI(obj); - DriveInfo *di; - /* FIXME use a qdev drive property instead of drive_get_next() */ - di = drive_get_next(IF_SD); - sdhci_initfn(s, di ? blk_by_legacy_dinfo(di) : NULL); + sdhci_initfn(s); } static void sdhci_sysbus_finalize(Object *obj) @@ -1318,8 +1312,6 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) dc->vmsd = &sdhci_vmstate; dc->props = sdhci_sysbus_properties; dc->realize = sdhci_sysbus_realize; - /* Reason: instance_init() method uses drive_get_next() */ - dc->cannot_instantiate_with_device_add_yet = true; } static const TypeInfo sdhci_sysbus_info = { @@ -1331,10 +1323,26 @@ static const TypeInfo sdhci_sysbus_info = { .class_init = sdhci_sysbus_class_init, }; +static void sdhci_bus_class_init(ObjectClass *klass, void *data) +{ + SDBusClass *sbc = SD_BUS_CLASS(klass); + + sbc->set_inserted = sdhci_set_inserted; + sbc->set_readonly = sdhci_set_readonly; +} + +static const TypeInfo sdhci_bus_info = { + .name = TYPE_SDHCI_BUS, + .parent = TYPE_SD_BUS, + .instance_size = sizeof(SDBus), + .class_init = sdhci_bus_class_init, +}; + static void sdhci_register_types(void) { type_register_static(&sdhci_pci_info); type_register_static(&sdhci_sysbus_info); + type_register_static(&sdhci_bus_info); } type_init(sdhci_register_types) diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index ba390ad50b..f1ede5f53b 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -277,21 +277,25 @@ static const VMStateDescription vmstate_sp804 = { } }; -static int sp804_init(SysBusDevice *sbd) +static void sp804_init(Object *obj) { - DeviceState *dev = DEVICE(sbd); - SP804State *s = SP804(dev); + SP804State *s = SP804(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); sysbus_init_irq(sbd, &s->irq); + memory_region_init_io(&s->iomem, obj, &sp804_ops, s, + "sp804", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void sp804_realize(DeviceState *dev, Error **errp) +{ + SP804State *s = SP804(dev); + s->timer[0] = arm_timer_init(s->freq0); s->timer[1] = arm_timer_init(s->freq1); s->timer[0]->irq = qemu_allocate_irq(sp804_set_irq, s, 0); s->timer[1]->irq = qemu_allocate_irq(sp804_set_irq, s, 1); - memory_region_init_io(&s->iomem, OBJECT(s), &sp804_ops, s, - "sp804", 0x1000); - sysbus_init_mmio(sbd, &s->iomem); - vmstate_register(dev, -1, &vmstate_sp804, s); - return 0; } /* Integrator/CP timer module. */ @@ -344,9 +348,10 @@ static const MemoryRegionOps icp_pit_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int icp_pit_init(SysBusDevice *dev) +static void icp_pit_init(Object *obj) { - icp_pit_state *s = INTEGRATOR_PIT(dev); + icp_pit_state *s = INTEGRATOR_PIT(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); /* Timer 0 runs at the system clock speed (40MHz). */ s->timer[0] = arm_timer_init(40000000); @@ -358,26 +363,18 @@ static int icp_pit_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->timer[1]->irq); sysbus_init_irq(dev, &s->timer[2]->irq); - memory_region_init_io(&s->iomem, OBJECT(s), &icp_pit_ops, s, + memory_region_init_io(&s->iomem, obj, &icp_pit_ops, s, "icp_pit", 0x1000); sysbus_init_mmio(dev, &s->iomem); /* This device has no state to save/restore. The component timers will save themselves. */ - return 0; -} - -static void icp_pit_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = icp_pit_init; } static const TypeInfo icp_pit_info = { .name = TYPE_INTEGRATOR_PIT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(icp_pit_state), - .class_init = icp_pit_class_init, + .instance_init = icp_pit_init, }; static Property sp804_properties[] = { @@ -388,17 +385,18 @@ static Property sp804_properties[] = { static void sp804_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *k = DEVICE_CLASS(klass); - sdc->init = sp804_init; + k->realize = sp804_realize; k->props = sp804_properties; + k->vmsd = &vmstate_sp804; } static const TypeInfo sp804_info = { .name = TYPE_SP804, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(SP804State), + .instance_init = sp804_init, .class_init = sp804_class_init, }; diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index 73e547c91f..ae69345f0d 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -1422,10 +1422,11 @@ static const MemoryRegionOps exynos4210_mct_ops = { }; /* MCT init */ -static int exynos4210_mct_init(SysBusDevice *dev) +static void exynos4210_mct_init(Object *obj) { int i; - Exynos4210MCTState *s = EXYNOS4210_MCT(dev); + Exynos4210MCTState *s = EXYNOS4210_MCT(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); QEMUBH *bh[2]; /* Global timer */ @@ -1450,19 +1451,15 @@ static int exynos4210_mct_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->l_timer[i].irq); } - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_mct_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_mct_ops, s, "exynos4210-mct", MCT_SFR_SIZE); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void exynos4210_mct_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_mct_init; dc->reset = exynos4210_mct_reset; dc->vmsd = &vmstate_exynos4210_mct_state; } @@ -1471,6 +1468,7 @@ static const TypeInfo exynos4210_mct_info = { .name = TYPE_EXYNOS4210_MCT, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210MCTState), + .instance_init = exynos4210_mct_init, .class_init = exynos4210_mct_class_init, }; diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index 38f941f441..0e9e2e9bf5 100644 --- a/hw/timer/exynos4210_pwm.c +++ b/hw/timer/exynos4210_pwm.c @@ -380,9 +380,10 @@ static const MemoryRegionOps exynos4210_pwm_ops = { /* * PWM timer initialization */ -static int exynos4210_pwm_init(SysBusDevice *dev) +static void exynos4210_pwm_init(Object *obj) { - Exynos4210PWMState *s = EXYNOS4210_PWM(dev); + Exynos4210PWMState *s = EXYNOS4210_PWM(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); int i; QEMUBH *bh; @@ -394,19 +395,15 @@ static int exynos4210_pwm_init(SysBusDevice *dev) s->timer[i].parent = s; } - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_pwm_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_pwm_ops, s, "exynos4210-pwm", EXYNOS4210_PWM_REG_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_pwm_init; dc->reset = exynos4210_pwm_reset; dc->vmsd = &vmstate_exynos4210_pwm_state; } @@ -415,6 +412,7 @@ static const TypeInfo exynos4210_pwm_info = { .name = TYPE_EXYNOS4210_PWM, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210PWMState), + .instance_init = exynos4210_pwm_init, .class_init = exynos4210_pwm_class_init, }; diff --git a/hw/timer/exynos4210_rtc.c b/hw/timer/exynos4210_rtc.c index 33413039aa..f21fb54f5c 100644 --- a/hw/timer/exynos4210_rtc.c +++ b/hw/timer/exynos4210_rtc.c @@ -547,9 +547,10 @@ static const MemoryRegionOps exynos4210_rtc_ops = { /* * RTC timer initialization */ -static int exynos4210_rtc_init(SysBusDevice *dev) +static void exynos4210_rtc_init(Object *obj) { - Exynos4210RTCState *s = EXYNOS4210_RTC(dev); + Exynos4210RTCState *s = EXYNOS4210_RTC(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); QEMUBH *bh; bh = qemu_bh_new(exynos4210_rtc_tick, s); @@ -564,19 +565,15 @@ static int exynos4210_rtc_init(SysBusDevice *dev) sysbus_init_irq(dev, &s->alm_irq); sysbus_init_irq(dev, &s->tick_irq); - memory_region_init_io(&s->iomem, OBJECT(s), &exynos4210_rtc_ops, s, + memory_region_init_io(&s->iomem, obj, &exynos4210_rtc_ops, s, "exynos4210-rtc", EXYNOS4210_RTC_REG_MEM_SIZE); sysbus_init_mmio(dev, &s->iomem); - - return 0; } static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = exynos4210_rtc_init; dc->reset = exynos4210_rtc_reset; dc->vmsd = &vmstate_exynos4210_rtc_state; } @@ -585,6 +582,7 @@ static const TypeInfo exynos4210_rtc_info = { .name = TYPE_EXYNOS4210_RTC, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Exynos4210RTCState), + .instance_init = exynos4210_rtc_init, .class_init = exynos4210_rtc_class_init, }; diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c index d99d18ce51..3ccb2cb460 100644 --- a/hw/timer/pl031.c +++ b/hw/timer/pl031.c @@ -192,12 +192,13 @@ static const MemoryRegionOps pl031_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int pl031_init(SysBusDevice *dev) +static void pl031_init(Object *obj) { - PL031State *s = PL031(dev); + PL031State *s = PL031(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); struct tm tm; - memory_region_init_io(&s->iomem, OBJECT(s), &pl031_ops, s, "pl031", 0x1000); + memory_region_init_io(&s->iomem, obj, &pl031_ops, s, "pl031", 0x1000); sysbus_init_mmio(dev, &s->iomem); sysbus_init_irq(dev, &s->irq); @@ -206,7 +207,6 @@ static int pl031_init(SysBusDevice *dev) qemu_clock_get_ns(rtc_clock) / get_ticks_per_sec(); s->timer = timer_new_ns(rtc_clock, pl031_interrupt, s); - return 0; } static void pl031_pre_save(void *opaque) @@ -249,9 +249,7 @@ static const VMStateDescription vmstate_pl031 = { static void pl031_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = pl031_init; dc->vmsd = &vmstate_pl031; } @@ -259,6 +257,7 @@ static const TypeInfo pl031_info = { .name = TYPE_PL031, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PL031State), + .instance_init = pl031_init, .class_init = pl031_class_init, }; diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 0daa69c6b9..33449e66b5 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -433,10 +433,10 @@ static int pxa25x_timer_post_load(void *opaque, int version_id) return 0; } -static int pxa2xx_timer_init(SysBusDevice *dev) +static void pxa2xx_timer_init(Object *obj) { - PXA2xxTimerInfo *s = PXA2XX_TIMER(dev); - int i; + PXA2xxTimerInfo *s = PXA2XX_TIMER(obj); + SysBusDevice *dev = SYS_BUS_DEVICE(obj); s->irq_enabled = 0; s->oldclock = 0; @@ -444,16 +444,28 @@ static int pxa2xx_timer_init(SysBusDevice *dev) s->lastload = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); s->reset3 = 0; + memory_region_init_io(&s->iomem, obj, &pxa2xx_timer_ops, s, + "pxa2xx-timer", 0x00001000); + sysbus_init_mmio(dev, &s->iomem); +} + +static void pxa2xx_timer_realize(DeviceState *dev, Error **errp) +{ + PXA2xxTimerInfo *s = PXA2XX_TIMER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + int i; + for (i = 0; i < 4; i ++) { s->timer[i].value = 0; - sysbus_init_irq(dev, &s->timer[i].irq); + sysbus_init_irq(sbd, &s->timer[i].irq); s->timer[i].info = s; s->timer[i].num = i; s->timer[i].qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - pxa2xx_timer_tick, &s->timer[i]); + pxa2xx_timer_tick, &s->timer[i]); } + if (s->flags & (1 << PXA2XX_TIMER_HAVE_TM4)) { - sysbus_init_irq(dev, &s->irq4); + sysbus_init_irq(sbd, &s->irq4); for (i = 0; i < 8; i ++) { s->tm4[i].tm.value = 0; @@ -462,15 +474,9 @@ static int pxa2xx_timer_init(SysBusDevice *dev) s->tm4[i].freq = 0; s->tm4[i].control = 0x0; s->tm4[i].tm.qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - pxa2xx_timer_tick4, &s->tm4[i]); + pxa2xx_timer_tick4, &s->tm4[i]); } } - - memory_region_init_io(&s->iomem, OBJECT(s), &pxa2xx_timer_ops, s, - "pxa2xx-timer", 0x00001000); - sysbus_init_mmio(dev, &s->iomem); - - return 0; } static const VMStateDescription vmstate_pxa2xx_timer0_regs = { @@ -573,9 +579,8 @@ static const TypeInfo pxa27x_timer_dev_info = { static void pxa2xx_timer_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(oc); - sdc->init = pxa2xx_timer_init; + dc->realize = pxa2xx_timer_realize; dc->vmsd = &vmstate_pxa2xx_timer_regs; } @@ -583,6 +588,7 @@ static const TypeInfo pxa2xx_timer_type_info = { .name = TYPE_PXA2XX_TIMER, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PXA2xxTimerInfo), + .instance_init = pxa2xx_timer_init, .abstract = true, .class_init = pxa2xx_timer_class_init, }; diff --git a/include/block/nbd.h b/include/block/nbd.h index 7eccb41da8..b197adca1c 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -23,6 +23,8 @@ #include "qemu-common.h" #include "qemu/option.h" +#include "io/channel-socket.h" +#include "crypto/tlscreds.h" struct nbd_request { uint32_t magic; @@ -55,7 +57,10 @@ struct nbd_reply { #define NBD_REP_ACK (1) /* Data sending finished. */ #define NBD_REP_SERVER (2) /* Export description. */ #define NBD_REP_ERR_UNSUP ((UINT32_C(1) << 31) | 1) /* Unknown option. */ +#define NBD_REP_ERR_POLICY ((UINT32_C(1) << 31) | 2) /* Server denied */ #define NBD_REP_ERR_INVALID ((UINT32_C(1) << 31) | 3) /* Invalid length. */ +#define NBD_REP_ERR_TLS_REQD ((UINT32_C(1) << 31) | 5) /* TLS required */ + #define NBD_CMD_MASK_COMMAND 0x0000ffff #define NBD_CMD_FLAG_FUA (1 << 16) @@ -73,12 +78,19 @@ enum { /* Maximum size of a single READ/WRITE data buffer */ #define NBD_MAX_BUFFER_SIZE (32 * 1024 * 1024) -ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read); -int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, +ssize_t nbd_wr_syncv(QIOChannel *ioc, + struct iovec *iov, + size_t niov, + size_t offset, + size_t length, + bool do_read); +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, + QCryptoTLSCreds *tlscreds, const char *hostname, + QIOChannel **outioc, off_t *size, Error **errp); -int nbd_init(int fd, int csock, uint32_t flags, off_t size); -ssize_t nbd_send_request(int csock, struct nbd_request *request); -ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply); +int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size); +ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request); +ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply); int nbd_client(int fd); int nbd_disconnect(int fd); @@ -98,7 +110,11 @@ NBDExport *nbd_export_find(const char *name); void nbd_export_set_name(NBDExport *exp, const char *name); void nbd_export_close_all(void); -void nbd_client_new(NBDExport *exp, int csock, void (*close_fn)(NBDClient *)); +void nbd_client_new(NBDExport *exp, + QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, + void (*close)(NBDClient *)); void nbd_client_get(NBDClient *client); void nbd_client_put(NBDClient *client); diff --git a/include/hw/input/adb.h b/include/hw/input/adb.h index bdfccd4041..db51d03804 100644 --- a/include/hw/input/adb.h +++ b/include/hw/input/adb.h @@ -79,7 +79,7 @@ struct ADBBusState { int adb_request(ADBBusState *s, uint8_t *buf_out, const uint8_t *buf, int len); -int adb_poll(ADBBusState *s, uint8_t *buf_out); +int adb_poll(ADBBusState *s, uint8_t *buf_out, uint16_t poll_mask); #define TYPE_ADB_KEYBOARD "adb-keyboard" #define TYPE_ADB_MOUSE "adb-mouse" diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 1f9e722545..098d85d1a1 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -72,7 +72,6 @@ struct sPAPRMachineState { int htab_save_index; bool htab_first_pass; int htab_fd; - bool htab_fd_stale; /* RTAS state */ QTAILQ_HEAD(, sPAPRConfigureConnectorState) ccs_list; diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 79adb5bb48..d5d273a449 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -67,7 +67,51 @@ typedef struct { } SDRequest; typedef struct SDState SDState; +typedef struct SDBus SDBus; +#define TYPE_SD_CARD "sd-card" +#define SD_CARD(obj) OBJECT_CHECK(SDState, (obj), TYPE_SD_CARD) +#define SD_CARD_CLASS(klass) \ + OBJECT_CLASS_CHECK(SDCardClass, (klass), TYPE_SD_CARD) +#define SD_CARD_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SDCardClass, (obj), TYPE_SD_CARD) + +typedef struct { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + + int (*do_command)(SDState *sd, SDRequest *req, uint8_t *response); + void (*write_data)(SDState *sd, uint8_t value); + uint8_t (*read_data)(SDState *sd); + bool (*data_ready)(SDState *sd); + void (*enable)(SDState *sd, bool enable); + bool (*get_inserted)(SDState *sd); + bool (*get_readonly)(SDState *sd); +} SDCardClass; + +#define TYPE_SD_BUS "sd-bus" +#define SD_BUS(obj) OBJECT_CHECK(SDBus, (obj), TYPE_SD_BUS) +#define SD_BUS_CLASS(klass) OBJECT_CLASS_CHECK(SDBusClass, (klass), TYPE_SD_BUS) +#define SD_BUS_GET_CLASS(obj) OBJECT_GET_CLASS(SDBusClass, (obj), TYPE_SD_BUS) + +struct SDBus { + BusState qbus; +}; + +typedef struct { + /*< private >*/ + BusClass parent_class; + /*< public >*/ + + /* These methods are called by the SD device to notify the controller + * when the card insertion or readonly status changes + */ + void (*set_inserted)(DeviceState *dev, bool inserted); + void (*set_readonly)(DeviceState *dev, bool readonly); +} SDBusClass; + +/* Legacy functions to be used only by non-qdevified callers */ SDState *sd_init(BlockBackend *bs, bool is_spi); int sd_do_command(SDState *sd, SDRequest *req, uint8_t *response); @@ -75,6 +119,27 @@ void sd_write_data(SDState *sd, uint8_t value); uint8_t sd_read_data(SDState *sd); void sd_set_cb(SDState *sd, qemu_irq readonly, qemu_irq insert); bool sd_data_ready(SDState *sd); +/* sd_enable should not be used -- it is only used on the nseries boards, + * where it is part of a broken implementation of the MMC card slot switch + * (there should be two card slots which are multiplexed to a single MMC + * controller, but instead we model it with one card and controller and + * disable the card when the second slot is selected, so it looks like the + * second slot is always empty). + */ void sd_enable(SDState *sd, bool enable); +/* Functions to be used by qdevified callers (working via + * an SDBus rather than directly with SDState) + */ +int sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *response); +void sdbus_write_data(SDBus *sd, uint8_t value); +uint8_t sdbus_read_data(SDBus *sd); +bool sdbus_data_ready(SDBus *sd); +bool sdbus_get_inserted(SDBus *sd); +bool sdbus_get_readonly(SDBus *sd); + +/* Functions to be used by SD devices to report back to qdevified controllers */ +void sdbus_set_inserted(SDBus *sd, bool inserted); +void sdbus_set_readonly(SDBus *sd, bool inserted); + #endif /* __hw_sd_h */ diff --git a/include/hw/sd/sdhci.h b/include/hw/sd/sdhci.h index ffd1f80891..607a83e855 100644 --- a/include/hw/sd/sdhci.h +++ b/include/hw/sd/sdhci.h @@ -37,9 +37,8 @@ typedef struct SDHCIState { PCIDevice pcidev; SysBusDevice busdev; }; - SDState *card; + SDBus sdbus; MemoryRegion iomem; - BlockBackend *blk; QEMUTimer *insert_timer; /* timer for 'changing' sd card. */ QEMUTimer *transfer_timer; diff --git a/include/io/channel-buffer.h b/include/io/channel-buffer.h index 91a52b3373..65c498b2c2 100644 --- a/include/io/channel-buffer.h +++ b/include/io/channel-buffer.h @@ -42,7 +42,7 @@ struct QIOChannelBuffer { size_t capacity; /* Total allocated memory */ size_t usage; /* Current size of data */ size_t offset; /* Offset for future I/O ops */ - char *data; + uint8_t *data; }; diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h index 810537ef6f..70d06b40d9 100644 --- a/include/io/channel-socket.h +++ b/include/io/channel-socket.h @@ -105,7 +105,9 @@ int qio_channel_socket_connect_sync(QIOChannelSocket *ioc, * Attempt to connect to the address @addr. This method * will run in the background so the caller will regain * execution control immediately. The function @callback - * will be invoked on completion or failure. + * will be invoked on completion or failure. The @addr + * parameter will be copied, so may be freed as soon + * as this function returns without waiting for completion. */ void qio_channel_socket_connect_async(QIOChannelSocket *ioc, SocketAddress *addr, @@ -140,7 +142,9 @@ int qio_channel_socket_listen_sync(QIOChannelSocket *ioc, * Attempt to listen to the address @addr. This method * will run in the background so the caller will regain * execution control immediately. The function @callback - * will be invoked on completion or failure. + * will be invoked on completion or failure. The @addr + * parameter will be copied, so may be freed as soon + * as this function returns without waiting for completion. */ void qio_channel_socket_listen_async(QIOChannelSocket *ioc, SocketAddress *addr, @@ -181,6 +185,9 @@ int qio_channel_socket_dgram_sync(QIOChannelSocket *ioc, * This method will run in the background so the caller * will regain execution control immediately. The function * @callback will be invoked on completion or failure. + * The @localAddr and @remoteAddr parameters will be copied, + * so may be freed as soon as this function returns without + * waiting for completion. */ void qio_channel_socket_dgram_async(QIOChannelSocket *ioc, SocketAddress *localAddr, diff --git a/include/io/channel-util.h b/include/io/channel-util.h new file mode 100644 index 0000000000..c93af82884 --- /dev/null +++ b/include/io/channel-util.h @@ -0,0 +1,52 @@ +/* + * QEMU I/O channels utility APIs + * + * Copyright (c) 2016 Red Hat, Inc. + * + * 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 QIO_CHANNEL_UTIL_H__ +#define QIO_CHANNEL_UTIL_H__ + +#include "io/channel.h" + +/* + * This module provides helper functions that are useful when dealing + * with QIOChannel objects + */ + + +/** + * qio_channel_new_fd: + * @fd: the file descriptor + * @errp: pointer to a NULL-initialized error object + * + * Create a channel for performing I/O on the file + * descriptor @fd. The particular subclass of QIOChannel + * that is returned will depend on what underlying object + * the file descriptor is associated with. It may be either + * a QIOChannelSocket or a QIOChannelFile instance. Upon + * success, the returned QIOChannel instance will own + * the @fd file descriptor, and take responsibility for + * closing it when no longer required. On failure, the + * caller is responsible for closing @fd. + * + * Returns: the channel object, or NULL on error + */ +QIOChannel *qio_channel_new_fd(int fd, + Error **errp); + +#endif /* QIO_CHANNEL_UTIL_H__ */ diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 91b95ae90a..aa0f37320c 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -43,9 +43,6 @@ void monitor_read_command(Monitor *mon, int show_prompt); int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, void *opaque); -void object_add(const char *type, const char *id, const QDict *qdict, - Visitor *v, Error **errp); - AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, bool has_opaque, const char *opaque, Error **errp); diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index d22eb01be4..c5fbe28b02 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -77,18 +77,6 @@ #define typeof_field(type, field) typeof(((type *)0)->field) #define type_check(t1,t2) ((t1*)0 - (t2*)0) -#ifndef always_inline -#if !((__GNUC__ < 3) || defined(__APPLE__)) -#ifdef __OPTIMIZE__ -#undef inline -#define inline __attribute__ (( always_inline )) __inline__ -#endif -#endif -#else -#undef inline -#define inline always_inline -#endif - #define QEMU_BUILD_BUG_ON(x) \ typedef char glue(qemu_build_bug_on__,__LINE__)[(x)?-1:1] __attribute__((unused)); diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 59a7f8deca..cc055c909e 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -27,6 +27,17 @@ #include "config-host.h" #include "qemu/compiler.h" + +/* The following block of code temporarily renames the daemon() function so the + * compiler does not see the warning associated with it in stdlib.h on OSX + */ +#ifdef __APPLE__ +#define daemon qemu_fake_daemon_function +#include <stdlib.h> +#undef daemon +extern int daemon(int, int); +#endif + #include <stdarg.h> #include <stddef.h> #include <stdbool.h> diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 283ae0db4d..d579746db6 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -2,6 +2,8 @@ #define OBJECT_INTERFACES_H #include "qom/object.h" +#include "qapi/qmp/qdict.h" +#include "qapi/visitor.h" #define TYPE_USER_CREATABLE "user-creatable" @@ -72,4 +74,94 @@ void user_creatable_complete(Object *obj, Error **errp); * from implements USER_CREATABLE interface. */ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp); + +/** + * user_creatable_add: + * @qdict: the object definition + * @v: the visitor + * @errp: if an error occurs, a pointer to an area to store the error + * + * Create an instance of the user creatable object whose type + * is defined in @qdict by the 'qom-type' field, placing it + * in the object composition tree with name provided by the + * 'id' field. The remaining fields in @qdict are used to + * initialize the object properties. + * + * Returns: the newly created object or NULL on error + */ +Object *user_creatable_add(const QDict *qdict, + Visitor *v, Error **errp); + +/** + * user_creatable_add_type: + * @type: the object type name + * @id: the unique ID for the object + * @qdict: the object properties + * @v: the visitor + * @errp: if an error occurs, a pointer to an area to store the error + * + * Create an instance of the user creatable object @type, placing + * it in the object composition tree with name @id, initializing + * it with properties from @qdict + * + * Returns: the newly created object or NULL on error + */ +Object *user_creatable_add_type(const char *type, const char *id, + const QDict *qdict, + Visitor *v, Error **errp); + +/** + * user_creatable_add_opts: + * @opts: the object definition + * @errp: if an error occurs, a pointer to an area to store the error + * + * Create an instance of the user creatable object whose type + * is defined in @opts by the 'qom-type' option, placing it + * in the object composition tree with name provided by the + * 'id' field. The remaining options in @opts are used to + * initialize the object properties. + * + * Returns: the newly created object or NULL on error + */ +Object *user_creatable_add_opts(QemuOpts *opts, Error **errp); + + +/** + * user_creatable_add_opts_predicate: + * @type: the QOM type to be added + * + * A callback function to determine whether an object + * of type @type should be created. Instances of this + * callback should be passed to user_creatable_add_opts_foreach + */ +typedef bool (*user_creatable_add_opts_predicate)(const char *type); + +/** + * user_creatable_add_opts_foreach: + * @opaque: a user_creatable_add_opts_predicate callback or NULL + * @opts: options to create + * @errp: if an error occurs, a pointer to an area to store the error + * + * An iterator callback to be used in conjunction with + * the qemu_opts_foreach() method for creating a list of + * objects from a set of QemuOpts + * + * The @opaque parameter can be passed a user_creatable_add_opts_predicate + * callback to filter which types of object are created during iteration. + * + * Returns: 0 on success, -1 on error + */ +int user_creatable_add_opts_foreach(void *opaque, + QemuOpts *opts, Error **errp); + +/** + * user_creatable_del: + * @id: the unique ID for the object + * @errp: if an error occurs, a pointer to an area to store the error + * + * Delete an instance of the user creatable object identified + * by @id. + */ +void user_creatable_del(const char *id, Error **errp); + #endif diff --git a/io/Makefile.objs b/io/Makefile.objs index 0e3de31a5a..9d8337d89a 100644 --- a/io/Makefile.objs +++ b/io/Makefile.objs @@ -6,4 +6,5 @@ io-obj-y += channel-socket.o io-obj-y += channel-tls.o io-obj-y += channel-watch.o io-obj-y += channel-websock.o +io-obj-y += channel-util.o io-obj-y += task.o diff --git a/io/channel-buffer.c b/io/channel-buffer.c index 0f7c5671fe..3e5117bf28 100644 --- a/io/channel-buffer.c +++ b/io/channel-buffer.c @@ -32,7 +32,7 @@ qio_channel_buffer_new(size_t capacity) ioc = QIO_CHANNEL_BUFFER(object_new(TYPE_QIO_CHANNEL_BUFFER)); if (capacity) { - ioc->data = g_new0(char, capacity); + ioc->data = g_new0(uint8_t, capacity); ioc->capacity = capacity; } diff --git a/io/channel-util.c b/io/channel-util.c new file mode 100644 index 0000000000..f96907cb5c --- /dev/null +++ b/io/channel-util.c @@ -0,0 +1,50 @@ +/* + * QEMU I/O channels utility APIs + * + * Copyright (c) 2016 Red Hat, Inc. + * + * 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 "io/channel-util.h" +#include "io/channel-file.h" +#include "io/channel-socket.h" + + +static bool fd_is_socket(int fd) +{ + int optval; + socklen_t optlen; + optlen = sizeof(optval); + return qemu_getsockopt(fd, + SOL_SOCKET, + SO_TYPE, + (char *)&optval, + &optlen) == 0; +} + + +QIOChannel *qio_channel_new_fd(int fd, + Error **errp) +{ + QIOChannel *ioc; + + if (fd_is_socket(fd)) { + ioc = QIO_CHANNEL(qio_channel_socket_new_fd(fd, errp)); + } else { + ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd)); + } + return ioc; +} diff --git a/libdecnumber/decContext.c b/libdecnumber/decContext.c index 68e6f60e96..7d97a65ac5 100644 --- a/libdecnumber/decContext.c +++ b/libdecnumber/decContext.c @@ -35,8 +35,7 @@ /* context structures. */ /* ------------------------------------------------------------------ */ -#include <string.h> /* for strcmp */ -#include <stdio.h> /* for printf if DECCHECK */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #include "libdecnumber/decContext.h" #include "libdecnumber/decNumberLocal.h" diff --git a/libdecnumber/decNumber.c b/libdecnumber/decNumber.c index ca1412f30b..c9e7807f87 100644 --- a/libdecnumber/decNumber.c +++ b/libdecnumber/decNumber.c @@ -166,10 +166,7 @@ /* ** -- raise to the power */ /* ------------------------------------------------------------------ */ -#include <stdlib.h> /* for malloc, free, etc. */ -#include <stdio.h> /* for printf [if needed] */ -#include <string.h> /* for strcpy */ -#include <ctype.h> /* for lower */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #include "libdecnumber/decNumber.h" #include "libdecnumber/decNumberLocal.h" diff --git a/libdecnumber/dpd/decimal128.c b/libdecnumber/dpd/decimal128.c index 7551b7caaf..ca4764e547 100644 --- a/libdecnumber/dpd/decimal128.c +++ b/libdecnumber/dpd/decimal128.c @@ -39,8 +39,7 @@ /* */ /* Error handling is the same as decNumber (qv.). */ /* ------------------------------------------------------------------ */ -#include <string.h> /* [for memset/memcpy] */ -#include <stdio.h> /* [for printf] */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #define DECNUMDIGITS 34 /* make decNumbers with space for 34 */ diff --git a/libdecnumber/dpd/decimal32.c b/libdecnumber/dpd/decimal32.c index 095ab75654..53f29789d7 100644 --- a/libdecnumber/dpd/decimal32.c +++ b/libdecnumber/dpd/decimal32.c @@ -39,8 +39,7 @@ /* */ /* Error handling is the same as decNumber (qv.). */ /* ------------------------------------------------------------------ */ -#include <string.h> /* [for memset/memcpy] */ -#include <stdio.h> /* [for printf] */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #define DECNUMDIGITS 7 /* make decNumbers with space for 7 */ diff --git a/libdecnumber/dpd/decimal64.c b/libdecnumber/dpd/decimal64.c index 8256084e90..4816176410 100644 --- a/libdecnumber/dpd/decimal64.c +++ b/libdecnumber/dpd/decimal64.c @@ -39,8 +39,7 @@ /* */ /* Error handling is the same as decNumber (qv.). */ /* ------------------------------------------------------------------ */ -#include <string.h> /* [for memset/memcpy] */ -#include <stdio.h> /* [for printf] */ +#include "qemu/osdep.h" #include "libdecnumber/dconfig.h" #define DECNUMDIGITS 16 /* make decNumbers with space for 16 */ diff --git a/migration/ram.c b/migration/ram.c index 96c749face..704f6a95bf 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1920,6 +1920,9 @@ static int ram_save_setup(QEMUFile *f, void *opaque) acct_clear(); } + /* For memory_global_dirty_log_start below. */ + qemu_mutex_lock_iothread(); + qemu_mutex_lock_ramlist(); rcu_read_lock(); bytes_transferred = 0; @@ -1944,6 +1947,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) memory_global_dirty_log_start(); migration_bitmap_sync(); qemu_mutex_unlock_ramlist(); + qemu_mutex_unlock_iothread(); qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); diff --git a/nbd/client.c b/nbd/client.c index f07cb4822d..9e5b651082 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -71,19 +71,307 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); */ -int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, + +static int nbd_handle_reply_err(uint32_t opt, uint32_t type, Error **errp) +{ + if (!(type & (1 << 31))) { + return 0; + } + + switch (type) { + case NBD_REP_ERR_UNSUP: + error_setg(errp, "Unsupported option type %x", opt); + break; + + case NBD_REP_ERR_POLICY: + error_setg(errp, "Denied by server for option %x", opt); + break; + + case NBD_REP_ERR_INVALID: + error_setg(errp, "Invalid data length for option %x", opt); + break; + + case NBD_REP_ERR_TLS_REQD: + error_setg(errp, "TLS negotiation required before option %x", opt); + break; + + default: + error_setg(errp, "Unknown error code when asking for option %x", opt); + break; + } + + return -1; +} + +static int nbd_receive_list(QIOChannel *ioc, char **name, Error **errp) +{ + uint64_t magic; + uint32_t opt; + uint32_t type; + uint32_t len; + uint32_t namelen; + + *name = NULL; + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "failed to read list option magic"); + return -1; + } + magic = be64_to_cpu(magic); + if (magic != NBD_REP_MAGIC) { + error_setg(errp, "Unexpected option list magic"); + return -1; + } + if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "failed to read list option"); + return -1; + } + opt = be32_to_cpu(opt); + if (opt != NBD_OPT_LIST) { + error_setg(errp, "Unexpected option type %x expected %x", + opt, NBD_OPT_LIST); + return -1; + } + + if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) { + error_setg(errp, "failed to read list option type"); + return -1; + } + type = be32_to_cpu(type); + if (type == NBD_REP_ERR_UNSUP) { + return 0; + } + if (nbd_handle_reply_err(opt, type, errp) < 0) { + return -1; + } + + if (read_sync(ioc, &len, sizeof(len)) != sizeof(len)) { + error_setg(errp, "failed to read option length"); + return -1; + } + len = be32_to_cpu(len); + + if (type == NBD_REP_ACK) { + if (len != 0) { + error_setg(errp, "length too long for option end"); + return -1; + } + } else if (type == NBD_REP_SERVER) { + if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) { + error_setg(errp, "failed to read option name length"); + return -1; + } + namelen = be32_to_cpu(namelen); + if (len != (namelen + sizeof(namelen))) { + error_setg(errp, "incorrect option mame length"); + return -1; + } + if (namelen > 255) { + error_setg(errp, "export name length too long %d", namelen); + return -1; + } + + *name = g_new0(char, namelen + 1); + if (read_sync(ioc, *name, namelen) != namelen) { + error_setg(errp, "failed to read export name"); + g_free(*name); + *name = NULL; + return -1; + } + (*name)[namelen] = '\0'; + } else { + error_setg(errp, "Unexpected reply type %x expected %x", + type, NBD_REP_SERVER); + return -1; + } + return 1; +} + + +static int nbd_receive_query_exports(QIOChannel *ioc, + const char *wantname, + Error **errp) +{ + uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC); + uint32_t opt = cpu_to_be32(NBD_OPT_LIST); + uint32_t length = 0; + bool foundExport = false; + + TRACE("Querying export list"); + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to send list option magic"); + return -1; + } + + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "Failed to send list option number"); + return -1; + } + + if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "Failed to send list option length"); + return -1; + } + + TRACE("Reading available export names"); + while (1) { + char *name = NULL; + int ret = nbd_receive_list(ioc, &name, errp); + + if (ret < 0) { + g_free(name); + name = NULL; + return -1; + } + if (ret == 0) { + /* Server doesn't support export listing, so + * we will just assume an export with our + * wanted name exists */ + foundExport = true; + break; + } + if (name == NULL) { + TRACE("End of export name list"); + break; + } + if (g_str_equal(name, wantname)) { + foundExport = true; + TRACE("Found desired export name '%s'", name); + } else { + TRACE("Ignored export name '%s'", name); + } + g_free(name); + } + + if (!foundExport) { + error_setg(errp, "No export with name '%s' available", wantname); + return -1; + } + + return 0; +} + +static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, + QCryptoTLSCreds *tlscreds, + const char *hostname, Error **errp) +{ + uint64_t magic = cpu_to_be64(NBD_OPTS_MAGIC); + uint32_t opt = cpu_to_be32(NBD_OPT_STARTTLS); + uint32_t length = 0; + uint32_t type; + QIOChannelTLS *tioc; + struct NBDTLSHandshakeData data = { 0 }; + + TRACE("Requesting TLS from server"); + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "Failed to send option magic"); + return NULL; + } + + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "Failed to send option number"); + return NULL; + } + + if (write_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "Failed to send option length"); + return NULL; + } + + TRACE("Getting TLS reply from server1"); + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { + error_setg(errp, "failed to read option magic"); + return NULL; + } + magic = be64_to_cpu(magic); + if (magic != NBD_REP_MAGIC) { + error_setg(errp, "Unexpected option magic"); + return NULL; + } + TRACE("Getting TLS reply from server2"); + if (read_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { + error_setg(errp, "failed to read option"); + return NULL; + } + opt = be32_to_cpu(opt); + if (opt != NBD_OPT_STARTTLS) { + error_setg(errp, "Unexpected option type %x expected %x", + opt, NBD_OPT_STARTTLS); + return NULL; + } + + TRACE("Getting TLS reply from server"); + if (read_sync(ioc, &type, sizeof(type)) != sizeof(type)) { + error_setg(errp, "failed to read option type"); + return NULL; + } + type = be32_to_cpu(type); + if (type != NBD_REP_ACK) { + error_setg(errp, "Server rejected request to start TLS %x", + type); + return NULL; + } + + TRACE("Getting TLS reply from server"); + if (read_sync(ioc, &length, sizeof(length)) != sizeof(length)) { + error_setg(errp, "failed to read option length"); + return NULL; + } + length = be32_to_cpu(length); + if (length != 0) { + error_setg(errp, "Start TLS reponse was not zero %x", + length); + return NULL; + } + + TRACE("TLS request approved, setting up TLS"); + tioc = qio_channel_tls_new_client(ioc, tlscreds, hostname, errp); + if (!tioc) { + return NULL; + } + data.loop = g_main_loop_new(g_main_context_default(), FALSE); + TRACE("Starting TLS hanshake"); + qio_channel_tls_handshake(tioc, + nbd_tls_handshake, + &data, + NULL); + + if (!data.complete) { + g_main_loop_run(data.loop); + } + g_main_loop_unref(data.loop); + if (data.error) { + error_propagate(errp, data.error); + object_unref(OBJECT(tioc)); + return NULL; + } + + return QIO_CHANNEL(tioc); +} + + +int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint32_t *flags, + QCryptoTLSCreds *tlscreds, const char *hostname, + QIOChannel **outioc, off_t *size, Error **errp) { char buf[256]; uint64_t magic, s; - uint16_t tmp; int rc; - TRACE("Receiving negotiation."); + TRACE("Receiving negotiation tlscreds=%p hostname=%s.", + tlscreds, hostname ? hostname : "<null>"); rc = -EINVAL; - if (read_sync(csock, buf, 8) != 8) { + if (outioc) { + *outioc = NULL; + } + if (tlscreds && !outioc) { + error_setg(errp, "Output I/O channel required for TLS"); + goto fail; + } + + if (read_sync(ioc, buf, 8) != 8) { error_setg(errp, "Failed to read data"); goto fail; } @@ -109,93 +397,133 @@ int nbd_receive_negotiate(int csock, const char *name, uint32_t *flags, goto fail; } - if (read_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { error_setg(errp, "Failed to read magic"); goto fail; } magic = be64_to_cpu(magic); TRACE("Magic is 0x%" PRIx64, magic); - if (name) { - uint32_t reserved = 0; + if (magic == NBD_OPTS_MAGIC) { + uint32_t clientflags = 0; uint32_t opt; uint32_t namesize; + uint16_t globalflags; + uint16_t exportflags; + bool fixedNewStyle = false; - TRACE("Checking magic (opts_magic)"); - if (magic != NBD_OPTS_MAGIC) { - if (magic == NBD_CLIENT_MAGIC) { - error_setg(errp, "Server does not support export names"); - } else { - error_setg(errp, "Bad magic received"); - } - goto fail; - } - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + if (read_sync(ioc, &globalflags, sizeof(globalflags)) != + sizeof(globalflags)) { error_setg(errp, "Failed to read server flags"); goto fail; } - *flags = be16_to_cpu(tmp) << 16; - /* reserved for future use */ - if (write_sync(csock, &reserved, sizeof(reserved)) != - sizeof(reserved)) { - error_setg(errp, "Failed to read reserved field"); + globalflags = be16_to_cpu(globalflags); + *flags = globalflags << 16; + TRACE("Global flags are %x", globalflags); + if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { + fixedNewStyle = true; + TRACE("Server supports fixed new style"); + clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; + } + /* client requested flags */ + clientflags = cpu_to_be32(clientflags); + if (write_sync(ioc, &clientflags, sizeof(clientflags)) != + sizeof(clientflags)) { + error_setg(errp, "Failed to send clientflags field"); goto fail; } + if (tlscreds) { + if (fixedNewStyle) { + *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); + if (!*outioc) { + goto fail; + } + ioc = *outioc; + } else { + error_setg(errp, "Server does not support STARTTLS"); + goto fail; + } + } + if (!name) { + TRACE("Using default NBD export name \"\""); + name = ""; + } + if (fixedNewStyle) { + /* Check our desired export is present in the + * server export list. Since NBD_OPT_EXPORT_NAME + * cannot return an error message, running this + * query gives us good error reporting if the + * server required TLS + */ + if (nbd_receive_query_exports(ioc, name, errp) < 0) { + goto fail; + } + } /* write the export name */ magic = cpu_to_be64(magic); - if (write_sync(csock, &magic, sizeof(magic)) != sizeof(magic)) { + if (write_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { error_setg(errp, "Failed to send export name magic"); goto fail; } opt = cpu_to_be32(NBD_OPT_EXPORT_NAME); - if (write_sync(csock, &opt, sizeof(opt)) != sizeof(opt)) { + if (write_sync(ioc, &opt, sizeof(opt)) != sizeof(opt)) { error_setg(errp, "Failed to send export name option number"); goto fail; } namesize = cpu_to_be32(strlen(name)); - if (write_sync(csock, &namesize, sizeof(namesize)) != + if (write_sync(ioc, &namesize, sizeof(namesize)) != sizeof(namesize)) { error_setg(errp, "Failed to send export name length"); goto fail; } - if (write_sync(csock, (char*)name, strlen(name)) != strlen(name)) { + if (write_sync(ioc, (char *)name, strlen(name)) != strlen(name)) { error_setg(errp, "Failed to send export name"); goto fail; } - } else { - TRACE("Checking magic (cli_magic)"); - if (magic != NBD_CLIENT_MAGIC) { - if (magic == NBD_OPTS_MAGIC) { - error_setg(errp, "Server requires an export name"); - } else { - error_setg(errp, "Bad magic received"); - } + if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { + error_setg(errp, "Failed to read export length"); goto fail; } - } - - if (read_sync(csock, &s, sizeof(s)) != sizeof(s)) { - error_setg(errp, "Failed to read export length"); - goto fail; - } - *size = be64_to_cpu(s); - TRACE("Size is %" PRIu64, *size); + *size = be64_to_cpu(s); + TRACE("Size is %" PRIu64, *size); - if (!name) { - if (read_sync(csock, flags, sizeof(*flags)) != sizeof(*flags)) { + if (read_sync(ioc, &exportflags, sizeof(exportflags)) != + sizeof(exportflags)) { error_setg(errp, "Failed to read export flags"); goto fail; } - *flags = be32_to_cpup(flags); - } else { - if (read_sync(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + exportflags = be16_to_cpu(exportflags); + *flags |= exportflags; + TRACE("Export flags are %x", exportflags); + } else if (magic == NBD_CLIENT_MAGIC) { + if (name) { + error_setg(errp, "Server does not support export names"); + goto fail; + } + if (tlscreds) { + error_setg(errp, "Server does not support STARTTLS"); + goto fail; + } + + if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { + error_setg(errp, "Failed to read export length"); + goto fail; + } + *size = be64_to_cpu(s); + TRACE("Size is %" PRIu64, *size); + + if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) { error_setg(errp, "Failed to read export flags"); goto fail; } - *flags |= be16_to_cpu(tmp); + *flags = be32_to_cpup(flags); + } else { + error_setg(errp, "Bad magic received"); + goto fail; } - if (read_sync(csock, &buf, 124) != 124) { + + if (read_sync(ioc, &buf, 124) != 124) { error_setg(errp, "Failed to read reserved block"); goto fail; } @@ -206,11 +534,11 @@ fail: } #ifdef __linux__ -int nbd_init(int fd, int csock, uint32_t flags, off_t size) +int nbd_init(int fd, QIOChannelSocket *sioc, uint32_t flags, off_t size) { TRACE("Setting NBD socket"); - if (ioctl(fd, NBD_SET_SOCK, csock) < 0) { + if (ioctl(fd, NBD_SET_SOCK, sioc->fd) < 0) { int serrno = errno; LOG("Failed to set NBD socket"); return -serrno; @@ -283,7 +611,7 @@ int nbd_client(int fd) return ret; } #else -int nbd_init(int fd, int csock, uint32_t flags, off_t size) +int nbd_init(int fd, QIOChannelSocket *ioc, uint32_t flags, off_t size) { return -ENOTSUP; } @@ -294,7 +622,7 @@ int nbd_client(int fd) } #endif -ssize_t nbd_send_request(int csock, struct nbd_request *request) +ssize_t nbd_send_request(QIOChannel *ioc, struct nbd_request *request) { uint8_t buf[NBD_REQUEST_SIZE]; ssize_t ret; @@ -309,7 +637,7 @@ ssize_t nbd_send_request(int csock, struct nbd_request *request) "{ .from = %" PRIu64", .len = %u, .handle = %" PRIu64", .type=%i}", request->from, request->len, request->handle, request->type); - ret = write_sync(csock, buf, sizeof(buf)); + ret = write_sync(ioc, buf, sizeof(buf)); if (ret < 0) { return ret; } @@ -321,13 +649,13 @@ ssize_t nbd_send_request(int csock, struct nbd_request *request) return 0; } -ssize_t nbd_receive_reply(int csock, struct nbd_reply *reply) +ssize_t nbd_receive_reply(QIOChannel *ioc, struct nbd_reply *reply) { uint8_t buf[NBD_REPLY_SIZE]; uint32_t magic; ssize_t ret; - ret = read_sync(csock, buf, sizeof(buf)); + ret = read_sync(ioc, buf, sizeof(buf)); if (ret < 0) { return ret; } diff --git a/nbd/common.c b/nbd/common.c index 178d4a7ff2..bde673a04a 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -19,47 +19,74 @@ #include "qemu/osdep.h" #include "nbd-internal.h" -ssize_t nbd_wr_sync(int fd, void *buffer, size_t size, bool do_read) +ssize_t nbd_wr_syncv(QIOChannel *ioc, + struct iovec *iov, + size_t niov, + size_t offset, + size_t length, + bool do_read) { - size_t offset = 0; - int err; + ssize_t done = 0; + Error *local_err = NULL; + struct iovec *local_iov = g_new(struct iovec, niov); + struct iovec *local_iov_head = local_iov; + unsigned int nlocal_iov = niov; - if (qemu_in_coroutine()) { - if (do_read) { - return qemu_co_recv(fd, buffer, size); - } else { - return qemu_co_send(fd, buffer, size); - } - } + nlocal_iov = iov_copy(local_iov, nlocal_iov, + iov, niov, + offset, length); - while (offset < size) { + while (nlocal_iov > 0) { ssize_t len; - if (do_read) { - len = qemu_recv(fd, buffer + offset, size - offset, 0); + len = qio_channel_readv(ioc, local_iov, nlocal_iov, &local_err); } else { - len = send(fd, buffer + offset, size - offset, 0); + len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err); } - - if (len < 0) { - err = socket_error(); - - /* recoverable error */ - if (err == EINTR || (offset > 0 && (err == EAGAIN || err == EWOULDBLOCK))) { - continue; + if (len == QIO_CHANNEL_ERR_BLOCK) { + if (qemu_in_coroutine()) { + /* XXX figure out if we can create a variant on + * qio_channel_yield() that works with AIO contexts + * and consider using that in this branch */ + qemu_coroutine_yield(); + } else { + qio_channel_wait(ioc, + do_read ? G_IO_IN : G_IO_OUT); } - - /* unrecoverable error */ - return -err; + continue; + } + if (len < 0) { + TRACE("I/O error: %s", error_get_pretty(local_err)); + error_free(local_err); + /* XXX handle Error objects */ + done = -EIO; + goto cleanup; } - /* eof */ - if (len == 0) { + if (do_read && len == 0) { break; } - offset += len; + iov_discard_front(&local_iov, &nlocal_iov, len); + done += len; } - return offset; + cleanup: + g_free(local_iov_head); + return done; +} + + +void nbd_tls_handshake(Object *src, + Error *err, + void *opaque) +{ + struct NBDTLSHandshakeData *data = opaque; + + if (err) { + TRACE("TLS failed %s", error_get_pretty(err)); + data->error = error_copy(err); + } + data->complete = true; + g_main_loop_quit(data->loop); } diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index c0a657575b..db6ab65ced 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -11,8 +11,10 @@ #define NBD_INTERNAL_H #include "block/nbd.h" #include "sysemu/block-backend.h" +#include "io/channel-tls.h" #include "qemu/coroutine.h" +#include "qemu/iov.h" #include <errno.h> #include <string.h> @@ -29,7 +31,6 @@ #include <linux/fs.h> #endif -#include "qemu/sockets.h" #include "qemu/queue.h" #include "qemu/main-loop.h" @@ -78,6 +79,8 @@ #define NBD_OPT_EXPORT_NAME (1) #define NBD_OPT_ABORT (2) #define NBD_OPT_LIST (3) +#define NBD_OPT_PEEK_EXPORT (4) +#define NBD_OPT_STARTTLS (5) /* 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. @@ -90,24 +93,33 @@ #define NBD_EINVAL 22 #define NBD_ENOSPC 28 -static inline ssize_t read_sync(int fd, void *buffer, size_t size) +static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size) { + struct iovec iov = { .iov_base = buffer, .iov_len = size }; /* Sockets are kept in blocking mode in the negotiation phase. After * that, a non-readable socket simply means that another thread stole * our request/reply. Synchronization is done with recv_coroutine, so * that this is coroutine-safe. */ - return nbd_wr_sync(fd, buffer, size, true); + return nbd_wr_syncv(ioc, &iov, 1, 0, size, true); } -static inline ssize_t write_sync(int fd, void *buffer, size_t size) +static inline ssize_t write_sync(QIOChannel *ioc, void *buffer, size_t size) { - int ret; - do { - /* For writes, we do expect the socket to be writable. */ - ret = nbd_wr_sync(fd, buffer, size, false); - } while (ret == -EAGAIN); - return ret; + struct iovec iov = { .iov_base = buffer, .iov_len = size }; + + return nbd_wr_syncv(ioc, &iov, 1, 0, size, false); } +struct NBDTLSHandshakeData { + GMainLoop *loop; + bool complete; + Error *error; +}; + + +void nbd_tls_handshake(Object *src, + Error *err, + void *opaque); + #endif diff --git a/nbd/server.c b/nbd/server.c index dc1d66fa47..d4225cdb55 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -76,7 +76,10 @@ struct NBDClient { void (*close)(NBDClient *client); NBDExport *exp; - int sock; + QCryptoTLSCreds *tlscreds; + char *tlsaclname; + QIOChannelSocket *sioc; /* The underlying data channel */ + QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ Coroutine *recv_coroutine; @@ -96,45 +99,56 @@ static void nbd_set_handlers(NBDClient *client); static void nbd_unset_handlers(NBDClient *client); static void nbd_update_can_read(NBDClient *client); -static void nbd_negotiate_continue(void *opaque) +static gboolean nbd_negotiate_continue(QIOChannel *ioc, + GIOCondition condition, + void *opaque) { qemu_coroutine_enter(opaque, NULL); + return TRUE; } -static ssize_t nbd_negotiate_read(int fd, void *buffer, size_t size) +static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size) { ssize_t ret; + guint watch; assert(qemu_in_coroutine()); /* Negotiation are always in main loop. */ - qemu_set_fd_handler(fd, nbd_negotiate_continue, NULL, - qemu_coroutine_self()); - ret = read_sync(fd, buffer, size); - qemu_set_fd_handler(fd, NULL, NULL, NULL); + watch = qio_channel_add_watch(ioc, + G_IO_IN, + nbd_negotiate_continue, + qemu_coroutine_self(), + NULL); + ret = read_sync(ioc, buffer, size); + g_source_remove(watch); return ret; } -static ssize_t nbd_negotiate_write(int fd, void *buffer, size_t size) +static ssize_t nbd_negotiate_write(QIOChannel *ioc, void *buffer, size_t size) { ssize_t ret; + guint watch; assert(qemu_in_coroutine()); /* Negotiation are always in main loop. */ - qemu_set_fd_handler(fd, NULL, nbd_negotiate_continue, - qemu_coroutine_self()); - ret = write_sync(fd, buffer, size); - qemu_set_fd_handler(fd, NULL, NULL, NULL); + watch = qio_channel_add_watch(ioc, + G_IO_OUT, + nbd_negotiate_continue, + qemu_coroutine_self(), + NULL); + ret = write_sync(ioc, buffer, size); + g_source_remove(watch); return ret; } -static ssize_t nbd_negotiate_drop_sync(int fd, size_t size) +static ssize_t nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size) { ssize_t ret, dropped = size; uint8_t *buffer = g_malloc(MIN(65536, size)); while (size > 0) { - ret = nbd_negotiate_read(fd, buffer, MIN(65536, size)); + ret = nbd_negotiate_read(ioc, buffer, MIN(65536, size)); if (ret < 0) { g_free(buffer); return ret; @@ -175,66 +189,69 @@ static ssize_t nbd_negotiate_drop_sync(int fd, size_t size) */ -static int nbd_negotiate_send_rep(int csock, uint32_t type, uint32_t opt) +static int nbd_negotiate_send_rep(QIOChannel *ioc, uint32_t type, uint32_t opt) { uint64_t magic; uint32_t len; + TRACE("Reply opt=%x type=%x", type, opt); + magic = cpu_to_be64(NBD_REP_MAGIC); - if (nbd_negotiate_write(csock, &magic, sizeof(magic)) != sizeof(magic)) { + if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) { LOG("write failed (rep magic)"); return -EINVAL; } opt = cpu_to_be32(opt); - if (nbd_negotiate_write(csock, &opt, sizeof(opt)) != sizeof(opt)) { + if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) { LOG("write failed (rep opt)"); return -EINVAL; } type = cpu_to_be32(type); - if (nbd_negotiate_write(csock, &type, sizeof(type)) != sizeof(type)) { + if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) { LOG("write failed (rep type)"); return -EINVAL; } len = cpu_to_be32(0); - if (nbd_negotiate_write(csock, &len, sizeof(len)) != sizeof(len)) { + if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { LOG("write failed (rep data length)"); return -EINVAL; } return 0; } -static int nbd_negotiate_send_rep_list(int csock, NBDExport *exp) +static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp) { uint64_t magic, name_len; uint32_t opt, type, len; + TRACE("Advertizing export name '%s'", exp->name ? exp->name : ""); name_len = strlen(exp->name); magic = cpu_to_be64(NBD_REP_MAGIC); - if (nbd_negotiate_write(csock, &magic, sizeof(magic)) != sizeof(magic)) { + if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) { LOG("write failed (magic)"); return -EINVAL; } opt = cpu_to_be32(NBD_OPT_LIST); - if (nbd_negotiate_write(csock, &opt, sizeof(opt)) != sizeof(opt)) { + if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) { LOG("write failed (opt)"); return -EINVAL; } type = cpu_to_be32(NBD_REP_SERVER); - if (nbd_negotiate_write(csock, &type, sizeof(type)) != sizeof(type)) { + if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) { LOG("write failed (reply type)"); return -EINVAL; } len = cpu_to_be32(name_len + sizeof(len)); - if (nbd_negotiate_write(csock, &len, sizeof(len)) != sizeof(len)) { + if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { LOG("write failed (length)"); return -EINVAL; } len = cpu_to_be32(name_len); - if (nbd_negotiate_write(csock, &len, sizeof(len)) != sizeof(len)) { + if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { LOG("write failed (length)"); return -EINVAL; } - if (nbd_negotiate_write(csock, exp->name, name_len) != name_len) { + if (nbd_negotiate_write(ioc, exp->name, name_len) != name_len) { LOG("write failed (buffer)"); return -EINVAL; } @@ -243,30 +260,29 @@ static int nbd_negotiate_send_rep_list(int csock, NBDExport *exp) static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length) { - int csock; NBDExport *exp; - csock = client->sock; if (length) { - if (nbd_negotiate_drop_sync(csock, length) != length) { + if (nbd_negotiate_drop_sync(client->ioc, length) != length) { return -EIO; } - return nbd_negotiate_send_rep(csock, NBD_REP_ERR_INVALID, NBD_OPT_LIST); + return nbd_negotiate_send_rep(client->ioc, + NBD_REP_ERR_INVALID, NBD_OPT_LIST); } /* For each export, send a NBD_REP_SERVER reply. */ QTAILQ_FOREACH(exp, &exports, next) { - if (nbd_negotiate_send_rep_list(csock, exp)) { + if (nbd_negotiate_send_rep_list(client->ioc, exp)) { return -EINVAL; } } /* Finish with a NBD_REP_ACK. */ - return nbd_negotiate_send_rep(csock, NBD_REP_ACK, NBD_OPT_LIST); + return nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_LIST); } static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length) { - int rc = -EINVAL, csock = client->sock; + int rc = -EINVAL; char name[256]; /* Client sends: @@ -277,12 +293,14 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length) LOG("Bad length received"); goto fail; } - if (nbd_negotiate_read(csock, name, length) != length) { + if (nbd_negotiate_read(client->ioc, name, length) != length) { LOG("read failed"); goto fail; } name[length] = '\0'; + TRACE("Client requested export '%s'", name); + client->exp = nbd_export_find(name); if (!client->exp) { LOG("export not found"); @@ -296,10 +314,59 @@ fail: return rc; } + +static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, + uint32_t length) +{ + QIOChannel *ioc; + QIOChannelTLS *tioc; + struct NBDTLSHandshakeData data = { 0 }; + + TRACE("Setting up TLS"); + ioc = client->ioc; + if (length) { + if (nbd_negotiate_drop_sync(ioc, length) != length) { + return NULL; + } + nbd_negotiate_send_rep(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS); + return NULL; + } + + nbd_negotiate_send_rep(client->ioc, NBD_REP_ACK, NBD_OPT_STARTTLS); + + tioc = qio_channel_tls_new_server(ioc, + client->tlscreds, + client->tlsaclname, + NULL); + if (!tioc) { + return NULL; + } + + TRACE("Starting TLS handshake"); + data.loop = g_main_loop_new(g_main_context_default(), FALSE); + qio_channel_tls_handshake(tioc, + nbd_tls_handshake, + &data, + NULL); + + if (!data.complete) { + g_main_loop_run(data.loop); + } + g_main_loop_unref(data.loop); + if (data.error) { + object_unref(OBJECT(tioc)); + error_free(data.error); + return NULL; + } + + return QIO_CHANNEL(tioc); +} + + static int nbd_negotiate_options(NBDClient *client) { - int csock = client->sock; uint32_t flags; + bool fixedNewstyle = false; /* Client sends: [ 0 .. 3] client flags @@ -315,23 +382,30 @@ static int nbd_negotiate_options(NBDClient *client) ... Rest of request */ - if (nbd_negotiate_read(csock, &flags, sizeof(flags)) != sizeof(flags)) { + if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) != + sizeof(flags)) { LOG("read failed"); return -EIO; } TRACE("Checking client flags"); be32_to_cpus(&flags); - if (flags != 0 && flags != NBD_FLAG_C_FIXED_NEWSTYLE) { - LOG("Bad client flags received"); + if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { + TRACE("Support supports fixed newstyle handshake"); + fixedNewstyle = true; + flags &= ~NBD_FLAG_C_FIXED_NEWSTYLE; + } + if (flags != 0) { + TRACE("Unknown client flags 0x%x received", flags); return -EIO; } while (1) { int ret; - uint32_t tmp, length; + uint32_t clientflags, length; uint64_t magic; - if (nbd_negotiate_read(csock, &magic, sizeof(magic)) != sizeof(magic)) { + if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) != + sizeof(magic)) { LOG("read failed"); return -EINVAL; } @@ -341,38 +415,89 @@ static int nbd_negotiate_options(NBDClient *client) return -EINVAL; } - if (nbd_negotiate_read(csock, &tmp, sizeof(tmp)) != sizeof(tmp)) { + if (nbd_negotiate_read(client->ioc, &clientflags, + sizeof(clientflags)) != sizeof(clientflags)) { LOG("read failed"); return -EINVAL; } + clientflags = be32_to_cpu(clientflags); - if (nbd_negotiate_read(csock, &length, - sizeof(length)) != sizeof(length)) { + if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) != + sizeof(length)) { LOG("read failed"); return -EINVAL; } length = be32_to_cpu(length); - TRACE("Checking option"); - switch (be32_to_cpu(tmp)) { - case NBD_OPT_LIST: - ret = nbd_negotiate_handle_list(client, length); - if (ret < 0) { - return ret; + TRACE("Checking option 0x%x", clientflags); + if (client->tlscreds && + client->ioc == (QIOChannel *)client->sioc) { + QIOChannel *tioc; + if (!fixedNewstyle) { + TRACE("Unsupported option 0x%x", clientflags); + return -EINVAL; + } + switch (clientflags) { + case NBD_OPT_STARTTLS: + tioc = nbd_negotiate_handle_starttls(client, length); + if (!tioc) { + return -EIO; + } + object_unref(OBJECT(client->ioc)); + client->ioc = QIO_CHANNEL(tioc); + break; + + default: + TRACE("Option 0x%x not permitted before TLS", clientflags); + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_TLS_REQD, + clientflags); + return -EINVAL; + } + } else if (fixedNewstyle) { + switch (clientflags) { + case NBD_OPT_LIST: + ret = nbd_negotiate_handle_list(client, length); + if (ret < 0) { + return ret; + } + break; + + case NBD_OPT_ABORT: + return -EINVAL; + + case NBD_OPT_EXPORT_NAME: + return nbd_negotiate_handle_export_name(client, length); + + case NBD_OPT_STARTTLS: + if (client->tlscreds) { + TRACE("TLS already enabled"); + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_INVALID, + clientflags); + } else { + TRACE("TLS not configured"); + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_POLICY, + clientflags); + } + return -EINVAL; + default: + TRACE("Unsupported option 0x%x", clientflags); + nbd_negotiate_send_rep(client->ioc, NBD_REP_ERR_UNSUP, + clientflags); + return -EINVAL; + } + } else { + /* + * If broken new-style we should drop the connection + * for anything except NBD_OPT_EXPORT_NAME + */ + switch (clientflags) { + case NBD_OPT_EXPORT_NAME: + return nbd_negotiate_handle_export_name(client, length); + + default: + TRACE("Unsupported option 0x%x", clientflags); + return -EINVAL; } - break; - - case NBD_OPT_ABORT: - return -EINVAL; - - case NBD_OPT_EXPORT_NAME: - return nbd_negotiate_handle_export_name(client, length); - - default: - tmp = be32_to_cpu(tmp); - LOG("Unsupported option 0x%x", tmp); - nbd_negotiate_send_rep(client->sock, NBD_REP_ERR_UNSUP, tmp); - return -EINVAL; } } } @@ -385,13 +510,13 @@ typedef struct { static coroutine_fn int nbd_negotiate(NBDClientNewData *data) { NBDClient *client = data->client; - int csock = client->sock; char buf[8 + 8 + 8 + 128]; int rc; const int myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA); + bool oldStyle; - /* Negotiation header without options: + /* Old style negotiation header without options [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (NBD_CLIENT_MAGIC) [16 .. 23] size @@ -399,23 +524,25 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) [26 .. 27] export flags [28 .. 151] reserved (0) - Negotiation header with options, part 1: + New style negotiation header with options [ 0 .. 7] passwd ("NBDMAGIC") [ 8 .. 15] magic (NBD_OPTS_MAGIC) [16 .. 17] server flags (0) - - part 2 (after options are sent): + ....options sent.... [18 .. 25] size [26 .. 27] export flags [28 .. 151] reserved (0) */ + qio_channel_set_blocking(client->ioc, false, NULL); rc = -EINVAL; TRACE("Beginning negotiation."); memset(buf, 0, sizeof(buf)); memcpy(buf, "NBDMAGIC", 8); - if (client->exp) { + + oldStyle = client->exp != NULL && !client->tlscreds; + if (oldStyle) { assert ((client->exp->nbdflags & ~65535) == 0); stq_be_p(buf + 8, NBD_CLIENT_MAGIC); stq_be_p(buf + 16, client->exp->size); @@ -425,13 +552,17 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE); } - if (client->exp) { - if (nbd_negotiate_write(csock, buf, sizeof(buf)) != sizeof(buf)) { + if (oldStyle) { + if (client->tlscreds) { + TRACE("TLS cannot be enabled with oldstyle protocol"); + goto fail; + } + if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) { LOG("write failed"); goto fail; } } else { - if (nbd_negotiate_write(csock, buf, 18) != 18) { + if (nbd_negotiate_write(client->ioc, buf, 18) != 18) { LOG("write failed"); goto fail; } @@ -444,8 +575,8 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) assert ((client->exp->nbdflags & ~65535) == 0); stq_be_p(buf + 18, client->exp->size); stw_be_p(buf + 26, client->exp->nbdflags | myflags); - if (nbd_negotiate_write(csock, buf + 18, - sizeof(buf) - 18) != sizeof(buf) - 18) { + if (nbd_negotiate_write(client->ioc, buf + 18, sizeof(buf) - 18) != + sizeof(buf) - 18) { LOG("write failed"); goto fail; } @@ -475,13 +606,13 @@ int nbd_disconnect(int fd) } #endif -static ssize_t nbd_receive_request(int csock, struct nbd_request *request) +static ssize_t nbd_receive_request(QIOChannel *ioc, struct nbd_request *request) { uint8_t buf[NBD_REQUEST_SIZE]; uint32_t magic; ssize_t ret; - ret = read_sync(csock, buf, sizeof(buf)); + ret = read_sync(ioc, buf, sizeof(buf)); if (ret < 0) { return ret; } @@ -516,7 +647,7 @@ static ssize_t nbd_receive_request(int csock, struct nbd_request *request) return 0; } -static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply) +static ssize_t nbd_send_reply(QIOChannel *ioc, struct nbd_reply *reply) { uint8_t buf[NBD_REPLY_SIZE]; ssize_t ret; @@ -534,7 +665,7 @@ static ssize_t nbd_send_reply(int csock, struct nbd_reply *reply) TRACE("Sending response to client"); - ret = write_sync(csock, buf, sizeof(buf)); + ret = write_sync(ioc, buf, sizeof(buf)); if (ret < 0) { return ret; } @@ -562,8 +693,12 @@ void nbd_client_put(NBDClient *client) assert(client->closing); nbd_unset_handlers(client); - close(client->sock); - client->sock = -1; + object_unref(OBJECT(client->sioc)); + object_unref(OBJECT(client->ioc)); + if (client->tlscreds) { + object_unref(OBJECT(client->tlscreds)); + } + g_free(client->tlsaclname); if (client->exp) { QTAILQ_REMOVE(&client->exp->clients, client, next); nbd_export_put(client->exp); @@ -583,7 +718,8 @@ static void client_close(NBDClient *client) /* Force requests to finish. They will drop their own references, * then we'll close the socket and free the NBDClient. */ - shutdown(client->sock, 2); + qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, + NULL); /* Also tell the client, so that they release their reference. */ if (client->close) { @@ -789,25 +925,25 @@ static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, int len) { NBDClient *client = req->client; - int csock = client->sock; ssize_t rc, ret; + g_assert(qemu_in_coroutine()); qemu_co_mutex_lock(&client->send_lock); client->send_coroutine = qemu_coroutine_self(); nbd_set_handlers(client); if (!len) { - rc = nbd_send_reply(csock, reply); + rc = nbd_send_reply(client->ioc, reply); } else { - socket_set_cork(csock, 1); - rc = nbd_send_reply(csock, reply); + qio_channel_set_cork(client->ioc, true); + rc = nbd_send_reply(client->ioc, reply); if (rc >= 0) { - ret = qemu_co_send(csock, req->data, len); + ret = write_sync(client->ioc, req->data, len); if (ret != len) { rc = -EIO; } } - socket_set_cork(csock, 0); + qio_channel_set_cork(client->ioc, false); } client->send_coroutine = NULL; @@ -819,14 +955,14 @@ static ssize_t nbd_co_send_reply(NBDRequest *req, struct nbd_reply *reply, static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *request) { NBDClient *client = req->client; - int csock = client->sock; uint32_t command; ssize_t rc; + g_assert(qemu_in_coroutine()); client->recv_coroutine = qemu_coroutine_self(); nbd_update_can_read(client); - rc = nbd_receive_request(csock, request); + rc = nbd_receive_request(client->ioc, request); if (rc < 0) { if (rc != -EAGAIN) { rc = -EIO; @@ -861,7 +997,7 @@ static ssize_t nbd_co_receive_request(NBDRequest *req, struct nbd_request *reque if (command == NBD_CMD_WRITE) { TRACE("Reading %u byte(s)", request->len); - if (qemu_co_recv(csock, req->data, request->len) != request->len) { + if (read_sync(client->ioc, req->data, request->len) != request->len) { LOG("reading from socket failed"); rc = -EIO; goto out; @@ -1056,7 +1192,7 @@ static void nbd_restart_write(void *opaque) static void nbd_set_handlers(NBDClient *client) { if (client->exp && client->exp->ctx) { - aio_set_fd_handler(client->exp->ctx, client->sock, + aio_set_fd_handler(client->exp->ctx, client->sioc->fd, true, client->can_read ? nbd_read : NULL, client->send_coroutine ? nbd_restart_write : NULL, @@ -1067,7 +1203,7 @@ static void nbd_set_handlers(NBDClient *client) static void nbd_unset_handlers(NBDClient *client) { if (client->exp && client->exp->ctx) { - aio_set_fd_handler(client->exp->ctx, client->sock, + aio_set_fd_handler(client->exp->ctx, client->sioc->fd, true, NULL, NULL, NULL); } } @@ -1109,7 +1245,11 @@ out: g_free(data); } -void nbd_client_new(NBDExport *exp, int csock, void (*close_fn)(NBDClient *)) +void nbd_client_new(NBDExport *exp, + QIOChannelSocket *sioc, + QCryptoTLSCreds *tlscreds, + const char *tlsaclname, + void (*close_fn)(NBDClient *)) { NBDClient *client; NBDClientNewData *data = g_new(NBDClientNewData, 1); @@ -1117,7 +1257,15 @@ void nbd_client_new(NBDExport *exp, int csock, void (*close_fn)(NBDClient *)) client = g_malloc0(sizeof(NBDClient)); client->refcount = 1; client->exp = exp; - client->sock = csock; + client->tlscreds = tlscreds; + if (tlscreds) { + object_ref(OBJECT(client->tlscreds)); + } + client->tlsaclname = g_strdup(tlsaclname); + client->sioc = sioc; + object_ref(OBJECT(client->sioc)); + client->ioc = QIO_CHANNEL(sioc); + object_ref(OBJECT(client->ioc)); client->can_read = true; client->close = close_fn; diff --git a/qapi/block.json b/qapi/block.json index ed61f82359..58e6b301bf 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -146,13 +146,15 @@ # QEMU instance could refer to them as "nbd:HOST:PORT:exportname=NAME". # # @addr: Address on which to listen. +# @tls-creds: (optional) ID of the TLS credentials object. Since 2.6 # # Returns: error if the server is already running. # # Since: 1.3.0 ## { 'command': 'nbd-server-start', - 'data': { 'addr': 'SocketAddress' } } + 'data': { 'addr': 'SocketAddress', + '*tls-creds': 'str'} } ## # @nbd-server-add: diff --git a/qemu-char.c b/qemu-char.c index 1b7d5dac76..ad11b75e3d 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -896,13 +896,13 @@ static int io_channel_send_full(QIOChannel *ioc, ioc, &iov, 1, fds, nfds, NULL); if (ret == QIO_CHANNEL_ERR_BLOCK) { - errno = EAGAIN; - return -1; - } else if (ret < 0) { if (offset) { return offset; } + errno = EAGAIN; + return -1; + } else if (ret < 0) { errno = EINVAL; return -1; } @@ -1171,7 +1171,6 @@ typedef struct { int connected; guint timer_tag; guint open_tag; - int slave_fd; } PtyCharDriver; static void pty_chr_update_read_handler_locked(CharDriverState *chr); @@ -1348,7 +1347,6 @@ static void pty_chr_close(struct CharDriverState *chr) qemu_mutex_lock(&chr->chr_write_lock); pty_chr_state(chr, 0); - close(s->slave_fd); object_unref(OBJECT(s->ioc)); if (s->timer_tag) { g_source_remove(s->timer_tag); @@ -1376,6 +1374,7 @@ static CharDriverState *qemu_chr_open_pty(const char *id, return NULL; } + close(slave_fd); qemu_set_nonblock(master_fd); chr = qemu_chr_alloc(common, errp); @@ -1400,7 +1399,6 @@ static CharDriverState *qemu_chr_open_pty(const char *id, chr->explicit_be_open = true; s->ioc = QIO_CHANNEL(qio_channel_file_new_fd(master_fd)); - s->slave_fd = slave_fd; s->timer_tag = 0; return chr; diff --git a/qemu-img.c b/qemu-img.c index 163d8c1664..7030107da2 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3104,6 +3104,7 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + module_call_init(MODULE_INIT_QOM); bdrv_init(); if (argc < 2) { error_exit("Not enough arguments"); @@ -394,6 +394,7 @@ int main(int argc, char **argv) progname = basename(argv[0]); qemu_init_exec_dir(argv[0]); + module_call_init(MODULE_INIT_QOM); bdrv_init(); while ((c = getopt_long(argc, argv, sopt, lopt, &opt_index)) != -1) { diff --git a/qemu-nbd.c b/qemu-nbd.c index d374015125..933ca4a368 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -22,17 +22,17 @@ #include "block/block_int.h" #include "block/nbd.h" #include "qemu/main-loop.h" -#include "qemu/sockets.h" #include "qemu/error-report.h" +#include "qemu/config-file.h" #include "block/snapshot.h" #include "qapi/util.h" #include "qapi/qmp/qstring.h" +#include "qom/object_interfaces.h" +#include "io/channel-socket.h" #include <getopt.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/tcp.h> -#include <arpa/inet.h> +#include <sys/types.h> +#include <signal.h> #include <libgen.h> #include <pthread.h> @@ -41,8 +41,11 @@ #define QEMU_NBD_OPT_AIO 2 #define QEMU_NBD_OPT_DISCARD 3 #define QEMU_NBD_OPT_DETECT_ZEROES 4 +#define QEMU_NBD_OPT_OBJECT 5 +#define QEMU_NBD_OPT_TLSCREDS 6 static NBDExport *exp; +static bool newproto; static int verbose; static char *srcpath; static SocketAddress *saddr; @@ -50,7 +53,9 @@ static int persistent = 0; static enum { RUNNING, TERMINATE, TERMINATING, TERMINATED } state; static int shared = 1; static int nb_fds; -static int server_fd; +static QIOChannelSocket *server_ioc; +static int server_watch = -1; +static QCryptoTLSCreds *tlscreds; static void usage(const char *name) { @@ -74,6 +79,9 @@ static void usage(const char *name) " -o, --offset=OFFSET offset into the image\n" " -P, --partition=NUM only expose partition NUM\n" "\n" +"General purpose options:\n" +" --object type,id=ID,... define an object such as 'secret' for providing\n" +" passwords and/or encryption keys\n" #ifdef __linux__ "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" @@ -230,19 +238,22 @@ static void *nbd_client_thread(void *arg) char *device = arg; off_t size; uint32_t nbdflags; - int fd, sock; + QIOChannelSocket *sioc; + int fd; int ret; pthread_t show_parts_thread; Error *local_error = NULL; - - sock = socket_connect(saddr, &local_error, NULL, NULL); - if (sock < 0) { + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, + saddr, + &local_error) < 0) { error_report_err(local_error); goto out; } - ret = nbd_receive_negotiate(sock, NULL, &nbdflags, + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, &nbdflags, + NULL, NULL, NULL, &size, &local_error); if (ret < 0) { if (local_error) { @@ -258,7 +269,7 @@ static void *nbd_client_thread(void *arg) goto out_socket; } - ret = nbd_init(fd, sock, nbdflags, size); + ret = nbd_init(fd, sioc, nbdflags, size); if (ret < 0) { goto out_fd; } @@ -279,13 +290,14 @@ static void *nbd_client_thread(void *arg) goto out_fd; } close(fd); + object_unref(OBJECT(sioc)); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; out_fd: close(fd); out_socket: - closesocket(sock); + object_unref(OBJECT(sioc)); out: kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; @@ -302,7 +314,7 @@ static void nbd_export_closed(NBDExport *exp) state = TERMINATED; } -static void nbd_update_server_fd_handler(int fd); +static void nbd_update_server_watch(void); static void nbd_client_closed(NBDClient *client) { @@ -310,37 +322,48 @@ static void nbd_client_closed(NBDClient *client) if (nb_fds == 0 && !persistent && state == RUNNING) { state = TERMINATE; } - nbd_update_server_fd_handler(server_fd); + nbd_update_server_watch(); nbd_client_put(client); } -static void nbd_accept(void *opaque) +static gboolean nbd_accept(QIOChannel *ioc, GIOCondition cond, gpointer opaque) { - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); + QIOChannelSocket *cioc; - int fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - if (fd < 0) { - perror("accept"); - return; + cioc = qio_channel_socket_accept(QIO_CHANNEL_SOCKET(ioc), + NULL); + if (!cioc) { + return TRUE; } if (state >= TERMINATE) { - close(fd); - return; + object_unref(OBJECT(cioc)); + return TRUE; } nb_fds++; - nbd_update_server_fd_handler(server_fd); - nbd_client_new(exp, fd, nbd_client_closed); + nbd_update_server_watch(); + nbd_client_new(newproto ? NULL : exp, cioc, + tlscreds, NULL, nbd_client_closed); + object_unref(OBJECT(cioc)); + + return TRUE; } -static void nbd_update_server_fd_handler(int fd) +static void nbd_update_server_watch(void) { if (nbd_can_accept()) { - qemu_set_fd_handler(fd, nbd_accept, NULL, (void *)(uintptr_t)fd); + if (server_watch == -1) { + server_watch = qio_channel_add_watch(QIO_CHANNEL(server_ioc), + G_IO_IN, + nbd_accept, + NULL, NULL); + } } else { - qemu_set_fd_handler(fd, NULL, NULL, NULL); + if (server_watch != -1) { + g_source_remove(server_watch); + server_watch = -1; + } } } @@ -371,6 +394,47 @@ static SocketAddress *nbd_build_socket_address(const char *sockpath, } +static QemuOptsList qemu_object_opts = { + .name = "object", + .implied_opt_name = "qom-type", + .head = QTAILQ_HEAD_INITIALIZER(qemu_object_opts.head), + .desc = { + { } + }, +}; + + + +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +{ + Object *obj; + QCryptoTLSCreds *creds; + + obj = object_resolve_path_component( + object_get_objects_root(), id); + if (!obj) { + error_setg(errp, "No TLS credentials with id '%s'", + id); + return NULL; + } + creds = (QCryptoTLSCreds *) + object_dynamic_cast(obj, TYPE_QCRYPTO_TLS_CREDS); + if (!creds) { + error_setg(errp, "Object with id '%s' is not TLS credentials", + id); + return NULL; + } + + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { + error_setg(errp, + "Expecting TLS credentials with a server endpoint"); + return NULL; + } + object_ref(obj); + return creds; +} + + int main(int argc, char **argv) { BlockBackend *blk; @@ -385,7 +449,7 @@ int main(int argc, char **argv) off_t fd_size; QemuOpts *sn_opts = NULL; const char *sn_id_or_name = NULL; - const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:"; struct option lopt[] = { { "help", 0, NULL, 'h' }, { "version", 0, NULL, 'V' }, @@ -408,6 +472,9 @@ int main(int argc, char **argv) { "format", 1, NULL, 'f' }, { "persistent", 0, NULL, 't' }, { "verbose", 0, NULL, 'v' }, + { "object", 1, NULL, QEMU_NBD_OPT_OBJECT }, + { "export-name", 1, NULL, 'x' }, + { "tls-creds", 1, NULL, QEMU_NBD_OPT_TLSCREDS }, { NULL, 0, NULL, 0 } }; int ch; @@ -416,7 +483,6 @@ int main(int argc, char **argv) int flags = BDRV_O_RDWR; int partition = -1; int ret = 0; - int fd; bool seen_cache = false; bool seen_discard = false; bool seen_aio = false; @@ -425,6 +491,8 @@ int main(int argc, char **argv) Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; + const char *export_name = NULL; + const char *tlscredsid = NULL; /* The client thread uses SIGTERM to interrupt the server. A signal * handler ensures that "qemu-nbd -v -c" exits with a nice status code. @@ -433,6 +501,8 @@ int main(int argc, char **argv) memset(&sa_sigterm, 0, sizeof(sa_sigterm)); sa_sigterm.sa_handler = termsig_handler; sigaction(SIGTERM, &sa_sigterm, NULL); + module_call_init(MODULE_INIT_QOM); + qemu_add_opts(&qemu_object_opts); qemu_init_exec_dir(argv[0]); while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) { @@ -574,6 +644,9 @@ int main(int argc, char **argv) case 't': persistent = 1; break; + case 'x': + export_name = optarg; + break; case 'v': verbose = 1; break; @@ -588,6 +661,17 @@ int main(int argc, char **argv) case '?': error_report("Try `%s --help' for more information.", argv[0]); exit(EXIT_FAILURE); + case QEMU_NBD_OPT_OBJECT: { + QemuOpts *opts; + opts = qemu_opts_parse_noisily(&qemu_object_opts, + optarg, true); + if (!opts) { + exit(EXIT_FAILURE); + } + } break; + case QEMU_NBD_OPT_TLSCREDS: + tlscredsid = optarg; + break; } } @@ -597,16 +681,45 @@ int main(int argc, char **argv) exit(EXIT_FAILURE); } + if (qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, &local_err)) { + error_report_err(local_err); + exit(EXIT_FAILURE); + } + + if (tlscredsid) { + if (sockpath) { + error_report("TLS is only supported with IPv4/IPv6"); + exit(EXIT_FAILURE); + } + if (device) { + error_report("TLS is not supported with a host device"); + exit(EXIT_FAILURE); + } + if (!export_name) { + /* Set the default NBD protocol export name, since + * we *must* use new style protocol for TLS */ + export_name = ""; + } + tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); + if (local_err) { + error_report("Failed to get TLS creds %s", + error_get_pretty(local_err)); + exit(EXIT_FAILURE); + } + } + if (disconnect) { - fd = open(argv[optind], O_RDWR); - if (fd < 0) { + int nbdfd = open(argv[optind], O_RDWR); + if (nbdfd < 0) { error_report("Cannot open %s: %s", argv[optind], strerror(errno)); exit(EXIT_FAILURE); } - nbd_disconnect(fd); + nbd_disconnect(nbdfd); - close(fd); + close(nbdfd); printf("%s disconnected\n", argv[optind]); @@ -738,9 +851,14 @@ int main(int argc, char **argv) error_report_err(local_err); exit(EXIT_FAILURE); } + if (export_name) { + nbd_export_set_name(exp, export_name); + newproto = true; + } - fd = socket_listen(saddr, &local_err); - if (fd < 0) { + server_ioc = qio_channel_socket_new(); + if (qio_channel_socket_listen_sync(server_ioc, saddr, &local_err) < 0) { + object_unref(OBJECT(server_ioc)); error_report_err(local_err); return 1; } @@ -758,8 +876,7 @@ int main(int argc, char **argv) memset(&client_thread, 0, sizeof(client_thread)); } - server_fd = fd; - nbd_update_server_fd_handler(fd); + nbd_update_server_watch(); /* now when the initialization is (almost) complete, chdir("/") * to free any busy filesystems */ diff --git a/qemu-nbd.texi b/qemu-nbd.texi index 0027841ecb..227a73ca36 100644 --- a/qemu-nbd.texi +++ b/qemu-nbd.texi @@ -18,6 +18,13 @@ Export a QEMU disk image using the NBD protocol. @var{dev} is an NBD device. @table @option +@item --object type,id=@var{id},...props... +Define a new instance of the @var{type} object class identified by @var{id}. +See the @code{qemu(1)} manual page for full details of the properties +supported. The common object types that it makes sense to define are the +@code{secret} object, which is used to supply passwords and/or encryption +keys, and the @code{tls-creds} object, which is used to supply TLS +credentials for the qemu-nbd server. @item -p, --port=@var{port} The TCP port to listen on (default @samp{10809}) @item -o, --offset=@var{offset} @@ -67,6 +74,13 @@ Disconnect the device @var{dev} Allow up to @var{num} clients to share the device (default @samp{1}) @item -t, --persistent Don't exit on the last connection +@item -x NAME, --export-name=NAME +Set the NBD volume export name. This switches the server to use +the new style NBD protocol negotiation +@item --tls-creds=ID +Enable mandatory TLS encryption for the server by setting the ID +of the TLS credentials object previously created with the --object +option. @item -v, --verbose Display extra debugging information @item -h, --help diff --git a/qmp-commands.hx b/qmp-commands.hx index 020e5ee96c..9fb0d788bc 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -3825,7 +3825,7 @@ EQMP { .name = "nbd-server-start", - .args_type = "addr:q", + .args_type = "addr:q,tls-creds:s?", .mhandler.cmd_new = qmp_marshal_nbd_server_start, }, { @@ -633,65 +633,13 @@ void qmp_add_client(const char *protocol, const char *fdname, close(fd); } -void object_add(const char *type, const char *id, const QDict *qdict, - Visitor *v, Error **errp) -{ - Object *obj; - ObjectClass *klass; - const QDictEntry *e; - Error *local_err = NULL; - - klass = object_class_by_name(type); - if (!klass) { - error_setg(errp, "invalid object type: %s", type); - return; - } - - if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { - error_setg(errp, "object type '%s' isn't supported by object-add", - type); - return; - } - - if (object_class_is_abstract(klass)) { - error_setg(errp, "object type '%s' is abstract", type); - return; - } - - obj = object_new(type); - if (qdict) { - for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { - object_property_set(obj, v, e->key, &local_err); - if (local_err) { - goto out; - } - } - } - - object_property_add_child(object_get_objects_root(), - id, obj, &local_err); - if (local_err) { - goto out; - } - - user_creatable_complete(obj, &local_err); - if (local_err) { - object_property_del(object_get_objects_root(), - id, &error_abort); - goto out; - } -out: - if (local_err) { - error_propagate(errp, local_err); - } - object_unref(obj); -} void qmp_object_add(const char *type, const char *id, bool has_props, QObject *props, Error **errp) { const QDict *pdict = NULL; QmpInputVisitor *qiv; + Object *obj; if (props) { pdict = qobject_to_qdict(props); @@ -702,27 +650,17 @@ void qmp_object_add(const char *type, const char *id, } qiv = qmp_input_visitor_new(props); - object_add(type, id, pdict, qmp_input_get_visitor(qiv), errp); + obj = user_creatable_add_type(type, id, pdict, + qmp_input_get_visitor(qiv), errp); qmp_input_visitor_cleanup(qiv); + if (obj) { + object_unref(obj); + } } void qmp_object_del(const char *id, Error **errp) { - Object *container; - Object *obj; - - container = object_get_objects_root(); - obj = object_resolve_path_component(container, id); - if (!obj) { - error_setg(errp, "object id not found"); - return; - } - - if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) { - error_setg(errp, "%s is in use, can not be deleted", id); - return; - } - object_unparent(obj); + user_creatable_del(id, errp); } MemoryDeviceInfoList *qmp_query_memory_devices(Error **errp) diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index f1218f0cc1..c2f6e2998e 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -1,6 +1,9 @@ #include "qemu/osdep.h" #include "qom/object_interfaces.h" #include "qemu/module.h" +#include "qapi-visit.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi/opts-visitor.h" void user_creatable_complete(Object *obj, Error **errp) { @@ -31,6 +34,177 @@ bool user_creatable_can_be_deleted(UserCreatable *uc, Error **errp) } } + +Object *user_creatable_add(const QDict *qdict, + Visitor *v, Error **errp) +{ + char *type = NULL; + char *id = NULL; + Object *obj = NULL; + Error *local_err = NULL, *end_err = NULL; + QDict *pdict; + + pdict = qdict_clone_shallow(qdict); + + visit_start_struct(v, NULL, NULL, 0, &local_err); + if (local_err) { + goto out; + } + + qdict_del(pdict, "qom-type"); + visit_type_str(v, "qom-type", &type, &local_err); + if (local_err) { + goto out_visit; + } + + qdict_del(pdict, "id"); + visit_type_str(v, "id", &id, &local_err); + if (local_err) { + goto out_visit; + } + + obj = user_creatable_add_type(type, id, pdict, v, &local_err); + if (local_err) { + goto out_visit; + } + + out_visit: + visit_end_struct(v, &end_err); + if (end_err) { + error_propagate(&local_err, end_err); + if (obj) { + user_creatable_del(id, NULL); + } + goto out; + } + +out: + QDECREF(pdict); + g_free(id); + g_free(type); + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + return obj; +} + + +Object *user_creatable_add_type(const char *type, const char *id, + const QDict *qdict, + Visitor *v, Error **errp) +{ + Object *obj; + ObjectClass *klass; + const QDictEntry *e; + Error *local_err = NULL; + + klass = object_class_by_name(type); + if (!klass) { + error_setg(errp, "invalid object type: %s", type); + return NULL; + } + + if (!object_class_dynamic_cast(klass, TYPE_USER_CREATABLE)) { + error_setg(errp, "object type '%s' isn't supported by object-add", + type); + return NULL; + } + + if (object_class_is_abstract(klass)) { + error_setg(errp, "object type '%s' is abstract", type); + return NULL; + } + + obj = object_new(type); + if (qdict) { + for (e = qdict_first(qdict); e; e = qdict_next(qdict, e)) { + object_property_set(obj, v, e->key, &local_err); + if (local_err) { + goto out; + } + } + } + + object_property_add_child(object_get_objects_root(), + id, obj, &local_err); + if (local_err) { + goto out; + } + + user_creatable_complete(obj, &local_err); + if (local_err) { + object_property_del(object_get_objects_root(), + id, &error_abort); + goto out; + } +out: + if (local_err) { + error_propagate(errp, local_err); + object_unref(obj); + return NULL; + } + return obj; +} + + +Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) +{ + OptsVisitor *ov; + QDict *pdict; + Object *obj = NULL; + + ov = opts_visitor_new(opts); + pdict = qemu_opts_to_qdict(opts, NULL); + + obj = user_creatable_add(pdict, opts_get_visitor(ov), errp); + opts_visitor_cleanup(ov); + QDECREF(pdict); + return obj; +} + + +int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) +{ + bool (*type_predicate)(const char *) = opaque; + Object *obj = NULL; + const char *type; + + type = qemu_opt_get(opts, "qom-type"); + if (type && type_predicate && + !type_predicate(type)) { + return 0; + } + + obj = user_creatable_add_opts(opts, errp); + if (!obj) { + return -1; + } + object_unref(obj); + return 0; +} + + +void user_creatable_del(const char *id, Error **errp) +{ + Object *container; + Object *obj; + + container = object_get_objects_root(); + obj = object_resolve_path_component(container, id); + if (!obj) { + error_setg(errp, "object '%s' not found", id); + return; + } + + if (!user_creatable_can_be_deleted(USER_CREATABLE(obj), errp)) { + error_setg(errp, "object '%s' is in use, can not be deleted", id); + return; + } + object_unparent(obj); +} + static void register_types(void) { static const TypeInfo uc_interface_info = { diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 257126f91b..c26f76ed09 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1715,11 +1715,15 @@ sub process { # 1. with a type on the left -- int [] a; # 2. at the beginning of a line for slice initialisers -- [0...10] = 5, # 3. inside a curly brace -- = { [0...10] = 5 } +# 4. after a comma -- [1] = 5, [2] = 6 +# 5. in a macro definition -- #define abc(x) [x] = y while ($line =~ /(.*?\s)\[/g) { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /{\s+$/) { + $prefix !~ /{\s+$/ && + $prefix !~ /\#\s*define[^(]*\([^)]*\)\s+$/ && + $prefix !~ /,\s+$/) { ERROR("space prohibited before open square bracket '['\n" . $herecurr); } } diff --git a/scripts/feature_to_c.sh b/scripts/feature_to_c.sh index 888548e58b..fb1f3363f7 100644 --- a/scripts/feature_to_c.sh +++ b/scripts/feature_to_c.sh @@ -36,7 +36,7 @@ for input; do arrayname=xml_feature_`echo $input | sed 's,.*/,,; s/[-.]/_/g'` ${AWK:-awk} 'BEGIN { n = 0 - printf "#include \"config.h\"\n" + printf "#include \"qemu/osdep.h\"\n" printf "#include \"qemu-common.h\"\n" printf "#include \"exec/gdbstub.h\"\n" print "static const char '$arrayname'[] = {" diff --git a/scripts/qapi-commands.py b/scripts/qapi-commands.py index 91c5a4e8cd..f831621843 100644 --- a/scripts/qapi-commands.py +++ b/scripts/qapi-commands.py @@ -297,6 +297,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/module.h" #include "qapi/qmp/types.h" diff --git a/scripts/qapi-event.py b/scripts/qapi-event.py index 07bcb73d3d..544ae1218d 100644 --- a/scripts/qapi-event.py +++ b/scripts/qapi-event.py @@ -159,6 +159,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qemu-common.h" #include "%(prefix)sqapi-event.h" #include "%(prefix)sqapi-visit.h" diff --git a/scripts/qapi-introspect.py b/scripts/qapi-introspect.py index 64f2cd0631..e0f926be04 100644 --- a/scripts/qapi-introspect.py +++ b/scripts/qapi-introspect.py @@ -1,7 +1,7 @@ # # QAPI introspection generator # -# Copyright (C) 2015 Red Hat, Inc. +# Copyright (C) 2015-2016 Red Hat, Inc. # # Authors: # Markus Armbruster <armbru@redhat.com> @@ -204,6 +204,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "%(prefix)sqmp-introspect.h" ''', diff --git a/scripts/qapi-types.py b/scripts/qapi-types.py index d3f631a2f4..7b0dca8c72 100644 --- a/scripts/qapi-types.py +++ b/scripts/qapi-types.py @@ -279,6 +279,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" #include "%(prefix)sqapi-types.h" #include "%(prefix)sqapi-visit.h" @@ -287,8 +288,6 @@ fdef.write(mcgen(''' # To avoid circular headers, use only typedefs.h here, not qobject.h fdecl.write(mcgen(''' -#include <stdbool.h> -#include <stdint.h> #include "qemu/typedefs.h" ''')) diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py index 0fdcebcca8..0cc9b08b14 100644 --- a/scripts/qapi-visit.py +++ b/scripts/qapi-visit.py @@ -435,6 +435,7 @@ h_comment = ''' c_comment, h_comment) fdef.write(mcgen(''' +#include "qemu/osdep.h" #include "qemu-common.h" #include "%(prefix)sqapi-visit.h" ''', diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index e8c2cd57e9..3246c20015 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -42,7 +42,8 @@ def generate_h(event): def generate_c_begin(events): - out('#include "trace.h"', + out('#include "qemu/osdep.h"', + '#include "trace.h"', '#include "trace/control.h"', '#include "trace/simple.h"', '') diff --git a/scripts/tracetool/format/events_c.py b/scripts/tracetool/format/events_c.py index 2717ea3a0b..1cc6a49a71 100644 --- a/scripts/tracetool/format/events_c.py +++ b/scripts/tracetool/format/events_c.py @@ -19,6 +19,7 @@ from tracetool import out def generate(events, backend): out('/* This file is autogenerated by tracetool, do not edit. */', '', + '#include "qemu/osdep.h"', '#include "trace.h"', '#include "trace/generated-events.h"', '#include "trace/control.h"', diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py index 96655a0590..afd6e98537 100644 --- a/scripts/tracetool/format/tcg_helper_c.py +++ b/scripts/tracetool/format/tcg_helper_c.py @@ -23,6 +23,7 @@ def generate(events, backend): out('/* This file is autogenerated by tracetool, do not edit. */', '', + '#include "qemu/osdep.h"', '#include "qemu-common.h"', '#include "trace.h"', '#include "exec/helper-proto.h"', diff --git a/stubs/blockdev-close-all-bdrv-states.c b/stubs/blockdev-close-all-bdrv-states.c index 12d2442362..f1f1d9cdca 100644 --- a/stubs/blockdev-close-all-bdrv-states.c +++ b/stubs/blockdev-close-all-bdrv-states.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "block/block_int.h" void blockdev_close_all_bdrv_states(void) diff --git a/target-arm/cpu-qom.h b/target-arm/cpu-qom.h index 07c0a71942..1cc4502fc4 100644 --- a/target-arm/cpu-qom.h +++ b/target-arm/cpu-qom.h @@ -148,6 +148,8 @@ typedef struct ARMCPU { uint32_t id_pfr0; uint32_t id_pfr1; uint32_t id_dfr0; + uint32_t pmceid0; + uint32_t pmceid1; uint32_t id_afr0; uint32_t id_mmfr0; uint32_t id_mmfr1; diff --git a/target-arm/cpu.c b/target-arm/cpu.c index f2393cd9f2..e95b0307a6 100644 --- a/target-arm/cpu.c +++ b/target-arm/cpu.c @@ -1156,6 +1156,8 @@ static void cortex_a15_initfn(Object *obj) cpu->id_pfr0 = 0x00001131; cpu->id_pfr1 = 0x00011011; cpu->id_dfr0 = 0x02010555; + cpu->pmceid0 = 0x0000000; + cpu->pmceid1 = 0x00000000; cpu->id_afr0 = 0x00000000; cpu->id_mmfr0 = 0x10201105; cpu->id_mmfr1 = 0x20000000; diff --git a/target-arm/cpu.h b/target-arm/cpu.h index 5137632ccc..16238216f4 100644 --- a/target-arm/cpu.h +++ b/target-arm/cpu.h @@ -595,6 +595,18 @@ void pmccntr_sync(CPUARMState *env); #define CPTR_TTA (1U << 20) #define CPTR_TFP (1U << 10) +#define MDCR_EPMAD (1U << 21) +#define MDCR_EDAD (1U << 20) +#define MDCR_SPME (1U << 17) +#define MDCR_SDD (1U << 16) +#define MDCR_TDRA (1U << 11) +#define MDCR_TDOSA (1U << 10) +#define MDCR_TDA (1U << 9) +#define MDCR_TDE (1U << 8) +#define MDCR_HPME (1U << 7) +#define MDCR_TPM (1U << 6) +#define MDCR_TPMCR (1U << 5) + #define CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -1255,6 +1267,18 @@ static inline bool cptype_valid(int cptype) #define PL1_RW (PL1_R | PL1_W) #define PL0_RW (PL0_R | PL0_W) +/* Return the highest implemented Exception Level */ +static inline int arm_highest_el(CPUARMState *env) +{ + if (arm_feature(env, ARM_FEATURE_EL3)) { + return 3; + } + if (arm_feature(env, ARM_FEATURE_EL2)) { + return 2; + } + return 1; +} + /* Return the current Exception Level (as per ARMv8; note that this differs * from the ARMv7 Privilege Level). */ @@ -1310,6 +1334,11 @@ typedef enum CPAccessResult { /* As CP_ACCESS_UNCATEGORIZED, but for traps directly to EL2 or EL3 */ CP_ACCESS_TRAP_UNCATEGORIZED_EL2 = 5, CP_ACCESS_TRAP_UNCATEGORIZED_EL3 = 6, + /* Access fails and results in an exception syndrome for an FP access, + * trapped directly to EL2 or EL3 + */ + CP_ACCESS_TRAP_FP_EL2 = 7, + CP_ACCESS_TRAP_FP_EL3 = 8, } CPAccessResult; /* Access functions for coprocessor registers. These cannot fail and diff --git a/target-arm/cpu64.c b/target-arm/cpu64.c index c5bc19a405..fa5eda2cd1 100644 --- a/target-arm/cpu64.c +++ b/target-arm/cpu64.c @@ -135,6 +135,8 @@ static void aarch64_a57_initfn(Object *obj) cpu->id_isar5 = 0x00011121; cpu->id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; + cpu->pmceid0 = 0x00000000; + cpu->pmceid1 = 0x00000000; cpu->id_aa64isar0 = 0x00011120; cpu->id_aa64mmfr0 = 0x00001124; cpu->dbgdidr = 0x3516d000; diff --git a/target-arm/helper.c b/target-arm/helper.c index 2f9db72806..5a0447b93a 100644 --- a/target-arm/helper.c +++ b/target-arm/helper.c @@ -385,6 +385,60 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_TRAP_UNCATEGORIZED; } +/* Check for traps to "powerdown debug" registers, which are controlled + * by MDCR.TDOSA + */ +static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDOSA) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* Check for traps to "debug ROM" registers, which are controlled + * by MDCR_EL2.TDRA for EL2 but by the more general MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDRA) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +/* Check for traps to general debug registers, which are controlled + * by MDCR_EL2.TDA for EL2 and MDCR_EL3.TDA for EL3. + */ +static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDA) + && !arm_is_secure_below_el3(env)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + static void dacr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -1003,6 +1057,13 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .accessfn = pmreg_access, .writefn = pmovsr_write, .raw_writefn = raw_write }, + { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsr_write, + .raw_writefn = raw_write }, /* Unimplemented so WI. */ { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, .access = PL0_W, .accessfn = pmreg_access, .type = ARM_CP_NOP }, @@ -1044,6 +1105,12 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), .resetvalue = 0, .writefn = pmuserenr_write, .raw_writefn = raw_write }, + { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0, + .access = PL0_R | PL1_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), + .resetvalue = 0, + .writefn = pmuserenr_write, .raw_writefn = raw_write }, { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, .access = PL1_RW, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), @@ -1053,6 +1120,11 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write, }, + { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, + .access = PL1_RW, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .writefn = pmintenclr_write }, { .name = "VBAR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 12, .crm = 0, .opc1 = 0, .opc2 = 0, .access = PL1_RW, .writefn = vbar_write, @@ -1218,10 +1290,33 @@ static const ARMCPRegInfo v6k_cp_reginfo[] = { static CPAccessResult gt_cntfrq_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero */ - if (arm_current_el(env) == 0 && !extract32(env->cp15.c14_cntkctl, 0, 2)) { - return CP_ACCESS_TRAP; + /* CNTFRQ: not visible from PL0 if both PL0PCTEN and PL0VCTEN are zero. + * Writable only at the highest implemented exception level. + */ + int el = arm_current_el(env); + + switch (el) { + case 0: + if (!extract32(env->cp15.c14_cntkctl, 0, 2)) { + return CP_ACCESS_TRAP; + } + break; + case 1: + if (!isread && ri->state == ARM_CP_STATE_AA32 && + arm_is_secure_below_el3(env)) { + /* Accesses from 32-bit Secure EL1 UNDEF (*not* trap to EL3!) */ + return CP_ACCESS_TRAP_UNCATEGORIZED; + } + break; + case 2: + case 3: + break; + } + + if (!isread && el < arm_highest_el(env)) { + return CP_ACCESS_TRAP_UNCATEGORIZED; } + return CP_ACCESS_OK; } @@ -2934,10 +3029,10 @@ static CPAccessResult fpexc32_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { if ((env->cp15.cptr_el[2] & CPTR_TFP) && arm_current_el(env) == 2) { - return CP_ACCESS_TRAP_EL2; + return CP_ACCESS_TRAP_FP_EL2; } if (env->cp15.cptr_el[3] & CPTR_TFP) { - return CP_ACCESS_TRAP_EL3; + return CP_ACCESS_TRAP_FP_EL3; } return CP_ACCESS_OK; } @@ -3325,7 +3420,8 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 1, - .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL2_RW, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "HPFAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 4, .access = PL2_RW, .accessfn = access_el3_aa32ns_aa64any, @@ -3732,16 +3828,19 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { * accessor. */ { .name = "DBGDRAR", .cp = 14, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "MDRAR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 0, - .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL1_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "DBGDSAR", .cp = 14, .crn = 2, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = 0 }, + .access = PL0_R, .accessfn = access_tdra, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* Monitor debug system control register; the 32-bit alias is DBGDSCRext. */ { .name = "MDSCR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), .resetvalue = 0 }, /* MDCCSR_EL0, aka DBGDSCRint. This is a read-only mirror of MDSCR_EL1. @@ -3750,26 +3849,30 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { { .name = "MDCCSR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .type = ARM_CP_ALIAS, - .access = PL1_R, + .access = PL1_R, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.mdscr_el1), }, { .name = "OSLAR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 0, .opc2 = 4, .access = PL1_W, .type = ARM_CP_NO_RAW, + .accessfn = access_tdosa, .writefn = oslar_write }, { .name = "OSLSR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 1, .opc2 = 4, .access = PL1_R, .resetvalue = 10, + .accessfn = access_tdosa, .fieldoffset = offsetof(CPUARMState, cp15.oslsr_el1) }, /* Dummy OSDLR_EL1: 32-bit Linux will read this */ { .name = "OSDLR_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 1, .crm = 3, .opc2 = 4, - .access = PL1_RW, .type = ARM_CP_NOP }, + .access = PL1_RW, .accessfn = access_tdosa, + .type = ARM_CP_NOP }, /* Dummy DBGVCR: Linux wants to clear this on startup, but we don't * implement vector catch debug events yet. */ { .name = "DBGVCR", .cp = 14, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NOP }, + .access = PL1_RW, .accessfn = access_tda, + .type = ARM_CP_NOP }, REGINFO_SENTINEL }; @@ -4034,7 +4137,8 @@ static void define_debug_regs(ARMCPU *cpu) int wrps, brps, ctx_cmps; ARMCPRegInfo dbgdidr = { .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0, - .access = PL0_R, .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr, + .access = PL0_R, .accessfn = access_tda, + .type = ARM_CP_CONST, .resetvalue = cpu->dbgdidr, }; /* Note that all these register fields hold "number of Xs minus 1". */ @@ -4065,13 +4169,13 @@ static void define_debug_regs(ARMCPU *cpu) ARMCPRegInfo dbgregs[] = { { .name = "DBGBVR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 4, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgbvr[i]), .writefn = dbgbvr_write, .raw_writefn = raw_write }, { .name = "DBGBCR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 5, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgbcr[i]), .writefn = dbgbcr_write, .raw_writefn = raw_write }, @@ -4084,13 +4188,13 @@ static void define_debug_regs(ARMCPU *cpu) ARMCPRegInfo dbgregs[] = { { .name = "DBGWVR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 6, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgwvr[i]), .writefn = dbgwvr_write, .raw_writefn = raw_write }, { .name = "DBGWCR", .state = ARM_CP_STATE_BOTH, .cp = 14, .opc0 = 2, .opc1 = 0, .crn = 0, .crm = i, .opc2 = 7, - .access = PL1_RW, + .access = PL1_RW, .accessfn = access_tda, .fieldoffset = offsetof(CPUARMState, cp15.dbgwcr[i]), .writefn = dbgwcr_write, .raw_writefn = raw_write }, @@ -4294,6 +4398,22 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .resetvalue = cpu->mvfr2 }, + { .name = "PMCEID0", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid0 }, + { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid0 }, + { .name = "PMCEID1", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid1 }, + { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = cpu->pmceid1 }, REGINFO_SENTINEL }; /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ @@ -5279,21 +5399,6 @@ void switch_mode(CPUARMState *env, int mode) } } -void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) -{ - ARMCPU *cpu = arm_env_get_cpu(env); - - cpu_abort(CPU(cpu), "banked r13 write\n"); -} - -uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) -{ - ARMCPU *cpu = arm_env_get_cpu(env); - - cpu_abort(CPU(cpu), "banked r13 read\n"); - return 0; -} - uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, uint32_t cur_el, bool secure) { @@ -5307,31 +5412,6 @@ void aarch64_sync_64_to_32(CPUARMState *env) #else -/* Map CPU modes onto saved register banks. */ -int bank_number(int mode) -{ - switch (mode) { - case ARM_CPU_MODE_USR: - case ARM_CPU_MODE_SYS: - return BANK_USRSYS; - case ARM_CPU_MODE_SVC: - return BANK_SVC; - case ARM_CPU_MODE_ABT: - return BANK_ABT; - case ARM_CPU_MODE_UND: - return BANK_UND; - case ARM_CPU_MODE_IRQ: - return BANK_IRQ; - case ARM_CPU_MODE_FIQ: - return BANK_FIQ; - case ARM_CPU_MODE_HYP: - return BANK_HYP; - case ARM_CPU_MODE_MON: - return BANK_MON; - } - g_assert_not_reached(); -} - void switch_mode(CPUARMState *env, int mode) { int old_mode; @@ -7676,24 +7756,6 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, return phys_addr; } -void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) -{ - if ((env->uncached_cpsr & CPSR_M) == mode) { - env->regs[13] = val; - } else { - env->banked_r13[bank_number(mode)] = val; - } -} - -uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) -{ - if ((env->uncached_cpsr & CPSR_M) == mode) { - return env->regs[13]; - } else { - return env->banked_r13[bank_number(mode)]; - } -} - uint32_t HELPER(v7m_mrs)(CPUARMState *env, uint32_t reg) { ARMCPU *cpu = arm_env_get_cpu(env); diff --git a/target-arm/internals.h b/target-arm/internals.h index 70bec4a4d2..2e70272be2 100644 --- a/target-arm/internals.h +++ b/target-arm/internals.h @@ -109,7 +109,31 @@ static inline unsigned int aarch64_banked_spsr_index(unsigned int el) return map[el]; } -int bank_number(int mode); +/* Map CPU modes onto saved register banks. */ +static inline int bank_number(int mode) +{ + switch (mode) { + case ARM_CPU_MODE_USR: + case ARM_CPU_MODE_SYS: + return BANK_USRSYS; + case ARM_CPU_MODE_SVC: + return BANK_SVC; + case ARM_CPU_MODE_ABT: + return BANK_ABT; + case ARM_CPU_MODE_UND: + return BANK_UND; + case ARM_CPU_MODE_IRQ: + return BANK_IRQ; + case ARM_CPU_MODE_FIQ: + return BANK_FIQ; + case ARM_CPU_MODE_HYP: + return BANK_HYP; + case ARM_CPU_MODE_MON: + return BANK_MON; + } + g_assert_not_reached(); +} + void switch_mode(CPUARMState *, int); void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index bd48549826..538887ce0c 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -457,6 +457,32 @@ void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val) } } +void HELPER(set_r13_banked)(CPUARMState *env, uint32_t mode, uint32_t val) +{ + if ((env->uncached_cpsr & CPSR_M) == mode) { + env->regs[13] = val; + } else { + env->banked_r13[bank_number(mode)] = val; + } +} + +uint32_t HELPER(get_r13_banked)(CPUARMState *env, uint32_t mode) +{ + if ((env->uncached_cpsr & CPSR_M) == ARM_CPU_MODE_SYS) { + /* SRS instruction is UNPREDICTABLE from System mode; we UNDEF. + * Other UNPREDICTABLE and UNDEF cases were caught at translate time. + */ + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } + + if ((env->uncached_cpsr & CPSR_M) == mode) { + return env->regs[13]; + } else { + return env->banked_r13[bank_number(mode)]; + } +} + void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, uint32_t isread) { @@ -500,6 +526,19 @@ void HELPER(access_check_cp_reg)(CPUARMState *env, void *rip, uint32_t syndrome, target_el = 3; syndrome = syn_uncategorized(); break; + case CP_ACCESS_TRAP_FP_EL2: + target_el = 2; + /* Since we are an implementation that takes exceptions on a trapped + * conditional insn only if the insn has passed its condition code + * check, we take the IMPDEF choice to always report CV=1 COND=0xe + * (which is also the required value for AArch64 traps). + */ + syndrome = syn_fp_access_trap(1, 0xe, false); + break; + case CP_ACCESS_TRAP_FP_EL3: + target_el = 3; + syndrome = syn_fp_access_trap(1, 0xe, false); + break; default: g_assert_not_reached(); } @@ -614,12 +653,14 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); bool smd = env->cp15.scr_el3 & SCR_SMD; - /* On ARMv8 AArch32, SMD only applies to NS state. - * On ARMv7 SMD only applies to NS state and only if EL2 is available. - * For ARMv7 non EL2, we force SMD to zero so we don't need to re-check - * the EL2 condition here. + /* On ARMv8 with EL3 AArch64, SMD applies to both S and NS state. + * On ARMv8 with EL3 AArch32, or ARMv7 with the Virtualization + * extensions, SMD only applies to NS state. + * On ARMv7 without the Virtualization extensions, the SMD bit + * doesn't exist, but we forbid the guest to set it to 1 in scr_write(), + * so we need not special case this here. */ - bool undef = is_a64(env) ? smd : (!secure && smd); + bool undef = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure; if (arm_is_psci_call(cpu, EXCP_SMC)) { /* If PSCI is enabled and this looks like a valid PSCI call then diff --git a/target-arm/translate.c b/target-arm/translate.c index cf3dc33774..e69145d401 100644 --- a/target-arm/translate.c +++ b/target-arm/translate.c @@ -7578,8 +7578,67 @@ static void gen_srs(DisasContext *s, uint32_t mode, uint32_t amode, bool writeback) { int32_t offset; - TCGv_i32 addr = tcg_temp_new_i32(); - TCGv_i32 tmp = tcg_const_i32(mode); + TCGv_i32 addr, tmp; + bool undef = false; + + /* SRS is: + * - trapped to EL3 if EL3 is AArch64 and we are at Secure EL1 + * - UNDEFINED in Hyp mode + * - UNPREDICTABLE in User or System mode + * - UNPREDICTABLE if the specified mode is: + * -- not implemented + * -- not a valid mode number + * -- a mode that's at a higher exception level + * -- Monitor, if we are Non-secure + * For the UNPREDICTABLE cases we choose to UNDEF. + */ + if (s->current_el == 1 && !s->ns) { + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), 3); + return; + } + + if (s->current_el == 0 || s->current_el == 2) { + undef = true; + } + + switch (mode) { + case ARM_CPU_MODE_USR: + case ARM_CPU_MODE_FIQ: + case ARM_CPU_MODE_IRQ: + case ARM_CPU_MODE_SVC: + case ARM_CPU_MODE_ABT: + case ARM_CPU_MODE_UND: + case ARM_CPU_MODE_SYS: + break; + case ARM_CPU_MODE_HYP: + if (s->current_el == 1 || !arm_dc_feature(s, ARM_FEATURE_EL2)) { + undef = true; + } + break; + case ARM_CPU_MODE_MON: + /* No need to check specifically for "are we non-secure" because + * we've already made EL0 UNDEF and handled the trap for S-EL1; + * so if this isn't EL3 then we must be non-secure. + */ + if (s->current_el != 3) { + undef = true; + } + break; + default: + undef = true; + } + + if (undef) { + gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), + default_exception_el(s)); + return; + } + + addr = tcg_temp_new_i32(); + tmp = tcg_const_i32(mode); + /* get_r13_banked() will raise an exception if called from System mode */ + gen_set_condexec(s); + gen_set_pc_im(s, s->pc - 4); gen_helper_get_r13_banked(addr, cpu_env, tmp); tcg_temp_free_i32(tmp); switch (amode) { @@ -7629,6 +7688,7 @@ static void gen_srs(DisasContext *s, tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); + s->is_jmp = DISAS_UPDATE; } static void disas_arm_insn(DisasContext *s, unsigned int insn) @@ -7739,9 +7799,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } } else if ((insn & 0x0e5fffe0) == 0x084d0500) { /* srs */ - if (IS_USER(s)) { - goto illegal_op; - } ARCH(6); gen_srs(s, (insn & 0x1f), (insn >> 23) & 3, insn & (1 << 21)); return; diff --git a/target-cris/mmu.c b/target-cris/mmu.c index 1c95a415f2..4278d2dce4 100644 --- a/target-cris/mmu.c +++ b/target-cris/mmu.c @@ -18,8 +18,7 @@ * License along with this library; if not, see <http://www.gnu.org/licenses/>. */ -#ifndef CONFIG_USER_ONLY - +#include "qemu/osdep.h" #include "cpu.h" #include "mmu.h" @@ -360,4 +359,3 @@ int cris_mmu_translate(struct cris_mmu_result *res, env->pregs[PR_SRS] = old_srs; return miss; } -#endif diff --git a/target-i386/Makefile.objs b/target-i386/Makefile.objs index 2255f46a9e..b223d7932b 100644 --- a/target-i386/Makefile.objs +++ b/target-i386/Makefile.objs @@ -1,6 +1,6 @@ obj-y += translate.o helper.o cpu.o bpt_helper.o obj-y += excp_helper.o fpu_helper.o cc_helper.o int_helper.o svm_helper.o -obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o +obj-y += smm_helper.o misc_helper.o mem_helper.o seg_helper.o mpx_helper.o obj-y += gdbstub.o obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o obj-$(CONFIG_KVM) += kvm.o hyperv.o diff --git a/target-i386/cc_helper.c b/target-i386/cc_helper.c index 99a3b5496b..83af223c9f 100644 --- a/target-i386/cc_helper.c +++ b/target-i386/cc_helper.c @@ -383,13 +383,3 @@ void helper_sti_vm(CPUX86State *env) } } #endif - -void helper_set_inhibit_irq(CPUX86State *env) -{ - env->hflags |= HF_INHIBIT_IRQ_MASK; -} - -void helper_reset_inhibit_irq(CPUX86State *env) -{ - env->hflags &= ~HF_INHIBIT_IRQ_MASK; -} diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 3fa14bf171..0af43a3ae1 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -331,14 +331,14 @@ static const char *cpuid_6_feature_name[] = { #define TCG_EXT_FEATURES (CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | \ CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | \ CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_POPCNT | \ + CPUID_EXT_XSAVE | /* CPUID_EXT_OSXSAVE is dynamic */ \ CPUID_EXT_MOVBE | CPUID_EXT_AES | CPUID_EXT_HYPERVISOR) /* missing: CPUID_EXT_DTES64, CPUID_EXT_DSCPL, CPUID_EXT_VMX, CPUID_EXT_SMX, CPUID_EXT_EST, CPUID_EXT_TM2, CPUID_EXT_CID, CPUID_EXT_FMA, CPUID_EXT_XTPR, CPUID_EXT_PDCM, CPUID_EXT_PCID, CPUID_EXT_DCA, - CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_XSAVE, - CPUID_EXT_OSXSAVE, CPUID_EXT_AVX, CPUID_EXT_F16C, - CPUID_EXT_RDRAND */ + CPUID_EXT_X2APIC, CPUID_EXT_TSC_DEADLINE_TIMER, CPUID_EXT_AVX, + CPUID_EXT_F16C, CPUID_EXT_RDRAND */ #ifdef TARGET_X86_64 #define TCG_EXT2_X86_64_FEATURES (CPUID_EXT2_SYSCALL | CPUID_EXT2_LM) @@ -358,15 +358,17 @@ static const char *cpuid_6_feature_name[] = { #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ - CPUID_7_0_EBX_CLWB) + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE) /* missing: - CPUID_7_0_EBX_FSGSBASE, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, + CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, CPUID_7_0_EBX_RDSEED */ #define TCG_7_0_ECX_FEATURES 0 #define TCG_APM_FEATURES 0 #define TCG_6_EAX_FEATURES CPUID_6_EAX_ARAT - +#define TCG_XSAVE_FEATURES (CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1) + /* missing: + CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ typedef struct FeatureWordInfo { const char **feat_names; @@ -440,7 +442,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .cpuid_eax = 0xd, .cpuid_needs_ecx = true, .cpuid_ecx = 1, .cpuid_reg = R_EAX, - .tcg_features = 0, + .tcg_features = TCG_XSAVE_FEATURES, }, [FEAT_6_EAX] = { .feat_names = cpuid_6_feature_name, @@ -470,12 +472,7 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = { }; #undef REGISTER -typedef struct ExtSaveArea { - uint32_t feature, bits; - uint32_t offset, size; -} ExtSaveArea; - -static const ExtSaveArea ext_save_areas[] = { +const ExtSaveArea x86_ext_save_areas[] = { [2] = { .feature = FEAT_1_ECX, .bits = CPUID_EXT_AVX, .offset = 0x240, .size = 0x100 }, [3] = { .feature = FEAT_7_0_EBX, .bits = CPUID_7_0_EBX_MPX, @@ -2323,10 +2320,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = (cpu->apic_id << 24) | 8 << 8; /* CLFLUSH size in quad words, Linux wants it. */ *ecx = env->features[FEAT_1_ECX]; + if ((*ecx & CPUID_EXT_XSAVE) && (env->cr[4] & CR4_OSXSAVE_MASK)) { + *ecx |= CPUID_EXT_OSXSAVE; + } *edx = env->features[FEAT_1_EDX]; if (cs->nr_cores * cs->nr_threads > 1) { *ebx |= (cs->nr_cores * cs->nr_threads) << 16; - *edx |= 1 << 28; /* HTT bit */ + *edx |= CPUID_HT; } break; case 2: @@ -2450,7 +2450,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0xD: { KVMState *s = cs->kvm_state; - uint64_t kvm_mask; + uint64_t ena_mask; int i; /* Processor Extended State */ @@ -2458,35 +2458,39 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx = 0; *ecx = 0; *edx = 0; - if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) || !kvm_enabled()) { + if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { break; } - kvm_mask = - kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX) | - ((uint64_t)kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX) << 32); + if (kvm_enabled()) { + ena_mask = kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EDX); + ena_mask <<= 32; + ena_mask |= kvm_arch_get_supported_cpuid(s, 0xd, 0, R_EAX); + } else { + ena_mask = -1; + } if (count == 0) { *ecx = 0x240; - for (i = 2; i < ARRAY_SIZE(ext_save_areas); i++) { - const ExtSaveArea *esa = &ext_save_areas[i]; - if ((env->features[esa->feature] & esa->bits) == esa->bits && - (kvm_mask & (1 << i)) != 0) { + for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) { + const ExtSaveArea *esa = &x86_ext_save_areas[i]; + if ((env->features[esa->feature] & esa->bits) == esa->bits + && ((ena_mask >> i) & 1) != 0) { if (i < 32) { - *eax |= 1 << i; + *eax |= 1u << i; } else { - *edx |= 1 << (i - 32); + *edx |= 1u << (i - 32); } *ecx = MAX(*ecx, esa->offset + esa->size); } } - *eax |= kvm_mask & (XSTATE_FP | XSTATE_SSE); + *eax |= ena_mask & (XSTATE_FP | XSTATE_SSE); *ebx = *ecx; } else if (count == 1) { *eax = env->features[FEAT_XSAVE]; - } else if (count < ARRAY_SIZE(ext_save_areas)) { - const ExtSaveArea *esa = &ext_save_areas[count]; - if ((env->features[esa->feature] & esa->bits) == esa->bits && - (kvm_mask & (1 << count)) != 0) { + } else if (count < ARRAY_SIZE(x86_ext_save_areas)) { + const ExtSaveArea *esa = &x86_ext_save_areas[count]; + if ((env->features[esa->feature] & esa->bits) == esa->bits + && ((ena_mask >> count) & 1) != 0) { *eax = esa->size; *ebx = esa->offset; } @@ -2639,6 +2643,8 @@ static void x86_cpu_reset(CPUState *s) X86CPU *cpu = X86_CPU(s); X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; + target_ulong cr4; + uint64_t xcr0; int i; xcc->parent_reset(s); @@ -2698,7 +2704,8 @@ static void x86_cpu_reset(CPUState *s) cpu_set_fpuc(env, 0x37f); env->mxcsr = 0x1f80; - env->xstate_bv = XSTATE_FP | XSTATE_SSE; + /* All units are in INIT state. */ + env->xstate_bv = 0; env->pat = 0x0007040600070406ULL; env->msr_ia32_misc_enable = MSR_IA32_MISC_ENABLE_DEFAULT; @@ -2709,7 +2716,27 @@ static void x86_cpu_reset(CPUState *s) cpu_breakpoint_remove_all(s, BP_CPU); cpu_watchpoint_remove_all(s, BP_CPU); - env->xcr0 = 1; + cr4 = 0; + xcr0 = XSTATE_FP; + +#ifdef CONFIG_USER_ONLY + /* Enable all the features for user-mode. */ + if (env->features[FEAT_1_EDX] & CPUID_SSE) { + xcr0 |= XSTATE_SSE; + } + if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_MPX) { + xcr0 |= XSTATE_BNDREGS | XSTATE_BNDCSR; + } + if (env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE) { + cr4 |= CR4_OSFXSR_MASK | CR4_OSXSAVE_MASK; + } + if (env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_FSGSBASE) { + cr4 |= CR4_FSGSBASE_MASK; + } +#endif + + env->xcr0 = xcr0; + cpu_x86_update_cr4(env, cr4); /* * SDM 11.11.5 requires: diff --git a/target-i386/cpu.h b/target-i386/cpu.h index a990ea7fef..94cb4db27d 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -156,6 +156,8 @@ #define HF_OSFXSR_SHIFT 22 /* CR4.OSFXSR */ #define HF_SMAP_SHIFT 23 /* CR4.SMAP */ #define HF_IOBPT_SHIFT 24 /* an io breakpoint enabled */ +#define HF_MPX_EN_SHIFT 25 /* MPX Enabled (CR4+XCR0+BNDCFGx) */ +#define HF_MPX_IU_SHIFT 26 /* BND registers in-use */ #define HF_CPL_MASK (3 << HF_CPL_SHIFT) #define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT) @@ -180,6 +182,8 @@ #define HF_OSFXSR_MASK (1 << HF_OSFXSR_SHIFT) #define HF_SMAP_MASK (1 << HF_SMAP_SHIFT) #define HF_IOBPT_MASK (1 << HF_IOBPT_SHIFT) +#define HF_MPX_EN_MASK (1 << HF_MPX_EN_SHIFT) +#define HF_MPX_IU_MASK (1 << HF_MPX_IU_SHIFT) /* hflags2 */ @@ -188,12 +192,14 @@ #define HF2_NMI_SHIFT 2 /* CPU serving NMI */ #define HF2_VINTR_SHIFT 3 /* value of V_INTR_MASKING bit */ #define HF2_SMM_INSIDE_NMI_SHIFT 4 /* CPU serving SMI nested inside NMI */ +#define HF2_MPX_PR_SHIFT 5 /* BNDCFGx.BNDPRESERVE */ #define HF2_GIF_MASK (1 << HF2_GIF_SHIFT) #define HF2_HIF_MASK (1 << HF2_HIF_SHIFT) #define HF2_NMI_MASK (1 << HF2_NMI_SHIFT) #define HF2_VINTR_MASK (1 << HF2_VINTR_SHIFT) #define HF2_SMM_INSIDE_NMI_MASK (1 << HF2_SMM_INSIDE_NMI_SHIFT) +#define HF2_MPX_PR_MASK (1 << HF2_MPX_PR_SHIFT) #define CR0_PE_SHIFT 0 #define CR0_MP_SHIFT 1 @@ -753,6 +759,10 @@ typedef struct BNDCSReg { uint64_t sts; } BNDCSReg; +#define BNDCFG_ENABLE 1ULL +#define BNDCFG_BNDPRESERVE 2ULL +#define BNDCFG_BDIR_MASK TARGET_PAGE_MASK + #ifdef HOST_WORDS_BIGENDIAN #define ZMM_B(n) _b_ZMMReg[63 - (n)] #define ZMM_W(n) _w_ZMMReg[31 - (n)] @@ -1121,7 +1131,14 @@ void cpu_x86_frstor(CPUX86State *s, target_ulong ptr, int data32); int cpu_x86_signal_handler(int host_signum, void *pinfo, void *puc); -/* cpuid.c */ +/* cpu.c */ +typedef struct ExtSaveArea { + uint32_t feature, bits; + uint32_t offset, size; +} ExtSaveArea; + +extern const ExtSaveArea x86_ext_save_areas[]; + void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); @@ -1342,6 +1359,8 @@ void cpu_report_tpr_access(CPUX86State *env, TPRAccess access); */ void x86_cpu_change_kvm_default(const char *prop, const char *value); +/* mpx_helper.c */ +void cpu_sync_bndcs_hflags(CPUX86State *env); /* Return name of 32-bit register, from a R_* constant */ const char *get_register_name_32(unsigned int reg); diff --git a/target-i386/fpu_helper.c b/target-i386/fpu_helper.c index 2d54b47ac6..9dfbc4c7a6 100644 --- a/target-i386/fpu_helper.c +++ b/target-i386/fpu_helper.c @@ -1115,89 +1115,174 @@ void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) } #endif -static void do_fxsave(CPUX86State *env, target_ulong ptr, int data64, - uintptr_t retaddr) +static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) { - int fpus, fptag, i, nb_xmm_regs; - floatx80 tmp; + int fpus, fptag, i; target_ulong addr; - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception_ra(env, EXCP0D_GPF, retaddr); - } - fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; fptag = 0; for (i = 0; i < 8; i++) { fptag |= (env->fptags[i] << i); } - cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); - cpu_stw_data_ra(env, ptr + 4, fptag ^ 0xff, retaddr); -#ifdef TARGET_X86_64 - if (data64) { - cpu_stq_data_ra(env, ptr + 0x08, 0, retaddr); /* rip */ - cpu_stq_data_ra(env, ptr + 0x10, 0, retaddr); /* rdp */ - } else -#endif - { - cpu_stl_data_ra(env, ptr + 0x08, 0, retaddr); /* eip */ - cpu_stl_data_ra(env, ptr + 0x0c, 0, retaddr); /* sel */ - cpu_stl_data_ra(env, ptr + 0x10, 0, retaddr); /* dp */ - cpu_stl_data_ra(env, ptr + 0x14, 0, retaddr); /* sel */ - } + cpu_stw_data_ra(env, ptr, env->fpuc, ra); + cpu_stw_data_ra(env, ptr + 2, fpus, ra); + cpu_stw_data_ra(env, ptr + 4, fptag ^ 0xff, ra); + + /* In 32-bit mode this is eip, sel, dp, sel. + In 64-bit mode this is rip, rdp. + But in either case we don't write actual data, just zeros. */ + cpu_stq_data_ra(env, ptr + 0x08, 0, ra); /* eip+sel; rip */ + cpu_stq_data_ra(env, ptr + 0x10, 0, ra); /* edp+sel; rdp */ addr = ptr + 0x20; for (i = 0; i < 8; i++) { - tmp = ST(i); - helper_fstt(env, tmp, addr, retaddr); + floatx80 tmp = ST(i); + helper_fstt(env, tmp, addr, ra); + addr += 16; + } +} + +static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_stl_data_ra(env, ptr + 0x18, env->mxcsr, ra); /* mxcsr */ + cpu_stl_data_ra(env, ptr + 0x1c, 0x0000ffff, ra); /* mxcsr_mask */ +} + +static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + target_ulong addr; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + addr = ptr + 0xa0; + for (i = 0; i < nb_xmm_regs; i++) { + cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), ra); + cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), ra); addr += 16; } +} + +static void do_xsave_bndregs(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + int i; + + for (i = 0; i < 4; i++, addr += 16) { + cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra); + cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra); + } +} + +static void do_xsave_bndcsr(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + cpu_stq_data_ra(env, addr, env->bndcs_regs.cfgu, ra); + cpu_stq_data_ra(env, addr + 8, env->bndcs_regs.sts, ra); +} + +void helper_fxsave(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + do_xsave_fpu(env, ptr, ra); if (env->cr[4] & CR4_OSFXSR_MASK) { - /* XXX: finish it */ - cpu_stl_data_ra(env, ptr + 0x18, env->mxcsr, retaddr); /* mxcsr */ - cpu_stl_data_ra(env, ptr + 0x1c, 0x0000ffff, retaddr); /* mxcsr_mask */ - if (env->hflags & HF_CS64_MASK) { - nb_xmm_regs = 16; - } else { - nb_xmm_regs = 8; - } - addr = ptr + 0xa0; + do_xsave_mxcsr(env, ptr, ra); /* Fast FXSAVE leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - for (i = 0; i < nb_xmm_regs; i++) { - cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), retaddr); - cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), retaddr); - addr += 16; - } + do_xsave_sse(env, ptr, ra); } } } -void helper_fxsave(CPUX86State *env, target_ulong ptr, int data64) +static uint64_t get_xinuse(CPUX86State *env) { - do_fxsave(env, ptr, data64, GETPC()); + uint64_t inuse = -1; + + /* For the most part, we don't track XINUSE. We could calculate it + here for all components, but it's probably less work to simply + indicate in use. That said, the state of BNDREGS is important + enough to track in HFLAGS, so we might as well use that here. */ + if ((env->hflags & HF_MPX_IU_MASK) == 0) { + inuse &= ~XSTATE_BNDREGS; + } + return inuse; } -static void do_fxrstor(CPUX86State *env, target_ulong ptr, int data64, - uintptr_t retaddr) +static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt, uintptr_t ra) { - int i, fpus, fptag, nb_xmm_regs; - floatx80 tmp; - target_ulong addr; + uint64_t old_bv, new_bv; - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception_ra(env, EXCP0D_GPF, retaddr); + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* Never save anything not enabled by XCR0. */ + rfbm &= env->xcr0; + opt &= rfbm; + + if (opt & XSTATE_FP) { + do_xsave_fpu(env, ptr, ra); + } + if (rfbm & XSTATE_SSE) { + /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ + do_xsave_mxcsr(env, ptr, ra); + } + if (opt & XSTATE_SSE) { + do_xsave_sse(env, ptr, ra); + } + if (opt & XSTATE_BNDREGS) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset; + do_xsave_bndregs(env, ptr + off, ra); } + if (opt & XSTATE_BNDCSR) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset; + do_xsave_bndcsr(env, ptr + off, ra); + } + + /* Update the XSTATE_BV field. */ + old_bv = cpu_ldq_data_ra(env, ptr + 512, ra); + new_bv = (old_bv & ~rfbm) | (inuse & rfbm); + cpu_stq_data_ra(env, ptr + 512, new_bv, ra); +} + +void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); +} - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); +void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + uint64_t inuse = get_xinuse(env); + do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); +} + +static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, fpus, fptag; + target_ulong addr; + + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, ra)); + fpus = cpu_lduw_data_ra(env, ptr + 2, ra); + fptag = cpu_lduw_data_ra(env, ptr + 4, ra); env->fpstt = (fpus >> 11) & 7; env->fpus = fpus & ~0x3800; fptag ^= 0xff; @@ -1207,37 +1292,206 @@ static void do_fxrstor(CPUX86State *env, target_ulong ptr, int data64, addr = ptr + 0x20; for (i = 0; i < 8; i++) { - tmp = helper_fldt(env, addr, retaddr); + floatx80 tmp = helper_fldt(env, addr, ra); ST(i) = tmp; addr += 16; } +} + +static void do_xrstor_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + 0x18, ra)); +} + +static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + target_ulong addr; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + addr = ptr + 0xa0; + for (i = 0; i < nb_xmm_regs; i++) { + env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, ra); + env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, ra); + addr += 16; + } +} + +static void do_xrstor_bndregs(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + int i; + + for (i = 0; i < 4; i++, addr += 16) { + env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra); + env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra); + } +} + +static void do_xrstor_bndcsr(CPUX86State *env, target_ulong addr, uintptr_t ra) +{ + /* FIXME: Extend highest implemented bit of linear address. */ + env->bndcs_regs.cfgu = cpu_ldq_data_ra(env, addr, ra); + env->bndcs_regs.sts = cpu_ldq_data_ra(env, addr + 8, ra); +} + +void helper_fxrstor(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + do_xrstor_fpu(env, ptr, ra); if (env->cr[4] & CR4_OSFXSR_MASK) { - /* XXX: finish it */ - cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + 0x18, retaddr)); - /* cpu_ldl_data_ra(env, ptr + 0x1c, retaddr); */ - if (env->hflags & HF_CS64_MASK) { - nb_xmm_regs = 16; - } else { - nb_xmm_regs = 8; - } - addr = ptr + 0xa0; - /* Fast FXRESTORE leaves out the XMM registers */ + do_xrstor_mxcsr(env, ptr, ra); + /* Fast FXRSTOR leaves out the XMM registers */ if (!(env->efer & MSR_EFER_FFXSR) || (env->hflags & HF_CPL_MASK) || !(env->hflags & HF_LMA_MASK)) { - for (i = 0; i < nb_xmm_regs; i++) { - env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, retaddr); - env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, retaddr); - addr += 16; - } + do_xrstor_sse(env, ptr, ra); } } } -void helper_fxrstor(CPUX86State *env, target_ulong ptr, int data64) +void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) { - do_fxrstor(env, ptr, data64, GETPC()); + uintptr_t ra = GETPC(); + uint64_t xstate_bv, xcomp_bv0, xcomp_bv1; + + rfbm &= env->xcr0; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + xstate_bv = cpu_ldq_data_ra(env, ptr + 512, ra); + + if ((int64_t)xstate_bv < 0) { + /* FIXME: Compact form. */ + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* Standard form. */ + + /* The XSTATE field must not set bits not present in XCR0. */ + if (xstate_bv & ~env->xcr0) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* The XCOMP field must be zero. */ + xcomp_bv0 = cpu_ldq_data_ra(env, ptr + 520, ra); + xcomp_bv1 = cpu_ldq_data_ra(env, ptr + 528, ra); + if (xcomp_bv0 || xcomp_bv1) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + if (rfbm & XSTATE_FP) { + if (xstate_bv & XSTATE_FP) { + do_xrstor_fpu(env, ptr, ra); + } else { + helper_fninit(env); + memset(env->fpregs, 0, sizeof(env->fpregs)); + } + } + if (rfbm & XSTATE_SSE) { + /* Note that the standard form of XRSTOR loads MXCSR from memory + whether or not the XSTATE_BV bit is set. */ + do_xrstor_mxcsr(env, ptr, ra); + if (xstate_bv & XSTATE_SSE) { + do_xrstor_sse(env, ptr, ra); + } else { + /* ??? When AVX is implemented, we may have to be more + selective in the clearing. */ + memset(env->xmm_regs, 0, sizeof(env->xmm_regs)); + } + } + if (rfbm & XSTATE_BNDREGS) { + if (xstate_bv & XSTATE_BNDREGS) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDREGS].offset; + do_xrstor_bndregs(env, ptr + off, ra); + env->hflags |= HF_MPX_IU_MASK; + } else { + memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); + env->hflags &= ~HF_MPX_IU_MASK; + } + } + if (rfbm & XSTATE_BNDCSR) { + if (xstate_bv & XSTATE_BNDCSR) { + target_ulong off = x86_ext_save_areas[XSTATE_BNDCSR].offset; + do_xrstor_bndcsr(env, ptr + off, ra); + } else { + memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); + } + cpu_sync_bndcs_hflags(env); + } +} + +uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) +{ + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } + + switch (ecx) { + case 0: + return env->xcr0; + case 1: + if (env->features[FEAT_XSAVE] & CPUID_XSAVE_XGETBV1) { + return env->xcr0 & get_xinuse(env); + } + break; + } + raise_exception_ra(env, EXCP0D_GPF, GETPC()); +} + +void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) +{ + uint32_t dummy, ena_lo, ena_hi; + uint64_t ena; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } + + /* Only XCR0 is defined at present; the FPU may not be disabled. */ + if (ecx != 0 || (mask & XSTATE_FP) == 0) { + goto do_gpf; + } + + /* Disallow enabling unimplemented features. */ + cpu_x86_cpuid(env, 0x0d, 0, &ena_lo, &dummy, &dummy, &ena_hi); + ena = ((uint64_t)ena_hi << 32) | ena_lo; + if (mask & ~ena) { + goto do_gpf; + } + + /* Disallow enabling only half of MPX. */ + if ((mask ^ (mask * (XSTATE_BNDCSR / XSTATE_BNDREGS))) & XSTATE_BNDCSR) { + goto do_gpf; + } + + env->xcr0 = mask; + cpu_sync_bndcs_hflags(env); + return; + + do_gpf: + raise_exception_ra(env, EXCP0D_GPF, GETPC()); } void cpu_get_fp80(uint64_t *pmant, uint16_t *pexp, floatx80 f) diff --git a/target-i386/helper.c b/target-i386/helper.c index 3802ed9359..3f60ec6122 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -647,6 +647,7 @@ void cpu_x86_update_cr3(CPUX86State *env, target_ulong new_cr3) void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) { X86CPU *cpu = x86_env_get_cpu(env); + uint32_t hflags; #if defined(DEBUG_MMU) printf("CR4 update: CR4=%08x\n", (uint32_t)env->cr[4]); @@ -656,24 +657,29 @@ void cpu_x86_update_cr4(CPUX86State *env, uint32_t new_cr4) CR4_SMEP_MASK | CR4_SMAP_MASK)) { tlb_flush(CPU(cpu), 1); } + + /* Clear bits we're going to recompute. */ + hflags = env->hflags & ~(HF_OSFXSR_MASK | HF_SMAP_MASK); + /* SSE handling */ if (!(env->features[FEAT_1_EDX] & CPUID_SSE)) { new_cr4 &= ~CR4_OSFXSR_MASK; } - env->hflags &= ~HF_OSFXSR_MASK; if (new_cr4 & CR4_OSFXSR_MASK) { - env->hflags |= HF_OSFXSR_MASK; + hflags |= HF_OSFXSR_MASK; } if (!(env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_SMAP)) { new_cr4 &= ~CR4_SMAP_MASK; } - env->hflags &= ~HF_SMAP_MASK; if (new_cr4 & CR4_SMAP_MASK) { - env->hflags |= HF_SMAP_MASK; + hflags |= HF_SMAP_MASK; } env->cr[4] = new_cr4; + env->hflags = hflags; + + cpu_sync_bndcs_hflags(env); } #if defined(CONFIG_USER_ONLY) diff --git a/target-i386/helper.h b/target-i386/helper.h index 3a25c3b392..e33451aea9 100644 --- a/target-i386/helper.h +++ b/target-i386/helper.h @@ -15,6 +15,14 @@ DEF_HELPER_2(idivl_EAX, void, env, tl) DEF_HELPER_2(divq_EAX, void, env, tl) DEF_HELPER_2(idivq_EAX, void, env, tl) #endif +DEF_HELPER_FLAGS_2(cr4_testbit, TCG_CALL_NO_WG, void, env, i32) + +DEF_HELPER_FLAGS_2(bndck, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_3(bndldx32, TCG_CALL_NO_WG, i64, env, tl, tl) +DEF_HELPER_FLAGS_3(bndldx64, TCG_CALL_NO_WG, i64, env, tl, tl) +DEF_HELPER_FLAGS_5(bndstx32, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) +DEF_HELPER_FLAGS_5(bndstx64, TCG_CALL_NO_WG, void, env, tl, tl, i64, i64) +DEF_HELPER_1(bnd_jmp, void, env) DEF_HELPER_2(aam, void, env, int) DEF_HELPER_2(aad, void, env, int) @@ -62,8 +70,6 @@ DEF_HELPER_1(cli, void, env) DEF_HELPER_1(sti, void, env) DEF_HELPER_1(clac, void, env) DEF_HELPER_1(stac, void, env) -DEF_HELPER_1(set_inhibit_irq, void, env) -DEF_HELPER_1(reset_inhibit_irq, void, env) DEF_HELPER_3(boundw, void, env, tl, int) DEF_HELPER_3(boundl, void, env, tl, int) DEF_HELPER_1(rsm, void, env) @@ -185,8 +191,13 @@ DEF_HELPER_3(fstenv, void, env, tl, int) DEF_HELPER_3(fldenv, void, env, tl, int) DEF_HELPER_3(fsave, void, env, tl, int) DEF_HELPER_3(frstor, void, env, tl, int) -DEF_HELPER_3(fxsave, void, env, tl, int) -DEF_HELPER_3(fxrstor, void, env, tl, int) +DEF_HELPER_FLAGS_2(fxsave, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(fxrstor, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_3(xsave, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_3(xsaveopt, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_3(xrstor, TCG_CALL_NO_WG, void, env, tl, i64) +DEF_HELPER_FLAGS_2(xgetbv, TCG_CALL_NO_WG, i64, env, i32) +DEF_HELPER_FLAGS_3(xsetbv, TCG_CALL_NO_WG, void, env, i32, i64) DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(ctz, TCG_CALL_NO_RWG_SE, tl, tl) diff --git a/target-i386/int_helper.c b/target-i386/int_helper.c index 9d0d21e786..cf5bbb0481 100644 --- a/target-i386/int_helper.c +++ b/target-i386/int_helper.c @@ -470,3 +470,13 @@ target_ulong helper_pext(target_ulong src, target_ulong mask) #include "shift_helper_template.h" #undef SHIFT #endif + +/* Test that BIT is enabled in CR4. If not, raise an illegal opcode + exception. This reduces the requirements for rare CR4 bits being + mapped into HFLAGS. */ +void helper_cr4_testbit(CPUX86State *env, uint32_t bit) +{ + if (unlikely((env->cr[4] & bit) == 0)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } +} diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 94024bc1b1..7974acb399 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -1855,13 +1855,16 @@ static int kvm_get_sregs(X86CPU *cpu) HF_OSFXSR_MASK | HF_LMA_MASK | HF_CS32_MASK | \ HF_SS32_MASK | HF_CS64_MASK | HF_ADDSEG_MASK) - hflags = (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; + hflags = env->hflags & HFLAG_COPY_MASK; + hflags |= (env->segs[R_SS].flags >> DESC_DPL_SHIFT) & HF_CPL_MASK; hflags |= (env->cr[0] & CR0_PE_MASK) << (HF_PE_SHIFT - CR0_PE_SHIFT); hflags |= (env->cr[0] << (HF_MP_SHIFT - CR0_MP_SHIFT)) & (HF_MP_MASK | HF_EM_MASK | HF_TS_MASK); hflags |= (env->eflags & (HF_TF_MASK | HF_VM_MASK | HF_IOPL_MASK)); - hflags |= (env->cr[4] & CR4_OSFXSR_MASK) << - (HF_OSFXSR_SHIFT - CR4_OSFXSR_SHIFT); + + if (env->cr[4] & CR4_OSFXSR_MASK) { + hflags |= HF_OSFXSR_MASK; + } if (env->efer & MSR_EFER_LMA) { hflags |= HF_LMA_MASK; @@ -1882,7 +1885,7 @@ static int kvm_get_sregs(X86CPU *cpu) env->segs[R_SS].base) != 0) << HF_ADDSEG_SHIFT; } } - env->hflags = (env->hflags & HFLAG_COPY_MASK) | hflags; + env->hflags = hflags; return 0; } @@ -2585,41 +2588,44 @@ int kvm_arch_get_registers(CPUState *cs) ret = kvm_getput_regs(cpu, 0); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_xsave(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_xcrs(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_sregs(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_msrs(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_mp_state(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_apic(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_vcpu_events(cpu); if (ret < 0) { - return ret; + goto out; } ret = kvm_get_debugregs(cpu); if (ret < 0) { - return ret; + goto out; } - return 0; + ret = 0; + out: + cpu_sync_bndcs_hflags(&cpu->env); + return ret; } void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) diff --git a/target-i386/mem_helper.c b/target-i386/mem_helper.c index 7de775259d..85e75161bc 100644 --- a/target-i386/mem_helper.c +++ b/target-i386/mem_helper.c @@ -112,6 +112,9 @@ void helper_boundw(CPUX86State *env, target_ulong a0, int v) high = cpu_ldsw_data_ra(env, a0 + 2, GETPC()); v = (int16_t)v; if (v < low || v > high) { + if (env->hflags & HF_MPX_EN_MASK) { + env->bndcs_regs.sts = 0; + } raise_exception_ra(env, EXCP05_BOUND, GETPC()); } } @@ -123,6 +126,9 @@ void helper_boundl(CPUX86State *env, target_ulong a0, int v) low = cpu_ldl_data_ra(env, a0, GETPC()); high = cpu_ldl_data_ra(env, a0 + 4, GETPC()); if (v < low || v > high) { + if (env->hflags & HF_MPX_EN_MASK) { + env->bndcs_regs.sts = 0; + } raise_exception_ra(env, EXCP05_BOUND, GETPC()); } } diff --git a/target-i386/misc_helper.c b/target-i386/misc_helper.c index 460257f6bc..5fbab8fd0c 100644 --- a/target-i386/misc_helper.c +++ b/target-i386/misc_helper.c @@ -361,6 +361,12 @@ void helper_wrmsr(CPUX86State *env) case MSR_IA32_MISC_ENABLE: env->msr_ia32_misc_enable = val; break; + case MSR_IA32_BNDCFGS: + /* FIXME: #GP if reserved bits are set. */ + /* FIXME: Extend highest implemented bit of linear address. */ + env->msr_bndcfgs = val; + cpu_sync_bndcs_hflags(env); + break; default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + @@ -506,6 +512,9 @@ void helper_rdmsr(CPUX86State *env) case MSR_IA32_MISC_ENABLE: val = env->msr_ia32_misc_enable; break; + case MSR_IA32_BNDCFGS: + val = env->msr_bndcfgs; + break; default: if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + diff --git a/target-i386/mpx_helper.c b/target-i386/mpx_helper.c new file mode 100644 index 0000000000..1bf717af05 --- /dev/null +++ b/target-i386/mpx_helper.c @@ -0,0 +1,166 @@ +/* + * x86 MPX helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * 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 "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + + +void cpu_sync_bndcs_hflags(CPUX86State *env) +{ + uint32_t hflags = env->hflags; + uint32_t hflags2 = env->hflags2; + uint32_t bndcsr; + + if ((hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + if ((env->cr[4] & CR4_OSXSAVE_MASK) + && (env->xcr0 & XSTATE_BNDCSR) + && (bndcsr & BNDCFG_ENABLE)) { + hflags |= HF_MPX_EN_MASK; + } else { + hflags &= ~HF_MPX_EN_MASK; + } + + if (bndcsr & BNDCFG_BNDPRESERVE) { + hflags2 |= HF2_MPX_PR_MASK; + } else { + hflags2 &= ~HF2_MPX_PR_MASK; + } + + env->hflags = hflags; + env->hflags2 = hflags2; +} + +void helper_bndck(CPUX86State *env, uint32_t fail) +{ + if (unlikely(fail)) { + env->bndcs_regs.sts = 1; + raise_exception_ra(env, EXCP05_BOUND, GETPC()); + } +} + +static uint64_t lookup_bte64(CPUX86State *env, uint64_t base, uintptr_t ra) +{ + uint64_t bndcsr, bde, bt; + + if ((env->hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + bde = (extract64(base, 20, 28) << 3) + (extract64(bndcsr, 20, 44) << 12); + bt = cpu_ldq_data_ra(env, bde, ra); + if ((bt & 1) == 0) { + env->bndcs_regs.sts = bde | 2; + raise_exception_ra(env, EXCP05_BOUND, ra); + } + + return (extract64(base, 3, 17) << 5) + (bt & ~7); +} + +static uint32_t lookup_bte32(CPUX86State *env, uint32_t base, uintptr_t ra) +{ + uint32_t bndcsr, bde, bt; + + if ((env->hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + bde = (extract32(base, 12, 20) << 2) + (bndcsr & TARGET_PAGE_MASK); + bt = cpu_ldl_data_ra(env, bde, ra); + if ((bt & 1) == 0) { + env->bndcs_regs.sts = bde | 2; + raise_exception_ra(env, EXCP05_BOUND, ra); + } + + return (extract32(base, 2, 10) << 4) + (bt & ~3); +} + +uint64_t helper_bndldx64(CPUX86State *env, target_ulong base, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + uint64_t bte, lb, ub, pt; + + bte = lookup_bte64(env, base, ra); + lb = cpu_ldq_data_ra(env, bte, ra); + ub = cpu_ldq_data_ra(env, bte + 8, ra); + pt = cpu_ldq_data_ra(env, bte + 16, ra); + + if (pt != ptr) { + lb = ub = 0; + } + env->mmx_t0.MMX_Q(0) = ub; + return lb; +} + +uint64_t helper_bndldx32(CPUX86State *env, target_ulong base, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + uint32_t bte, lb, ub, pt; + + bte = lookup_bte32(env, base, ra); + lb = cpu_ldl_data_ra(env, bte, ra); + ub = cpu_ldl_data_ra(env, bte + 4, ra); + pt = cpu_ldl_data_ra(env, bte + 8, ra); + + if (pt != ptr) { + lb = ub = 0; + } + return ((uint64_t)ub << 32) | lb; +} + +void helper_bndstx64(CPUX86State *env, target_ulong base, target_ulong ptr, + uint64_t lb, uint64_t ub) +{ + uintptr_t ra = GETPC(); + uint64_t bte; + + bte = lookup_bte64(env, base, ra); + cpu_stq_data_ra(env, bte, lb, ra); + cpu_stq_data_ra(env, bte + 8, ub, ra); + cpu_stq_data_ra(env, bte + 16, ptr, ra); +} + +void helper_bndstx32(CPUX86State *env, target_ulong base, target_ulong ptr, + uint64_t lb, uint64_t ub) +{ + uintptr_t ra = GETPC(); + uint32_t bte; + + bte = lookup_bte32(env, base, ra); + cpu_stl_data_ra(env, bte, lb, ra); + cpu_stl_data_ra(env, bte + 4, ub, ra); + cpu_stl_data_ra(env, bte + 8, ptr, ra); +} + +void helper_bnd_jmp(CPUX86State *env) +{ + if (!(env->hflags2 & HF2_MPX_PR_MASK)) { + memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); + env->hflags &= ~HF_MPX_IU_MASK; + } +} diff --git a/target-i386/smm_helper.c b/target-i386/smm_helper.c index e7bb5be521..4dd6a2c544 100644 --- a/target-i386/smm_helper.c +++ b/target-i386/smm_helper.c @@ -99,6 +99,10 @@ void do_smm_enter(X86CPU *cpu) x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit); x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); + /* ??? Vol 1, 16.5.6 Intel MPX and SMM says that IA32_BNDCFGS + is saved at offset 7ED0. Vol 3, 34.4.1.1, Table 32-2, has + 7EA0-7ED7 as "reserved". What's this, and what's really + supposed to happen? */ x86_stq_phys(cs, sm_state + 0x7ed0, env->efer); x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]); diff --git a/target-i386/translate.c b/target-i386/translate.c index c8e2799269..9171929fc7 100644 --- a/target-i386/translate.c +++ b/target-i386/translate.c @@ -56,6 +56,12 @@ # define clztl clz32 #endif +/* For a switch indexed by MODRM, match all memory operands for a given OP. */ +#define CASE_MEM_OP(OP) \ + case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ + case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ + case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7 + //#define MACRO_TEST 1 /* global register indexes */ @@ -65,6 +71,8 @@ static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_srcT; static TCGv_i32 cpu_cc_op; static TCGv cpu_regs[CPU_NB_REGS]; static TCGv cpu_seg_base[6]; +static TCGv_i64 cpu_bndl[4]; +static TCGv_i64 cpu_bndu[4]; /* local temps */ static TCGv cpu_T0, cpu_T1; /* local register indexes (only used inside old micro ops) */ @@ -121,6 +129,7 @@ typedef struct DisasContext { int cpuid_ext2_features; int cpuid_ext3_features; int cpuid_7_0_ebx_features; + int cpuid_xsave_features; } DisasContext; static void gen_eob(DisasContext *s); @@ -1800,37 +1809,52 @@ static void gen_shifti(DisasContext *s1, int op, TCGMemOp ot, int d, int c) } } -static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) +/* Decompose an address. */ + +typedef struct AddressParts { + int def_seg; + int base; + int index; + int scale; + target_long disp; +} AddressParts; + +static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, + int modrm) { + int def_seg, base, index, scale, mod, rm; target_long disp; - int havesib, base, index, scale; - int mod, rm, code, def_seg, ovr_seg; - TCGv sum; + bool havesib; def_seg = R_DS; - ovr_seg = s->override; + index = -1; + scale = 0; + disp = 0; + mod = (modrm >> 6) & 3; rm = modrm & 7; + base = rm | REX_B(s); + + if (mod == 3) { + /* Normally filtered out earlier, but including this path + simplifies multi-byte nop, as well as bndcl, bndcu, bndcn. */ + goto done; + } switch (s->aflag) { case MO_64: case MO_32: havesib = 0; - base = rm; - index = -1; - scale = 0; - - if (base == 4) { - havesib = 1; - code = cpu_ldub_code(env, s->pc++); + if (rm == 4) { + int code = cpu_ldub_code(env, s->pc++); scale = (code >> 6) & 3; index = ((code >> 3) & 7) | REX_X(s); if (index == 4) { index = -1; /* no index */ } - base = (code & 7); + base = (code & 7) | REX_B(s); + havesib = 1; } - base |= REX_B(s); switch (mod) { case 0: @@ -1839,10 +1863,9 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) disp = (int32_t)cpu_ldl_code(env, s->pc); s->pc += 4; if (CODE64(s) && !havesib) { + base = -2; disp += s->pc + s->rip_offset; } - } else { - disp = 0; } break; case 1: @@ -1859,46 +1882,19 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) if (base == R_ESP && s->popl_esp_hack) { disp += s->popl_esp_hack; } - - /* Compute the address, with a minimum number of TCG ops. */ - TCGV_UNUSED(sum); - if (index >= 0) { - if (scale == 0) { - sum = cpu_regs[index]; - } else { - tcg_gen_shli_tl(cpu_A0, cpu_regs[index], scale); - sum = cpu_A0; - } - if (base >= 0) { - tcg_gen_add_tl(cpu_A0, sum, cpu_regs[base]); - sum = cpu_A0; - } - } else if (base >= 0) { - sum = cpu_regs[base]; - } - if (TCGV_IS_UNUSED(sum)) { - tcg_gen_movi_tl(cpu_A0, disp); - sum = cpu_A0; - } else if (disp != 0) { - tcg_gen_addi_tl(cpu_A0, sum, disp); - sum = cpu_A0; - } - if (base == R_EBP || base == R_ESP) { def_seg = R_SS; } break; case MO_16: - sum = cpu_A0; if (mod == 0) { if (rm == 6) { + base = -1; disp = cpu_lduw_code(env, s->pc); s->pc += 2; - tcg_gen_movi_tl(cpu_A0, disp); break; } - disp = 0; } else if (mod == 1) { disp = (int8_t)cpu_ldub_code(env, s->pc++); } else { @@ -1908,102 +1904,104 @@ static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) switch (rm) { case 0: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_ESI]); + base = R_EBX; + index = R_ESI; break; case 1: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBX], cpu_regs[R_EDI]); + base = R_EBX; + index = R_EDI; break; case 2: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_ESI]); + base = R_EBP; + index = R_ESI; def_seg = R_SS; break; case 3: - tcg_gen_add_tl(cpu_A0, cpu_regs[R_EBP], cpu_regs[R_EDI]); + base = R_EBP; + index = R_EDI; def_seg = R_SS; break; case 4: - sum = cpu_regs[R_ESI]; + base = R_ESI; break; case 5: - sum = cpu_regs[R_EDI]; + base = R_EDI; break; case 6: - sum = cpu_regs[R_EBP]; + base = R_EBP; def_seg = R_SS; break; default: case 7: - sum = cpu_regs[R_EBX]; + base = R_EBX; break; } - if (disp != 0) { - tcg_gen_addi_tl(cpu_A0, sum, disp); - sum = cpu_A0; - } break; default: tcg_abort(); } - gen_lea_v_seg(s, s->aflag, sum, def_seg, ovr_seg); + done: + return (AddressParts){ def_seg, base, index, scale, disp }; } -static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) +/* Compute the address, with a minimum number of TCG ops. */ +static TCGv gen_lea_modrm_1(AddressParts a) { - int mod, rm, base, code; + TCGv ea; - mod = (modrm >> 6) & 3; - if (mod == 3) - return; - rm = modrm & 7; + TCGV_UNUSED(ea); + if (a.index >= 0) { + if (a.scale == 0) { + ea = cpu_regs[a.index]; + } else { + tcg_gen_shli_tl(cpu_A0, cpu_regs[a.index], a.scale); + ea = cpu_A0; + } + if (a.base >= 0) { + tcg_gen_add_tl(cpu_A0, ea, cpu_regs[a.base]); + ea = cpu_A0; + } + } else if (a.base >= 0) { + ea = cpu_regs[a.base]; + } + if (TCGV_IS_UNUSED(ea)) { + tcg_gen_movi_tl(cpu_A0, a.disp); + ea = cpu_A0; + } else if (a.disp != 0) { + tcg_gen_addi_tl(cpu_A0, ea, a.disp); + ea = cpu_A0; + } - switch (s->aflag) { - case MO_64: - case MO_32: - base = rm; + return ea; +} - if (base == 4) { - code = cpu_ldub_code(env, s->pc++); - base = (code & 7); - } +static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(a); + gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); +} - switch (mod) { - case 0: - if (base == 5) { - s->pc += 4; - } - break; - case 1: - s->pc++; - break; - default: - case 2: - s->pc += 4; - break; - } - break; +static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ + (void)gen_lea_modrm_0(env, s, modrm); +} - case MO_16: - switch (mod) { - case 0: - if (rm == 6) { - s->pc += 2; - } - break; - case 1: - s->pc++; - break; - default: - case 2: - s->pc += 2; - break; - } - break; +/* Used for BNDCL, BNDCU, BNDCN. */ +static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, + TCGCond cond, TCGv_i64 bndv) +{ + TCGv ea = gen_lea_modrm_1(gen_lea_modrm_0(env, s, modrm)); - default: - tcg_abort(); + tcg_gen_extu_tl_i64(cpu_tmp1_i64, ea); + if (!CODE64(s)) { + tcg_gen_ext32u_i64(cpu_tmp1_i64, cpu_tmp1_i64); } + tcg_gen_setcond_i64(cond, cpu_tmp1_i64, cpu_tmp1_i64, bndv); + tcg_gen_extrl_i64_i32(cpu_tmp2_i32, cpu_tmp1_i64); + gen_helper_bndck(cpu_env, cpu_tmp2_i32); } /* used for LEA and MOV AX, mem */ @@ -2384,14 +2382,49 @@ static void gen_debug(DisasContext *s, target_ulong cur_eip) s->is_jmp = DISAS_TB_JUMP; } +static void gen_set_hflag(DisasContext *s, uint32_t mask) +{ + if ((s->flags & mask) == 0) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_ori_i32(t, t, mask); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_temp_free_i32(t); + s->flags |= mask; + } +} + +static void gen_reset_hflag(DisasContext *s, uint32_t mask) +{ + if (s->flags & mask) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_andi_i32(t, t, ~mask); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_temp_free_i32(t); + s->flags &= ~mask; + } +} + +/* Clear BND registers during legacy branches. */ +static void gen_bnd_jmp(DisasContext *s) +{ + /* Do nothing if BND prefix present, MPX is disabled, or if the + BNDREGs are known to be in INIT state already. The helper + itself will check BNDPRESERVE at runtime. */ + if ((s->prefix & PREFIX_REPNZ) == 0 + && (s->flags & HF_MPX_EN_MASK) == 0 + && (s->flags & HF_MPX_IU_MASK) == 0) { + gen_helper_bnd_jmp(cpu_env); + } +} + /* generate a generic end of block. Trace exception is also generated if needed */ static void gen_eob(DisasContext *s) { gen_update_cc_op(s); - if (s->tb->flags & HF_INHIBIT_IRQ_MASK) { - gen_helper_reset_inhibit_irq(cpu_env); - } + gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); if (s->tb->flags & HF_RF_MASK) { gen_helper_reset_rf(cpu_env); } @@ -4775,6 +4808,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_movi_tl(cpu_T1, next_eip); gen_push_v(s, cpu_T1); gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 3: /* lcall Ev */ @@ -4800,6 +4834,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, tcg_gen_ext16u_tl(cpu_T0, cpu_T0); } gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 5: /* ljmp Ev */ @@ -5140,8 +5175,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, /* if reg == SS, inhibit interrupts/trace. */ /* If several instructions disable interrupts, only the _first_ does it */ - if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) - gen_helper_set_inhibit_irq(cpu_env); + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); s->tf = 0; } if (s->is_jmp) { @@ -5208,8 +5242,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, /* if reg == SS, inhibit interrupts/trace */ /* If several instructions disable interrupts, only the _first_ does it */ - if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) - gen_helper_set_inhibit_irq(cpu_env); + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); s->tf = 0; } if (s->is_jmp) { @@ -5275,19 +5308,16 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; case 0x8d: /* lea */ - ot = dflag; modrm = cpu_ldub_code(env, s->pc++); mod = (modrm >> 6) & 3; if (mod == 3) goto illegal_op; reg = ((modrm >> 3) & 7) | rex_r; - /* we must ensure that no segment is added */ - s->override = -1; - val = s->addseg; - s->addseg = 0; - gen_lea_modrm(env, s, modrm); - s->addseg = val; - gen_op_mov_reg_v(ot, reg, cpu_A0); + { + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(a); + gen_op_mov_reg_v(dflag, reg, ea); + } break; case 0xa0: /* mov EAX, Ov */ @@ -6186,6 +6216,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_stack_update(s, val + (1 << ot)); /* Note that gen_pop_T0 uses a zero-extending load. */ gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 0xc3: /* ret */ @@ -6193,6 +6224,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, gen_pop_update(s, ot); /* Note that gen_pop_T0 uses a zero-extending load. */ gen_op_jmp_v(cpu_T0); + gen_bnd_jmp(s); gen_eob(s); break; case 0xca: /* lret im */ @@ -6259,6 +6291,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } tcg_gen_movi_tl(cpu_T0, next_eip); gen_push_v(s, cpu_T0); + gen_bnd_jmp(s); gen_jmp(s, tval); } break; @@ -6288,6 +6321,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } else if (!CODE64(s)) { tval &= 0xffffffff; } + gen_bnd_jmp(s); gen_jmp(s, tval); break; case 0xea: /* ljmp im */ @@ -6327,6 +6361,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, if (dflag == MO_16) { tval &= 0xffff; } + gen_bnd_jmp(s); gen_jcc(s, b, tval, next_eip); break; @@ -6745,8 +6780,7 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, /* interruptions are enabled only the first insn after sti */ /* If several instructions disable interrupts, only the _first_ does it */ - if (!(s->tb->flags & HF_INHIBIT_IRQ_MASK)) - gen_helper_set_inhibit_irq(cpu_env); + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); /* give a chance to handle pending irqs */ gen_jmp_im(s->pc - s->cs_base); gen_eob(s); @@ -7000,15 +7034,11 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, goto illegal_op; } break; + case 0x101: modrm = cpu_ldub_code(env, s->pc++); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - rm = modrm & 7; - switch(op) { - case 0: /* sgdt */ - if (mod == 3) - goto illegal_op; + switch (modrm) { + CASE_MEM_OP(0): /* sgdt */ gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ); gen_lea_modrm(env, s, modrm); tcg_gen_ld32u_tl(cpu_T0, @@ -7021,178 +7051,230 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, } gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); break; - case 1: - if (mod == 3) { - switch (rm) { - case 0: /* monitor */ - if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || - s->cpl != 0) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EAX]); - gen_extu(s->aflag, cpu_A0); - gen_add_A0_ds_seg(s); - gen_helper_monitor(cpu_env, cpu_A0); - break; - case 1: /* mwait */ - if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || - s->cpl != 0) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); - gen_eob(s); - break; - case 2: /* clac */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || - s->cpl != 0) { - goto illegal_op; - } - gen_helper_clac(cpu_env); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); - break; - case 3: /* stac */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) || - s->cpl != 0) { - goto illegal_op; - } - gen_helper_stac(cpu_env); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); - break; - default: - goto illegal_op; - } - } else { /* sidt */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(cpu_T0, - cpu_env, offsetof(CPUX86State, idt.limit)); - gen_op_st_v(s, MO_16, cpu_T0, cpu_A0); - gen_add_A0_im(s, 2); - tcg_gen_ld_tl(cpu_T0, - cpu_env, offsetof(CPUX86State, idt.base)); - if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); - } - gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + + case 0xc8: /* monitor */ + if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { + goto illegal_op; } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EAX]); + gen_extu(s->aflag, cpu_A0); + gen_add_A0_ds_seg(s); + gen_helper_monitor(cpu_env, cpu_A0); break; - case 2: /* lgdt */ - case 3: /* lidt */ - if (mod == 3) { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - switch(rm) { - case 0: /* VMRUN */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), - tcg_const_i32(s->pc - pc_start)); - tcg_gen_exit_tb(0); - s->is_jmp = DISAS_TB_JUMP; - } - break; - case 1: /* VMMCALL */ - if (!(s->flags & HF_SVME_MASK)) - goto illegal_op; - gen_helper_vmmcall(cpu_env); - break; - case 2: /* VMLOAD */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); - } - break; - case 3: /* VMSAVE */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); - } - break; - case 4: /* STGI */ - if ((!(s->flags & HF_SVME_MASK) && - !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || - !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_stgi(cpu_env); - } - break; - case 5: /* CLGI */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_clgi(cpu_env); - } - break; - case 6: /* SKINIT */ - if ((!(s->flags & HF_SVME_MASK) && - !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) || - !s->pe) - goto illegal_op; - gen_helper_skinit(cpu_env); - break; - case 7: /* INVLPGA */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } else { - gen_helper_invlpga(cpu_env, - tcg_const_i32(s->aflag - 1)); - } - break; - default: - goto illegal_op; - } - } else if (s->cpl != 0) { + + case 0xc9: /* mwait */ + if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_eob(s); + break; + + case 0xca: /* clac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) + || s->cpl != 0) { + goto illegal_op; + } + gen_helper_clac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xcb: /* stac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) + || s->cpl != 0) { + goto illegal_op; + } + gen_helper_stac(cpu_env); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + + CASE_MEM_OP(1): /* sidt */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); + gen_lea_modrm(env, s, modrm); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.limit)); + gen_op_st_v(s, MO_16, cpu_T0, cpu_A0); + gen_add_A0_im(s, 2); + tcg_gen_ld_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.base)); + if (dflag == MO_16) { + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + } + gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + break; + + case 0xd0: /* xgetbv */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xgetbv(cpu_tmp1_i64, cpu_env, cpu_tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], cpu_tmp1_i64); + break; + + case 0xd1: /* xsetbv */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, - op==2 ? SVM_EXIT_GDTR_WRITE : SVM_EXIT_IDTR_WRITE); - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_16, cpu_T1, cpu_A0); - gen_add_A0_im(s, 2); - gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); - if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); - } - if (op == 2) { - tcg_gen_st_tl(cpu_T0, cpu_env, - offsetof(CPUX86State, gdt.base)); - tcg_gen_st32_tl(cpu_T1, cpu_env, - offsetof(CPUX86State, gdt.limit)); - } else { - tcg_gen_st_tl(cpu_T0, cpu_env, - offsetof(CPUX86State, idt.base)); - tcg_gen_st32_tl(cpu_T1, cpu_env, - offsetof(CPUX86State, idt.limit)); - } + break; + } + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xsetbv(cpu_env, cpu_tmp2_i32, cpu_tmp1_i64); + /* End TB because translation flags may change. */ + gen_jmp_im(s->pc - pc_start); + gen_eob(s); + break; + + case 0xd8: /* VMRUN */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), + tcg_const_i32(s->pc - pc_start)); + tcg_gen_exit_tb(0); + s->is_jmp = DISAS_TB_JUMP; + break; + + case 0xd9: /* VMMCALL */ + if (!(s->flags & HF_SVME_MASK)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmmcall(cpu_env); + break; + + case 0xda: /* VMLOAD */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + case 0xdb: /* VMSAVE */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); break; - case 4: /* smsw */ + + case 0xdc: /* STGI */ + if ((!(s->flags & HF_SVME_MASK) + && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) + || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_stgi(cpu_env); + break; + + case 0xdd: /* CLGI */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_clgi(cpu_env); + break; + + case 0xde: /* SKINIT */ + if ((!(s->flags & HF_SVME_MASK) + && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) + || !s->pe) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_skinit(cpu_env); + break; + + case 0xdf: /* INVLPGA */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_helper_invlpga(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + CASE_MEM_OP(2): /* lgdt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_WRITE); + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_16, cpu_T1, cpu_A0); + gen_add_A0_im(s, 2); + gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + if (dflag == MO_16) { + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + } + tcg_gen_st_tl(cpu_T0, cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_st32_tl(cpu_T1, cpu_env, offsetof(CPUX86State, gdt.limit)); + break; + + CASE_MEM_OP(3): /* lidt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_WRITE); + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_16, cpu_T1, cpu_A0); + gen_add_A0_im(s, 2); + gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + if (dflag == MO_16) { + tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + } + tcg_gen_st_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.base)); + tcg_gen_st32_tl(cpu_T1, cpu_env, offsetof(CPUX86State, idt.limit)); + break; + + CASE_MEM_OP(4): /* smsw */ gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0); #if defined TARGET_X86_64 && defined HOST_WORDS_BIGENDIAN tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, cr[0]) + 4); @@ -7201,70 +7283,70 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, #endif gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 1); break; - case 6: /* lmsw */ + + CASE_MEM_OP(6): /* lmsw */ if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_helper_lmsw(cpu_env, cpu_T0); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); + break; } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_helper_lmsw(cpu_env, cpu_T0); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; - case 7: - if (mod != 3) { /* invlpg */ + + CASE_MEM_OP(7): /* invlpg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + gen_lea_modrm(env, s, modrm); + gen_helper_invlpg(cpu_env, cpu_A0); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xf8: /* swapgs */ +#ifdef TARGET_X86_64 + if (CODE64(s)) { if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - gen_lea_modrm(env, s, modrm); - gen_helper_invlpg(cpu_env, cpu_A0); - gen_jmp_im(s->pc - s->cs_base); - gen_eob(s); + tcg_gen_mov_tl(cpu_T0, cpu_seg_base[R_GS]); + tcg_gen_ld_tl(cpu_seg_base[R_GS], cpu_env, + offsetof(CPUX86State, kernelgsbase)); + tcg_gen_st_tl(cpu_T0, cpu_env, + offsetof(CPUX86State, kernelgsbase)); } - } else { - switch (rm) { - case 0: /* swapgs */ -#ifdef TARGET_X86_64 - if (CODE64(s)) { - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - tcg_gen_mov_tl(cpu_T0, cpu_seg_base[R_GS]); - tcg_gen_ld_tl(cpu_seg_base[R_GS], cpu_env, - offsetof(CPUX86State, kernelgsbase)); - tcg_gen_st_tl(cpu_T0, cpu_env, - offsetof(CPUX86State, kernelgsbase)); - } - break; - } + break; + } #endif - goto illegal_op; - case 1: /* rdtscp */ - if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); - if (s->tb->cflags & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtscp(cpu_env); - if (s->tb->cflags & CF_USE_ICOUNT) { - gen_io_end(); - gen_jmp(s, s->pc - s->cs_base); - } - break; - default: - goto illegal_op; - } + goto illegal_op; + + case 0xf9: /* rdtscp */ + if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(pc_start - s->cs_base); + if (s->tb->cflags & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_rdtscp(cpu_env); + if (s->tb->cflags & CF_USE_ICOUNT) { + gen_io_end(); + gen_jmp(s, s->pc - s->cs_base); } break; + default: goto illegal_op; } break; + case 0x108: /* invd */ case 0x109: /* wbinvd */ if (s->cpl != 0) { @@ -7393,7 +7475,199 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; } break; - case 0x119 ... 0x11f: /* nop (multi byte) */ + case 0x11a: + modrm = cpu_ldub_code(env, s->pc++); + if (s->flags & HF_MPX_EN_MASK) { + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + if (prefixes & PREFIX_REPZ) { + /* bndcl */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + gen_bndck(env, s, modrm, TCG_COND_LTU, cpu_bndl[reg]); + } else if (prefixes & PREFIX_REPNZ) { + /* bndcu */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + TCGv_i64 notu = tcg_temp_new_i64(); + tcg_gen_not_i64(notu, cpu_bndu[reg]); + gen_bndck(env, s, modrm, TCG_COND_GTU, notu); + tcg_temp_free_i64(notu); + } else if (prefixes & PREFIX_DATA) { + /* bndmov -- from reg/mem */ + if (reg >= 4 || s->aflag == MO_16) { + goto illegal_op; + } + if (mod == 3) { + int reg2 = (modrm & 7) | REX_B(s); + if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if (s->flags & HF_MPX_IU_MASK) { + tcg_gen_mov_i64(cpu_bndl[reg], cpu_bndl[reg2]); + tcg_gen_mov_i64(cpu_bndu[reg], cpu_bndu[reg2]); + } + } else { + gen_lea_modrm(env, s, modrm); + if (CODE64(s)) { + tcg_gen_qemu_ld_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEQ); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 8); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEQ); + } else { + tcg_gen_qemu_ld_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEUL); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 4); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEUL); + } + /* bnd registers are now in-use */ + gen_set_hflag(s, HF_MPX_IU_MASK); + } + } else if (mod != 3) { + /* bndldx */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16 + || a.base < -1) { + goto illegal_op; + } + if (a.base >= 0) { + tcg_gen_addi_tl(cpu_A0, cpu_regs[a.base], a.disp); + } else { + tcg_gen_movi_tl(cpu_A0, 0); + } + gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + if (a.index >= 0) { + tcg_gen_mov_tl(cpu_T0, cpu_regs[a.index]); + } else { + tcg_gen_movi_tl(cpu_T0, 0); + } + if (CODE64(s)) { + gen_helper_bndldx64(cpu_bndl[reg], cpu_env, cpu_A0, cpu_T0); + tcg_gen_ld_i64(cpu_bndu[reg], cpu_env, + offsetof(CPUX86State, mmx_t0.MMX_Q(0))); + } else { + gen_helper_bndldx32(cpu_bndu[reg], cpu_env, cpu_A0, cpu_T0); + tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndu[reg]); + tcg_gen_shri_i64(cpu_bndu[reg], cpu_bndu[reg], 32); + } + gen_set_hflag(s, HF_MPX_IU_MASK); + } + } + gen_nop_modrm(env, s, modrm); + break; + case 0x11b: + modrm = cpu_ldub_code(env, s->pc++); + if (s->flags & HF_MPX_EN_MASK) { + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + if (mod != 3 && (prefixes & PREFIX_REPZ)) { + /* bndmk */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (a.base >= 0) { + tcg_gen_extu_tl_i64(cpu_bndl[reg], cpu_regs[a.base]); + if (!CODE64(s)) { + tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndl[reg]); + } + } else if (a.base == -1) { + /* no base register has lower bound of 0 */ + tcg_gen_movi_i64(cpu_bndl[reg], 0); + } else { + /* rip-relative generates #ud */ + goto illegal_op; + } + tcg_gen_not_tl(cpu_A0, gen_lea_modrm_1(a)); + if (!CODE64(s)) { + tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + } + tcg_gen_extu_tl_i64(cpu_bndu[reg], cpu_A0); + /* bnd registers are now in-use */ + gen_set_hflag(s, HF_MPX_IU_MASK); + break; + } else if (prefixes & PREFIX_REPNZ) { + /* bndcn */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + gen_bndck(env, s, modrm, TCG_COND_GTU, cpu_bndu[reg]); + } else if (prefixes & PREFIX_DATA) { + /* bndmov -- to reg/mem */ + if (reg >= 4 || s->aflag == MO_16) { + goto illegal_op; + } + if (mod == 3) { + int reg2 = (modrm & 7) | REX_B(s); + if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if (s->flags & HF_MPX_IU_MASK) { + tcg_gen_mov_i64(cpu_bndl[reg2], cpu_bndl[reg]); + tcg_gen_mov_i64(cpu_bndu[reg2], cpu_bndu[reg]); + } + } else { + gen_lea_modrm(env, s, modrm); + if (CODE64(s)) { + tcg_gen_qemu_st_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEQ); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 8); + tcg_gen_qemu_st_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEQ); + } else { + tcg_gen_qemu_st_i64(cpu_bndl[reg], cpu_A0, + s->mem_index, MO_LEUL); + tcg_gen_addi_tl(cpu_A0, cpu_A0, 4); + tcg_gen_qemu_st_i64(cpu_bndu[reg], cpu_A0, + s->mem_index, MO_LEUL); + } + } + } else if (mod != 3) { + /* bndstx */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16 + || a.base < -1) { + goto illegal_op; + } + if (a.base >= 0) { + tcg_gen_addi_tl(cpu_A0, cpu_regs[a.base], a.disp); + } else { + tcg_gen_movi_tl(cpu_A0, 0); + } + gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + if (a.index >= 0) { + tcg_gen_mov_tl(cpu_T0, cpu_regs[a.index]); + } else { + tcg_gen_movi_tl(cpu_T0, 0); + } + if (CODE64(s)) { + gen_helper_bndstx64(cpu_env, cpu_A0, cpu_T0, + cpu_bndl[reg], cpu_bndu[reg]); + } else { + gen_helper_bndstx32(cpu_env, cpu_A0, cpu_T0, + cpu_bndl[reg], cpu_bndu[reg]); + } + } + } + gen_nop_modrm(env, s, modrm); + break; + case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ modrm = cpu_ldub_code(env, s->pc++); gen_nop_modrm(env, s, modrm); break; @@ -7503,96 +7777,189 @@ static target_ulong disas_insn(CPUX86State *env, DisasContext *s, break; case 0x1ae: modrm = cpu_ldub_code(env, s->pc++); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* fxsave */ - if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || - (s->prefix & PREFIX_LOCK)) + switch (modrm) { + CASE_MEM_OP(0): /* fxsave */ + if (!(s->cpuid_features & CPUID_FXSR) + || (prefixes & PREFIX_LOCK)) { goto illegal_op; + } if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); break; } gen_lea_modrm(env, s, modrm); - gen_helper_fxsave(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); + gen_helper_fxsave(cpu_env, cpu_A0); break; - case 1: /* fxrstor */ - if (mod == 3 || !(s->cpuid_features & CPUID_FXSR) || - (s->prefix & PREFIX_LOCK)) + + CASE_MEM_OP(1): /* fxrstor */ + if (!(s->cpuid_features & CPUID_FXSR) + || (prefixes & PREFIX_LOCK)) { goto illegal_op; + } if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); break; } gen_lea_modrm(env, s, modrm); - gen_helper_fxrstor(cpu_env, cpu_A0, tcg_const_i32(dflag == MO_64)); + gen_helper_fxrstor(cpu_env, cpu_A0); break; - case 2: /* ldmxcsr */ - case 3: /* stmxcsr */ + + CASE_MEM_OP(2): /* ldmxcsr */ + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { + goto illegal_op; + } if (s->flags & HF_TS_MASK) { gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); break; } - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK) || - mod == 3) + gen_lea_modrm(env, s, modrm); + tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, s->mem_index, MO_LEUL); + gen_helper_ldmxcsr(cpu_env, cpu_tmp2_i32); + break; + + CASE_MEM_OP(3): /* stmxcsr */ + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { goto illegal_op; + } + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } gen_lea_modrm(env, s, modrm); - if (op == 2) { - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, - s->mem_index, MO_LEUL); - gen_helper_ldmxcsr(cpu_env, cpu_tmp2_i32); - } else { - tcg_gen_ld32u_tl(cpu_T0, - cpu_env, offsetof(CPUX86State, mxcsr)); - gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); + tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, mxcsr)); + gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); + break; + + CASE_MEM_OP(4): /* xsave */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (prefixes & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xsave(cpu_env, cpu_A0, cpu_tmp1_i64); break; - case 5: /* lfence */ - if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE2)) + + CASE_MEM_OP(5): /* xrstor */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (prefixes & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xrstor(cpu_env, cpu_A0, cpu_tmp1_i64); + /* XRSTOR is how MPX is enabled, which changes how + we translate. Thus we need to end the TB. */ + gen_update_cc_op(s); + gen_jmp_im(s->pc - s->cs_base); + gen_eob(s); break; - case 6: /* mfence/clwb */ - if (s->prefix & PREFIX_DATA) { + + CASE_MEM_OP(6): /* xsaveopt / clwb */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + if (prefixes & PREFIX_DATA) { /* clwb */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) { goto illegal_op; + } gen_nop_modrm(env, s, modrm); } else { - /* mfence */ - if ((modrm & 0xc7) != 0xc0 || !(s->cpuid_features & CPUID_SSE2)) + /* xsaveopt */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 + || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xsaveopt(cpu_env, cpu_A0, cpu_tmp1_i64); } break; - case 7: /* sfence / clflush / clflushopt / pcommit */ - if ((modrm & 0xc7) == 0xc0) { - if (s->prefix & PREFIX_DATA) { - /* pcommit */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT)) - goto illegal_op; - } else { - /* sfence */ - /* XXX: also check for cpuid_ext2_features & CPUID_EXT2_EMMX */ - if (!(s->cpuid_features & CPUID_SSE)) - goto illegal_op; + + CASE_MEM_OP(7): /* clflush / clflushopt */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + if (prefixes & PREFIX_DATA) { + /* clflushopt */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) { + goto illegal_op; } } else { - if (s->prefix & PREFIX_DATA) { - /* clflushopt */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) - goto illegal_op; + /* clflush */ + if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) + || !(s->cpuid_features & CPUID_CLFLUSH)) { + goto illegal_op; + } + } + gen_nop_modrm(env, s, modrm); + break; + + case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */ + case 0xc8 ... 0xc8: /* rdgsbase (f3 0f ae /1) */ + case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */ + case 0xd8 ... 0xd8: /* wrgsbase (f3 0f ae /3) */ + if (CODE64(s) + && (prefixes & PREFIX_REPZ) + && !(prefixes & PREFIX_LOCK) + && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) { + TCGv base, treg, src, dst; + + /* Preserve hflags bits by testing CR4 at runtime. */ + tcg_gen_movi_i32(cpu_tmp2_i32, CR4_FSGSBASE_MASK); + gen_helper_cr4_testbit(cpu_env, cpu_tmp2_i32); + + base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; + treg = cpu_regs[(modrm & 7) | REX_B(s)]; + + if (modrm & 0x10) { + /* wr*base */ + dst = base, src = treg; } else { - /* clflush */ - if (!(s->cpuid_features & CPUID_CLFLUSH)) - goto illegal_op; + /* rd*base */ + dst = treg, src = base; } - gen_lea_modrm(env, s, modrm); + + if (s->dflag == MO_32) { + tcg_gen_ext32u_tl(dst, src); + } else { + tcg_gen_mov_tl(dst, src); + } + break; + } + goto illegal_op; + + case 0xf8: /* sfence / pcommit */ + if (prefixes & PREFIX_DATA) { + /* pcommit */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + break; + } + /* fallthru */ + case 0xf9 ... 0xff: /* sfence */ + case 0xe8 ... 0xef: /* lfence */ + case 0xf0 ... 0xf7: /* mfence */ + if (!(s->cpuid_features & CPUID_SSE2) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; } break; + default: goto illegal_op; } break; + case 0x10d: /* 3DNow! prefetch(w) */ modrm = cpu_ldub_code(env, s->pc++); mod = (modrm >> 6) & 3; @@ -7699,6 +8066,12 @@ void tcg_x86_init(void) [R_GS] = "gs_base", [R_SS] = "ss_base", }; + static const char bnd_regl_names[4][8] = { + "bnd0_lb", "bnd1_lb", "bnd2_lb", "bnd3_lb" + }; + static const char bnd_regu_names[4][8] = { + "bnd0_ub", "bnd1_ub", "bnd2_ub", "bnd3_ub" + }; int i; cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); @@ -7724,6 +8097,17 @@ void tcg_x86_init(void) seg_base_names[i]); } + for (i = 0; i < 4; ++i) { + cpu_bndl[i] + = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUX86State, bnd_regs[i].lb), + bnd_regl_names[i]); + cpu_bndu[i] + = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUX86State, bnd_regs[i].ub), + bnd_regu_names[i]); + } + helper_lock_init(); } @@ -7770,6 +8154,7 @@ void gen_intermediate_code(CPUX86State *env, TranslationBlock *tb) dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; + dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; #ifdef TARGET_X86_64 dc->lma = (flags >> HF_LMA_SHIFT) & 1; dc->code64 = (flags >> HF_CS64_SHIFT) & 1; diff --git a/target-ppc/kvm_ppc.h b/target-ppc/kvm_ppc.h index 62406ce4e1..aaa828c055 100644 --- a/target-ppc/kvm_ppc.h +++ b/target-ppc/kvm_ppc.h @@ -184,11 +184,6 @@ static inline uint64_t kvmppc_rma_size(uint64_t current_size, return ram_size; } -static inline int kvmppc_update_sdr1(CPUPPCState *env) -{ - return 0; -} - #endif /* !CONFIG_USER_ONLY */ static inline bool kvmppc_has_cap_epr(void) diff --git a/target-ppc/mmu-hash64.h b/target-ppc/mmu-hash64.h index ab0f86b222..e7d9925411 100644 --- a/target-ppc/mmu-hash64.h +++ b/target-ppc/mmu-hash64.h @@ -102,7 +102,7 @@ static inline target_ulong ppc_hash64_load_hpte0(PowerPCCPU *cpu, uint64_t addr; addr = token + (index * HASH_PTE_SIZE_64); - if (env->external_htab) { + if (kvmppc_kern_htab || env->external_htab) { return ldq_p((const void *)(uintptr_t)addr); } else { return ldq_phys(CPU(cpu)->as, addr); @@ -116,7 +116,7 @@ static inline target_ulong ppc_hash64_load_hpte1(PowerPCCPU *cpu, uint64_t addr; addr = token + (index * HASH_PTE_SIZE_64) + HASH_PTE_SIZE_64/2; - if (env->external_htab) { + if (kvmppc_kern_htab || env->external_htab) { return ldq_p((const void *)(uintptr_t)addr); } else { return ldq_phys(CPU(cpu)->as, addr); diff --git a/target-ppc/translate.c b/target-ppc/translate.c index ffef754fe0..ecc85f0e6d 100644 --- a/target-ppc/translate.c +++ b/target-ppc/translate.c @@ -11352,7 +11352,9 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, case POWERPC_MMU_64B: case POWERPC_MMU_2_03: case POWERPC_MMU_2_06: + case POWERPC_MMU_2_06a: case POWERPC_MMU_2_07: + case POWERPC_MMU_2_07a: #endif cpu_fprintf(f, " SDR1 " TARGET_FMT_lx " DAR " TARGET_FMT_lx " DSISR " TARGET_FMT_lx "\n", env->spr[SPR_SDR1], diff --git a/tests/Makefile b/tests/Makefile index fc042f7309..839d357d46 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -391,8 +391,8 @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \ tests/test-qapi-event.o tests/test-qmp-introspect.o \ $(test-qom-obj-y) test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) -test-block-obj-y = $(block-obj-y) $(test-crypto-obj-y) test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y) +test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y) tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y) diff --git a/tests/ac97-test.c b/tests/ac97-test.c index af30ea1dd6..75cab8f98f 100644 --- a/tests/ac97-test.c +++ b/tests/ac97-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 3488058a18..0a80ddf04b 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -10,13 +10,11 @@ * See the COPYING file in the top-level directory. */ -#include <string.h> -#include <stdio.h> +#include "qemu/osdep.h" #include <glib.h> #include <glib/gstdio.h> #include "qemu-common.h" #include "libqtest.h" -#include "qemu/compiler.h" #include "hw/acpi/acpi-defs.h" #include "hw/smbios/smbios.h" #include "qemu/bitmap.h" diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c index 360a6911eb..210964a00c 100644 --- a/tests/boot-order-test.c +++ b/tests/boot-order-test.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include <string.h> +#include "qemu/osdep.h" #include <glib.h> #include "libqos/fw_cfg.h" #include "libqtest.h" diff --git a/tests/check-qdict.c b/tests/check-qdict.c index a136f2addf..a43056c5de 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qapi/qmp/qint.h" diff --git a/tests/check-qfloat.c b/tests/check-qfloat.c index 6404ac8df6..3102608f55 100644 --- a/tests/check-qfloat.c +++ b/tests/check-qfloat.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. * */ +#include "qemu/osdep.h" #include <glib.h> #include "qapi/qmp/qfloat.h" diff --git a/tests/check-qint.c b/tests/check-qint.c index 86868844ab..c86f7dfa38 100644 --- a/tests/check-qint.c +++ b/tests/check-qint.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qapi/qmp/qint.h" diff --git a/tests/check-qjson.c b/tests/check-qjson.c index 61e9bfbabf..99de6f5252 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. * */ +#include "qemu/osdep.h" #include <glib.h> #include "qapi/qmp/qstring.h" diff --git a/tests/check-qlist.c b/tests/check-qlist.c index b9c05d43fd..f231d5fa97 100644 --- a/tests/check-qlist.c +++ b/tests/check-qlist.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qapi/qmp/qint.h" diff --git a/tests/check-qom-interface.c b/tests/check-qom-interface.c index f06380ef14..09354deb70 100644 --- a/tests/check-qom-interface.c +++ b/tests/check-qom-interface.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qom/object.h" diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c index 448d270b68..a2bb556906 100644 --- a/tests/check-qom-proplist.c +++ b/tests/check-qom-proplist.c @@ -18,6 +18,7 @@ * Author: Daniel P. Berrange <berrange@redhat.com> */ +#include "qemu/osdep.h" #include <glib.h> #include "qom/object.h" diff --git a/tests/check-qstring.c b/tests/check-qstring.c index 95dc9e3e7b..9877b42c89 100644 --- a/tests/check-qstring.c +++ b/tests/check-qstring.c @@ -9,6 +9,7 @@ * This work is licensed under the terms of the GNU LGPL, version 2.1 or later. * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qapi/qmp/qstring.h" diff --git a/tests/crypto-tls-x509-helpers.c b/tests/crypto-tls-x509-helpers.c index 47b4c7ba53..64073d3bd3 100644 --- a/tests/crypto-tls-x509-helpers.c +++ b/tests/crypto-tls-x509-helpers.c @@ -18,10 +18,8 @@ * Author: Daniel P. Berrange <berrange@redhat.com> */ -#include <stdlib.h> -#include <fcntl.h> +#include "qemu/osdep.h" -#include "config-host.h" #include "crypto-tls-x509-helpers.h" #include "qemu/sockets.h" diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index 11d5fea3e2..4477926014 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -17,8 +17,8 @@ * bugs around here. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdarg.h> #include "qemu-common.h" #include "qapi/qmp/qstring.h" #include "libqtest.h" diff --git a/tests/display-vga-test.c b/tests/display-vga-test.c index 7694344eaf..5706d338a1 100644 --- a/tests/display-vga-test.c +++ b/tests/display-vga-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" static void pci_cirrus(void) { diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 33909469f1..fe03236f3a 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -10,8 +10,8 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" static void drive_add(void) diff --git a/tests/ds1338-test.c b/tests/ds1338-test.c index 7d513d8972..2792415841 100644 --- a/tests/ds1338-test.c +++ b/tests/ds1338-test.c @@ -17,6 +17,7 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "libqtest.h" #include "libqos/i2c.h" diff --git a/tests/e1000-test.c b/tests/e1000-test.c index 7ca6d7e72e..a42b3810c1 100644 --- a/tests/e1000-test.c +++ b/tests/e1000-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void test_device(gconstpointer data) diff --git a/tests/eepro100-test.c b/tests/eepro100-test.c index 8bfaccdcbb..e17eed0b7a 100644 --- a/tests/eepro100-test.c +++ b/tests/eepro100-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" static void test_device(gconstpointer data) { diff --git a/tests/endianness-test.c b/tests/endianness-test.c index 2054338e18..cc5bccd88e 100644 --- a/tests/endianness-test.c +++ b/tests/endianness-test.c @@ -11,11 +11,8 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> #include "libqtest.h" #include "qemu/bswap.h" diff --git a/tests/es1370-test.c b/tests/es1370-test.c index cc23fb5c67..824dc31c64 100644 --- a/tests/es1370-test.c +++ b/tests/es1370-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/fdc-test.c b/tests/fdc-test.c index dbabf50a9a..53df1d0d88 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -22,9 +22,7 @@ * THE SOFTWARE. */ -#include <stdint.h> -#include <string.h> -#include <stdio.h> +#include "qemu/osdep.h" #include <glib.h> diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c index b7d4007e32..398643aada 100644 --- a/tests/fw_cfg-test.c +++ b/tests/fw_cfg-test.c @@ -10,7 +10,7 @@ * See the COPYING file in the top-level directory. */ -#include <string.h> +#include "qemu/osdep.h" #include <glib.h> #include "libqtest.h" diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c index 13b763d6a7..c8e669ac26 100644 --- a/tests/hd-geo-test.c +++ b/tests/hd-geo-test.c @@ -15,10 +15,8 @@ * Improvements welcome. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> #include "qemu-common.h" #include "libqtest.h" diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c index 7fa170990f..05029e90b2 100644 --- a/tests/i440fx-test.c +++ b/tests/i440fx-test.c @@ -12,13 +12,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> #include <sys/mman.h> -#include <stdlib.h> #include "libqtest.h" #include "libqos/pci.h" @@ -27,8 +23,6 @@ #define BROKEN 1 -#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0])) - typedef struct TestData { int num_cpus; diff --git a/tests/i82801b11-test.c b/tests/i82801b11-test.c index 78d9ce0e6b..c3b5ebbca1 100644 --- a/tests/i82801b11-test.c +++ b/tests/i82801b11-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/intel-hda-test.c b/tests/intel-hda-test.c index d89b407dcc..1be6add9b5 100644 --- a/tests/intel-hda-test.c +++ b/tests/intel-hda-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" #define HDA_ID "hda0" #define CODEC_DEVICES " -device hda-output,bus=" HDA_ID ".0" \ diff --git a/tests/io-channel-helpers.c b/tests/io-channel-helpers.c index 78d36dd703..844066904b 100644 --- a/tests/io-channel-helpers.c +++ b/tests/io-channel-helpers.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include "io-channel-helpers.h" struct QIOChannelTest { diff --git a/tests/ioh3420-test.c b/tests/ioh3420-test.c index c991a5f873..93eb2f7506 100644 --- a/tests/ioh3420-test.c +++ b/tests/ioh3420-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c index 5b7eb73949..812907fb7b 100644 --- a/tests/ipmi-bt-test.c +++ b/tests/ipmi-bt-test.c @@ -22,10 +22,7 @@ * THE SOFTWARE. */ -#include <sys/types.h> -#include <stdint.h> -#include <string.h> -#include <stdio.h> +#include "qemu/osdep.h" #include <sys/socket.h> #include <netinet/in.h> diff --git a/tests/ipmi-kcs-test.c b/tests/ipmi-kcs-test.c index 564c470f55..42c4b974c5 100644 --- a/tests/ipmi-kcs-test.c +++ b/tests/ipmi-kcs-test.c @@ -22,9 +22,7 @@ * THE SOFTWARE. */ -#include <stdint.h> -#include <string.h> -#include <stdio.h> +#include "qemu/osdep.h" #include <glib.h> diff --git a/tests/ipoctal232-test.c b/tests/ipoctal232-test.c index 3ac1714b48..846aaf5711 100644 --- a/tests/ipoctal232-test.c +++ b/tests/ipoctal232-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 705fece717..e184c67a1d 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -8,17 +8,13 @@ * See the COPYING file in the top-level directory. */ -#include <errno.h> -#include <fcntl.h> +#include "qemu/osdep.h" #include <glib.h> #include <glib/gstdio.h> -#include <string.h> #include <sys/mman.h> -#include <unistd.h> #include "contrib/ivshmem-server/ivshmem-server.h" #include "libqos/pci-pc.h" #include "libqtest.h" -#include "qemu/osdep.h" #include "qemu-common.h" #define TMPSHMSIZE (1 << 20) diff --git a/tests/libqos/fw_cfg.c b/tests/libqos/fw_cfg.c index ef00fedf1a..76894d5759 100644 --- a/tests/libqos/fw_cfg.c +++ b/tests/libqos/fw_cfg.c @@ -12,6 +12,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "libqos/fw_cfg.h" #include "libqtest.h" diff --git a/tests/libqos/i2c-imx.c b/tests/libqos/i2c-imx.c index b5cef66b94..51c3468f97 100644 --- a/tests/libqos/i2c-imx.c +++ b/tests/libqos/i2c-imx.c @@ -17,12 +17,11 @@ * with this program; if not, see <http://www.gnu.org/licenses/>. */ +#include "qemu/osdep.h" #include "libqos/i2c.h" #include <glib.h> -#include <string.h> -#include "qemu/osdep.h" #include "libqtest.h" #include "hw/i2c/imx_i2c.h" diff --git a/tests/libqos/i2c-omap.c b/tests/libqos/i2c-omap.c index 3d4d45d848..2028f2f146 100644 --- a/tests/libqos/i2c-omap.c +++ b/tests/libqos/i2c-omap.c @@ -6,12 +6,11 @@ * 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 "libqos/i2c.h" #include <glib.h> -#include <string.h> -#include "qemu/osdep.h" #include "qemu/bswap.h" #include "libqtest.h" diff --git a/tests/libqos/i2c.c b/tests/libqos/i2c.c index da7592f713..23bc2a3eb2 100644 --- a/tests/libqos/i2c.c +++ b/tests/libqos/i2c.c @@ -6,6 +6,7 @@ * 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 "libqos/i2c.h" #include "libqtest.h" diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index 1403699377..72b5e3ba09 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include "libqos/libqos-pc.h" #include "libqos/malloc-pc.h" diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 2d1a802dbe..79b0b29b4d 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -1,9 +1,5 @@ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include "qemu/osdep.h" #include <glib.h> -#include <unistd.h> -#include <fcntl.h> #include <sys/wait.h> #include "libqtest.h" diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c index d30a2f4240..6000df2b82 100644 --- a/tests/libqos/malloc-generic.c +++ b/tests/libqos/malloc-generic.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "libqos/malloc-generic.h" #include "libqos/malloc.h" diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index 6e253b6877..74f76c59db 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqos/malloc-pc.h" #include "libqos/fw_cfg.h" diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c index 19d05cafa6..c0df52f338 100644 --- a/tests/libqos/malloc.c +++ b/tests/libqos/malloc.c @@ -10,10 +10,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqos/malloc.h" #include "qemu-common.h" -#include <stdio.h> -#include <inttypes.h> #include <glib.h> typedef QTAILQ_HEAD(MemList, MemBlock) MemList; diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index 6dba0db00a..08167c09fe 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqtest.h" #include "libqos/pci-pc.h" diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index 80b1a2117d..0e104e14ed 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include "libqos/pci.h" #include "hw/pci/pci_regs.h" diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c index 41d89b8487..87efb90782 100644 --- a/tests/libqos/usb.c +++ b/tests/libqos/usb.c @@ -11,10 +11,9 @@ * 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 <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" #include "hw/usb/uhci-regs.h" #include "libqos/usb.h" diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index b3e62e77d8..a4382f3660 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdio.h> #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-mmio.h" diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index f9fb924b8e..fde2ff0bcb 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdio.h> #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 3205b88d90..613decea5a 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "libqtest.h" #include "libqos/virtio.h" diff --git a/tests/libqtest.c b/tests/libqtest.c index fa314e1ee7..b12a9e4ca9 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -14,22 +14,14 @@ * See the COPYING file in the top-level directory. * */ +#include "qemu/osdep.h" #include "libqtest.h" #include <glib.h> -#include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <sys/un.h> -#include <inttypes.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <string.h> - -#include "qemu/compiler.h" -#include "qemu/osdep.h" + #include "qapi/qmp/json-parser.h" #include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qjson.h" diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 71b4f28052..a751fd350e 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -12,11 +12,8 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> #include "libqtest.h" diff --git a/tests/ne2000-test.c b/tests/ne2000-test.c index 61a678ad30..3727875f2e 100644 --- a/tests/ne2000-test.c +++ b/tests/ne2000-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/tests/nvme-test.c b/tests/nvme-test.c index ff38b5e48f..ec06893eee 100644 --- a/tests/nvme-test.c +++ b/tests/nvme-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/pc-cpu-test.c b/tests/pc-cpu-test.c index 3505c7c43f..6b34ca588b 100644 --- a/tests/pc-cpu-test.c +++ b/tests/pc-cpu-test.c @@ -7,12 +7,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "qemu-common.h" #include "libqtest.h" -#include "qemu/osdep.h" #include "qapi/qmp/types.h" struct PCTestData { diff --git a/tests/pcnet-test.c b/tests/pcnet-test.c index 84af4f327a..2ddf4965c6 100644 --- a/tests/pcnet-test.c +++ b/tests/pcnet-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/tests/pkix_asn1_tab.c b/tests/pkix_asn1_tab.c index 5b4e6b9e2d..903bc02518 100644 --- a/tests/pkix_asn1_tab.c +++ b/tests/pkix_asn1_tab.c @@ -3,6 +3,7 @@ * and is under copyright of various GNUTLS contributors. */ +#include "qemu/osdep.h" #include <libtasn1.h> const ASN1_ARRAY_TYPE pkix_asn1_tab[] = { diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c index a7ad6b3064..d435833f79 100644 --- a/tests/pvpanic-test.c +++ b/tests/pvpanic-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" static void test_panic(void) { diff --git a/tests/q35-test.c b/tests/q35-test.c index 812abe5480..a105f10782 100644 --- a/tests/q35-test.c +++ b/tests/q35-test.c @@ -9,12 +9,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "qemu/osdep.h" #include "hw/pci-host/q35.h" static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled) diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out index fdedeb3973..72f1b4cf1c 100644 --- a/tests/qemu-iotests/140.out +++ b/tests/qemu-iotests/140.out @@ -9,7 +9,7 @@ read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "DEVICE_TRAY_MOVED", "data": {"device": "drv", "tray-open": true}} {"return": {}} -can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Failed to read export length +can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: No export with name 'drv' available no file open, try 'help open' {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out index dad20240a4..d24ad20db3 100644 --- a/tests/qemu-iotests/143.out +++ b/tests/qemu-iotests/143.out @@ -1,7 +1,7 @@ QA output created by 143 {"return": {}} {"return": {}} -can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Failed to read export length +can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: No export with name 'no_such_export' available {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} *** done diff --git a/tests/qemu-iotests/socket_scm_helper.c b/tests/qemu-iotests/socket_scm_helper.c index 81959835eb..80cadf43bc 100644 --- a/tests/qemu-iotests/socket_scm_helper.c +++ b/tests/qemu-iotests/socket_scm_helper.c @@ -10,15 +10,9 @@ * See the COPYING.LIB file in the top-level directory. */ -#include <stdio.h> -#include <errno.h> +#include "qemu/osdep.h" #include <sys/socket.h> #include <sys/un.h> -#include <stdlib.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> /* #define SOCKET_SCM_DEBUG */ diff --git a/tests/qom-test.c b/tests/qom-test.c index 3e5e8730e7..3c6cfca788 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -7,12 +7,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "qemu-common.h" #include "libqtest.h" -#include "qemu/osdep.h" #include "qapi/qmp/types.h" static const char *blacklist_x86[] = { diff --git a/tests/rcutorture.c b/tests/rcutorture.c index d6b304d000..244f0f28b2 100644 --- a/tests/rcutorture.c +++ b/tests/rcutorture.c @@ -60,13 +60,10 @@ * Test variables. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> #include "qemu/atomic.h" #include "qemu/rcu.h" -#include "qemu/compiler.h" #include "qemu/thread.h" long long n_reads = 0LL; diff --git a/tests/rtc-test.c b/tests/rtc-test.c index 4243624de6..fa7029aa8a 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -11,11 +11,8 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> #include "libqtest.h" #include "hw/timer/mc146818rtc_regs.h" diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index ba62851cae..54e5aa7d0e 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -7,11 +7,10 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" #include "libqos/pci-pc.h" -#include "qemu/osdep.h" #include "qemu/timer.h" #include "qemu-common.h" diff --git a/tests/spapr-phb-test.c b/tests/spapr-phb-test.c index b629de475a..f53911d9f7 100644 --- a/tests/spapr-phb-test.c +++ b/tests/spapr-phb-test.c @@ -7,6 +7,7 @@ * 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 <glib.h> #include "libqtest.h" diff --git a/tests/tco-test.c b/tests/tco-test.c index 419f7cf46c..ac11175e90 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -6,10 +6,8 @@ * 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 <glib.h> -#include <string.h> -#include <stdio.h> -#include <stdlib.h> #include "libqtest.h" #include "libqos/pci.h" diff --git a/tests/test-aio.c b/tests/test-aio.c index 6ccea98977..a109bd0c42 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "block/aio.h" #include "qemu/timer.h" diff --git a/tests/test-base64.c b/tests/test-base64.c index a0ca2d8de8..ae0c107c7d 100644 --- a/tests/test-base64.c +++ b/tests/test-base64.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu/base64.h" diff --git a/tests/test-bitops.c b/tests/test-bitops.c index 47b5d3ed9a..5050950607 100644 --- a/tests/test-bitops.c +++ b/tests/test-bitops.c @@ -6,9 +6,8 @@ * */ -#include <glib.h> -#include <stdint.h> #include "qemu/osdep.h" +#include <glib.h> #include "qemu/bitops.h" typedef struct { diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 34747e924d..c866da66c8 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -10,8 +10,8 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob.h" diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index f5951cb1f1..dd4ced946c 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -11,6 +11,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu/coroutine.h" #include "qemu/coroutine_int.h" diff --git a/tests/test-crypto-cipher.c b/tests/test-crypto-cipher.c index c687307bcd..9f912eccb5 100644 --- a/tests/test-crypto-cipher.c +++ b/tests/test-crypto-cipher.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "crypto/init.h" diff --git a/tests/test-crypto-hash.c b/tests/test-crypto-hash.c index 3ec31dde7b..735d6d7e0b 100644 --- a/tests/test-crypto-hash.c +++ b/tests/test-crypto-hash.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "crypto/init.h" diff --git a/tests/test-crypto-secret.c b/tests/test-crypto-secret.c index a49c8e31e8..2bbc4d9e3c 100644 --- a/tests/test-crypto-secret.c +++ b/tests/test-crypto-secret.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "crypto/init.h" diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c index c70aa5563a..7ca387db65 100644 --- a/tests/test-crypto-tlscredsx509.c +++ b/tests/test-crypto-tlscredsx509.c @@ -18,10 +18,8 @@ * Author: Daniel P. Berrange <berrange@redhat.com> */ -#include <stdlib.h> -#include <fcntl.h> +#include "qemu/osdep.h" -#include "config-host.h" #include "crypto-tls-x509-helpers.h" #include "crypto/tlscredsx509.h" diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 4524128fca..036a86b0c3 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -18,10 +18,8 @@ * Author: Daniel P. Berrange <berrange@redhat.com> */ -#include <stdlib.h> -#include <fcntl.h> +#include "qemu/osdep.h" -#include "config-host.h" #include "crypto-tls-x509-helpers.h" #include "crypto/tlscredsx509.h" #include "crypto/tlssession.h" diff --git a/tests/test-cutils.c b/tests/test-cutils.c index a3de6ab870..398700df45 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -25,9 +25,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include <glib.h> -#include <errno.h> -#include <string.h> #include "qemu-common.h" diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index abcea0cda6..abe1427917 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -9,10 +9,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdarg.h> -#include <string.h> -#include <sys/types.h> #include "qemu/hbitmap.h" #define LOG_BITS_PER_LONG (BITS_PER_LONG == 32 ? 5 : 6) diff --git a/tests/test-int128.c b/tests/test-int128.c index 0772ef7538..cacf6beac8 100644 --- a/tests/test-int128.c +++ b/tests/test-int128.c @@ -6,10 +6,9 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdio.h> #include "qemu/int128.h" -#include "qemu/osdep.h" /* clang doesn't support __noclone__ but it does have a mechanism for * telling us this. We assume that if we don't have __has_attribute() diff --git a/tests/test-io-channel-buffer.c b/tests/test-io-channel-buffer.c index 6637501b41..64722a214b 100644 --- a/tests/test-io-channel-buffer.c +++ b/tests/test-io-channel-buffer.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include "io/channel-buffer.h" #include "io-channel-helpers.h" diff --git a/tests/test-io-channel-command.c b/tests/test-io-channel-command.c index 03cac36a3f..885543760a 100644 --- a/tests/test-io-channel-command.c +++ b/tests/test-io-channel-command.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include "io/channel-command.h" #include "io-channel-helpers.h" diff --git a/tests/test-io-channel-file.c b/tests/test-io-channel-file.c index f276a32de0..1e7f3c7f12 100644 --- a/tests/test-io-channel-file.c +++ b/tests/test-io-channel-file.c @@ -18,7 +18,9 @@ * */ +#include "qemu/osdep.h" #include "io/channel-file.h" +#include "io/channel-util.h" #include "io-channel-helpers.h" @@ -49,6 +51,26 @@ static void test_io_channel_file(void) } +static void test_io_channel_fd(void) +{ + QIOChannel *ioc; + int fd = -1; + +#define TEST_FILE "tests/test-io-channel-file.txt" + fd = open(TEST_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0600); + g_assert_cmpint(fd, >, -1); + + ioc = qio_channel_new_fd(fd, &error_abort); + + g_assert_cmpstr(object_get_typename(OBJECT(ioc)), + ==, + TYPE_QIO_CHANNEL_FILE); + + unlink(TEST_FILE); + object_unref(OBJECT(ioc)); +} + + #ifndef _WIN32 static void test_io_channel_pipe(bool async) { @@ -92,6 +114,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_test_add_func("/io/channel/file", test_io_channel_file); + g_test_add_func("/io/channel/file/fd", test_io_channel_fd); #ifndef _WIN32 g_test_add_func("/io/channel/pipe/sync", test_io_channel_pipe_sync); g_test_add_func("/io/channel/pipe/async", test_io_channel_pipe_async); diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c index 1a36a3c696..069736373c 100644 --- a/tests/test-io-channel-socket.c +++ b/tests/test-io-channel-socket.c @@ -18,7 +18,9 @@ * */ +#include "qemu/osdep.h" #include "io/channel-socket.h" +#include "io/channel-util.h" #include "io-channel-helpers.h" #ifdef HAVE_IFADDRS_H #include <ifaddrs.h> @@ -473,6 +475,24 @@ static void test_io_channel_unix_fd_pass(void) #endif /* _WIN32 */ +static void test_io_channel_ipv4_fd(void) +{ + QIOChannel *ioc; + int fd = -1; + + fd = socket(AF_INET, SOCK_STREAM, 0); + g_assert_cmpint(fd, >, -1); + + ioc = qio_channel_new_fd(fd, &error_abort); + + g_assert_cmpstr(object_get_typename(OBJECT(ioc)), + ==, + TYPE_QIO_CHANNEL_SOCKET); + + object_unref(OBJECT(ioc)); +} + + int main(int argc, char **argv) { bool has_ipv4, has_ipv6; @@ -495,6 +515,8 @@ int main(int argc, char **argv) test_io_channel_ipv4_sync); g_test_add_func("/io/channel/socket/ipv4-async", test_io_channel_ipv4_async); + g_test_add_func("/io/channel/socket/ipv4-fd", + test_io_channel_ipv4_fd); } if (has_ipv6) { g_test_add_func("/io/channel/socket/ipv6-sync", diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index 3c11a5097c..3c361a7bef 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -21,10 +21,8 @@ */ -#include <stdlib.h> -#include <fcntl.h> +#include "qemu/osdep.h" -#include "config-host.h" #include "crypto-tls-x509-helpers.h" #include "io/channel-tls.h" #include "io/channel-socket.h" diff --git a/tests/test-io-task.c b/tests/test-io-task.c index 3344382c7f..ae46c56a47 100644 --- a/tests/test-io-task.c +++ b/tests/test-io-task.c @@ -18,6 +18,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "io/task.h" diff --git a/tests/test-iov.c b/tests/test-iov.c index 46e4dddc55..3f25268dd4 100644 --- a/tests/test-iov.c +++ b/tests/test-iov.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" #include "qemu/iov.h" diff --git a/tests/test-mul64.c b/tests/test-mul64.c index a0a17f7775..1282ec5a22 100644 --- a/tests/test-mul64.c +++ b/tests/test-mul64.c @@ -6,10 +6,9 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdint.h> #include "qemu/host-utils.h" -#include "qemu/osdep.h" typedef struct { diff --git a/tests/test-netfilter.c b/tests/test-netfilter.c index 303deb7e09..7d105c3232 100644 --- a/tests/test-netfilter.c +++ b/tests/test-netfilter.c @@ -8,6 +8,7 @@ * later. See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "libqtest.h" diff --git a/tests/test-opts-visitor.c b/tests/test-opts-visitor.c index 9600b97012..b7acf7d294 100644 --- a/tests/test-opts-visitor.c +++ b/tests/test-opts-visitor.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu/config-file.h" /* qemu_add_opts() */ diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index 07c182338b..f0cc31e113 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -22,8 +22,8 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdint.h> #include "hw/qdev.h" #include "qom/object.h" diff --git a/tests/test-qemu-opts.c b/tests/test-qemu-opts.c index 0c1136d1b2..848374e2bd 100644 --- a/tests/test-qemu-opts.c +++ b/tests/test-qemu-opts.c @@ -7,12 +7,11 @@ * See the COPYING.LIB file in the top-level directory. */ -#include "qapi/error.h" +#include "qemu/osdep.h" #include "qapi/qmp/qstring.h" #include "qemu/config-file.h" #include <glib.h> -#include <string.h> static QemuOptsList opts_list_01 = { .name = "opts_list_01", diff --git a/tests/test-qga.c b/tests/test-qga.c index e6a84d17f0..0973b487d2 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -1,18 +1,11 @@ +#include "qemu/osdep.h" #include <locale.h> #include <glib.h> #include <glib/gstdio.h> -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <string.h> -#include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> -#include <unistd.h> -#include <inttypes.h> #include "libqtest.h" -#include "config-host.h" #include "qga/guest-agent-core.h" typedef struct { diff --git a/tests/test-qmp-commands.c b/tests/test-qmp-commands.c index 4d267b6a83..d6171f2d44 100644 --- a/tests/test-qmp-commands.c +++ b/tests/test-qmp-commands.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" #include "qapi/qmp/types.h" diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 035c65cfdf..a296fdbac2 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -11,8 +11,8 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdarg.h> #include "qemu-common.h" #include "test-qapi-types.h" diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c index 775ad39d93..6a33aa41e5 100644 --- a/tests/test-qmp-input-strict.c +++ b/tests/test-qmp-input-strict.c @@ -11,8 +11,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdarg.h> #include "qemu-common.h" #include "qapi/qmp-input-visitor.h" diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c index f6bd408db3..c72cdad563 100644 --- a/tests/test-qmp-input-visitor.c +++ b/tests/test-qmp-input-visitor.c @@ -10,8 +10,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdarg.h> #include "qemu-common.h" #include "qapi/qmp-input-visitor.h" diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c index 26dc752b81..965f298e11 100644 --- a/tests/test-qmp-output-visitor.c +++ b/tests/test-qmp-output-visitor.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c index daa8bf41db..79d3750144 100644 --- a/tests/test-rcu-list.c +++ b/tests/test-rcu-list.c @@ -20,14 +20,10 @@ * Copyright (c) 2013 Mike D. Day, IBM Corporation. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> #include "qemu/atomic.h" #include "qemu/rcu.h" -#include "qemu/compiler.h" -#include "qemu/osdep.h" #include "qemu/thread.h" #include "qemu/rcu_queue.h" diff --git a/tests/test-rfifolock.c b/tests/test-rfifolock.c index 0572ebb42a..9a3cb243ba 100644 --- a/tests/test-rfifolock.c +++ b/tests/test-rfifolock.c @@ -10,6 +10,7 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" #include "qemu/rfifolock.h" diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index d99498d098..4b48ec25d3 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -10,8 +10,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdarg.h> #include "qemu-common.h" #include "qapi/string-input-visitor.h" diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c index 4f4450fe70..0beccf98c7 100644 --- a/tests/test-string-output-visitor.c +++ b/tests/test-string-output-visitor.c @@ -10,6 +10,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c index ccdee39993..40600b40bb 100644 --- a/tests/test-thread-pool.c +++ b/tests/test-thread-pool.c @@ -1,3 +1,4 @@ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" #include "block/aio.h" diff --git a/tests/test-throttle.c b/tests/test-throttle.c index a95039fdbf..858f1aa43f 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -12,6 +12,7 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include <math.h> #include "block/aio.h" diff --git a/tests/test-timed-average.c b/tests/test-timed-average.c index a049799b80..1cc4ab3027 100644 --- a/tests/test-timed-average.c +++ b/tests/test-timed-average.c @@ -10,8 +10,8 @@ * See the COPYING.LIB file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <unistd.h> #include "qemu/timed-average.h" diff --git a/tests/test-visitor-serialization.c b/tests/test-visitor-serialization.c index f74a6df97b..ef4dac5e01 100644 --- a/tests/test-visitor-serialization.c +++ b/tests/test-visitor-serialization.c @@ -11,9 +11,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdlib.h> -#include <stdint.h> #include <float.h> #include "qemu-common.h" diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 4d13bd09b3..713d4443b2 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include <glib.h> #include "qemu-common.h" diff --git a/tests/test-write-threshold.c b/tests/test-write-threshold.c index faffa7b855..fdbc8020fd 100644 --- a/tests/test-write-threshold.c +++ b/tests/test-write-threshold.c @@ -6,8 +6,8 @@ * */ +#include "qemu/osdep.h" #include <glib.h> -#include <stdint.h> #include "block/block_int.h" #include "block/write-threshold.h" diff --git a/tests/test-x86-cpuid.c b/tests/test-x86-cpuid.c index 6cd20d4a23..8eb0bc6ad5 100644 --- a/tests/test-x86-cpuid.c +++ b/tests/test-x86-cpuid.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ +#include "qemu/osdep.h" #include <glib.h> #include "hw/i386/topology.h" diff --git a/tests/test-xbzrle.c b/tests/test-xbzrle.c index 1cd8cb7d78..abd309d418 100644 --- a/tests/test-xbzrle.c +++ b/tests/test-xbzrle.c @@ -10,11 +10,7 @@ * See the COPYING file in the top-level directory. * */ -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/time.h> +#include "qemu/osdep.h" #include "qemu-common.h" #include "include/migration/migration.h" diff --git a/tests/tmp105-test.c b/tests/tmp105-test.c index 99db538191..235cae0137 100644 --- a/tests/tmp105-test.c +++ b/tests/tmp105-test.c @@ -7,6 +7,7 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> #include "libqtest.h" diff --git a/tests/tpci200-test.c b/tests/tpci200-test.c index 9ae01277ee..cb2b00ca8b 100644 --- a/tests/tpci200-test.c +++ b/tests/tpci200-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index f5030b247b..f2b6c67e5b 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -29,23 +29,13 @@ #define _FILE_OFFSET_BITS 64 -#include <stddef.h> -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include <inttypes.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <sys/types.h> +#include "qemu/osdep.h" #include <sys/socket.h> #include <sys/un.h> #include <sys/unistd.h> #include <sys/mman.h> #include <sys/eventfd.h> #include <arpa/inet.h> -#include <ctype.h> #include <netdb.h> #include <qemu/osdep.h> diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index e30b7f4abc..69615968ce 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -8,6 +8,7 @@ * */ +#include "qemu/osdep.h" #include <glib.h> #include "libqtest.h" diff --git a/tests/virtio-balloon-test.c b/tests/virtio-balloon-test.c index becebb51a7..b010ce98e8 100644 --- a/tests/virtio-balloon-test.c +++ b/tests/virtio-balloon-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 4078321a20..3a66630d79 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -8,11 +8,8 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> -#include <stdlib.h> -#include <unistd.h> -#include <stdio.h> #include "libqtest.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" diff --git a/tests/virtio-console-test.c b/tests/virtio-console-test.c index 6be96e8c64..0b9c2a55ef 100644 --- a/tests/virtio-console-test.c +++ b/tests/virtio-console-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void console_pci_nop(void) diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index 982d77a14b..04cfcd594e 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -7,12 +7,11 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" #include "qemu-common.h" #include "qemu/sockets.h" -#include "qemu/osdep.h" #include "qemu/iov.h" #include "libqos/pci-pc.h" #include "libqos/virtio.h" diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index 41c1cdb1aa..771dbd73af 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" #include "libqos/pci.h" #define PCI_SLOT_HP 0x06 diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 66d8491e9d..d78747a466 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -8,11 +8,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" -#include <stdio.h> #include "block/scsi.h" #include "libqos/virtio.h" #include "libqos/virtio-pci.h" diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c index bf030a6162..480d4abb2d 100644 --- a/tests/virtio-serial-test.c +++ b/tests/virtio-serial-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void pci_nop(void) diff --git a/tests/vmxnet3-test.c b/tests/vmxnet3-test.c index a2ebed39cc..6ef0e2f043 100644 --- a/tests/vmxnet3-test.c +++ b/tests/vmxnet3-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" /* Tests only initialization so far. TODO: Replace with functional tests */ static void nop(void) diff --git a/tests/wdt_ib700-test.c b/tests/wdt_ib700-test.c index 82ca597252..efe3370453 100644 --- a/tests/wdt_ib700-test.c +++ b/tests/wdt_ib700-test.c @@ -7,10 +7,9 @@ * See the COPYING file in the top-level directory. */ +#include "qemu/osdep.h" #include <glib.h> -#include <string.h> #include "libqtest.h" -#include "qemu/osdep.h" #include "qemu/timer.h" static void qmp_check_no_event(void) diff --git a/util/oslib-posix.c b/util/oslib-posix.c index d844387b79..7615be4e7a 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -26,15 +26,6 @@ * THE SOFTWARE. */ -/* The following block of code temporarily renames the daemon() function so the - compiler does not see the warning associated with it in stdlib.h on OSX */ -#ifdef __APPLE__ -#define daemon qemu_fake_daemon_function -#include <stdlib.h> -#undef daemon -extern int daemon(int, int); -#endif - #if defined(__linux__) && (defined(__x86_64__) || defined(__arm__)) /* Use 2 MiB alignment so transparent hugepages can be used by KVM. Valgrind does not support alignments larger than 1 MiB, @@ -579,6 +579,7 @@ static const RunStateTransition runstate_transitions_def[] = { /* from -> to */ { RUN_STATE_DEBUG, RUN_STATE_RUNNING }, { RUN_STATE_DEBUG, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_DEBUG, RUN_STATE_PRELAUNCH }, { RUN_STATE_INMIGRATE, RUN_STATE_INTERNAL_ERROR }, { RUN_STATE_INMIGRATE, RUN_STATE_IO_ERROR }, @@ -589,18 +590,24 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_INMIGRATE, RUN_STATE_WATCHDOG }, { RUN_STATE_INMIGRATE, RUN_STATE_GUEST_PANICKED }, { RUN_STATE_INMIGRATE, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_INMIGRATE, RUN_STATE_PRELAUNCH }, + { RUN_STATE_INMIGRATE, RUN_STATE_POSTMIGRATE }, { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PAUSED }, { RUN_STATE_INTERNAL_ERROR, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_INTERNAL_ERROR, RUN_STATE_PRELAUNCH }, { RUN_STATE_IO_ERROR, RUN_STATE_RUNNING }, { RUN_STATE_IO_ERROR, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_IO_ERROR, RUN_STATE_PRELAUNCH }, { RUN_STATE_PAUSED, RUN_STATE_RUNNING }, { RUN_STATE_PAUSED, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_PAUSED, RUN_STATE_PRELAUNCH }, { RUN_STATE_POSTMIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_POSTMIGRATE, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_POSTMIGRATE, RUN_STATE_PRELAUNCH }, { RUN_STATE_PRELAUNCH, RUN_STATE_RUNNING }, { RUN_STATE_PRELAUNCH, RUN_STATE_FINISH_MIGRATE }, @@ -608,8 +615,10 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_FINISH_MIGRATE, RUN_STATE_RUNNING }, { RUN_STATE_FINISH_MIGRATE, RUN_STATE_POSTMIGRATE }, + { RUN_STATE_FINISH_MIGRATE, RUN_STATE_PRELAUNCH }, { RUN_STATE_RESTORE_VM, RUN_STATE_RUNNING }, + { RUN_STATE_RESTORE_VM, RUN_STATE_PRELAUNCH }, { RUN_STATE_RUNNING, RUN_STATE_DEBUG }, { RUN_STATE_RUNNING, RUN_STATE_INTERNAL_ERROR }, @@ -626,17 +635,21 @@ static const RunStateTransition runstate_transitions_def[] = { { RUN_STATE_SHUTDOWN, RUN_STATE_PAUSED }, { RUN_STATE_SHUTDOWN, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_SHUTDOWN, RUN_STATE_PRELAUNCH }, { RUN_STATE_DEBUG, RUN_STATE_SUSPENDED }, { RUN_STATE_RUNNING, RUN_STATE_SUSPENDED }, { RUN_STATE_SUSPENDED, RUN_STATE_RUNNING }, { RUN_STATE_SUSPENDED, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_SUSPENDED, RUN_STATE_PRELAUNCH }, { RUN_STATE_WATCHDOG, RUN_STATE_RUNNING }, { RUN_STATE_WATCHDOG, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_WATCHDOG, RUN_STATE_PRELAUNCH }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_RUNNING }, { RUN_STATE_GUEST_PANICKED, RUN_STATE_FINISH_MIGRATE }, + { RUN_STATE_GUEST_PANICKED, RUN_STATE_PRELAUNCH }, { RUN_STATE__MAX, RUN_STATE__MAX }, }; @@ -1881,8 +1894,9 @@ static bool main_loop_should_exit(void) cpu_synchronize_all_states(); qemu_system_reset(VMRESET_REPORT); resume_all_vcpus(); - if (runstate_needs_reset()) { - runstate_set(RUN_STATE_PAUSED); + if (!runstate_check(RUN_STATE_RUNNING) && + !runstate_check(RUN_STATE_INMIGRATE)) { + runstate_set(RUN_STATE_PRELAUNCH); } } if (qemu_wakeup_requested()) { @@ -2816,64 +2830,6 @@ static bool object_create_delayed(const char *type) } -static int object_create(void *opaque, QemuOpts *opts, Error **errp) -{ - Error *err = NULL; - Error *err_end = NULL; - char *type = NULL; - char *id = NULL; - OptsVisitor *ov; - QDict *pdict; - bool (*type_predicate)(const char *) = opaque; - Visitor *v; - - ov = opts_visitor_new(opts); - pdict = qemu_opts_to_qdict(opts, NULL); - v = opts_get_visitor(ov); - - visit_start_struct(v, NULL, NULL, 0, &err); - if (err) { - goto out; - } - - qdict_del(pdict, "qom-type"); - visit_type_str(v, "qom-type", &type, &err); - if (err) { - goto out; - } - if (!type_predicate(type)) { - visit_end_struct(v, NULL); - goto out; - } - - qdict_del(pdict, "id"); - visit_type_str(v, "id", &id, &err); - if (err) { - goto out_end; - } - - object_add(type, id, pdict, v, &err); - -out_end: - visit_end_struct(v, &err_end); - if (!err && err_end) { - qmp_object_del(id, NULL); - } - error_propagate(&err, err_end); - -out: - opts_visitor_cleanup(ov); - - QDECREF(pdict); - g_free(id); - g_free(type); - if (err) { - error_report_err(err); - return -1; - } - return 0; -} - static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, MachineClass *mc) { @@ -4299,8 +4255,9 @@ int main(int argc, char **argv, char **envp) socket_init(); if (qemu_opts_foreach(qemu_find_opts("object"), - object_create, - object_create_initial, NULL)) { + user_creatable_add_opts_foreach, + object_create_initial, &err)) { + error_report_err(err); exit(1); } @@ -4417,8 +4374,9 @@ int main(int argc, char **argv, char **envp) } if (qemu_opts_foreach(qemu_find_opts("object"), - object_create, - object_create_delayed, NULL)) { + user_creatable_add_opts_foreach, + object_create_delayed, &err)) { + error_report_err(err); exit(1); } |