diff options
75 files changed, 3502 insertions, 544 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index a3a2ad7d2f..f3c1f7f307 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1364,6 +1364,15 @@ F: util/uuid.c F: include/qemu/uuid.h F: tests/test-uuid.c +COLO Proxy +M: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> +M: Li Zhijian <lizhijian@cn.fujitsu.com> +S: Supported +F: docs/colo-proxy.txt +F: net/colo* +F: net/filter-rewriter.c +F: net/filter-mirror.c + Usermode Emulation ------------------ Overall diff --git a/aio-posix.c b/aio-posix.c index 43162a9f29..4ef34dd175 100644 --- a/aio-posix.c +++ b/aio-posix.c @@ -431,11 +431,13 @@ bool aio_poll(AioContext *ctx, bool blocking) assert(npfd == 0); /* fill pollfds */ - QLIST_FOREACH(node, &ctx->aio_handlers, node) { - if (!node->deleted && node->pfd.events - && !aio_epoll_enabled(ctx) - && aio_node_check(ctx, node->is_external)) { - add_pollfd(node); + + if (!aio_epoll_enabled(ctx)) { + QLIST_FOREACH(node, &ctx->aio_handlers, node) { + if (!node->deleted && node->pfd.events + && aio_node_check(ctx, node->is_external)) { + add_pollfd(node); + } } } diff --git a/block/linux-aio.c b/block/linux-aio.c index d4e19d444c..1685ec29a3 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -94,9 +94,12 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb) laiocb->ret = ret; if (laiocb->co) { - /* Jump and continue completion for foreign requests, don't do - * anything for current request, it will be completed shortly. */ - if (laiocb->co != qemu_coroutine_self()) { + /* If the coroutine is already entered it must be in ioq_submit() and + * will notice laio->ret has been filled in when it eventually runs + * later. Coroutines cannot be entered recursively so avoid doing + * that! + */ + if (!qemu_coroutine_entered(laiocb->co)) { qemu_coroutine_enter(laiocb->co); } } else { @@ -1955,6 +1955,61 @@ EOF /* * If we have stable libs the we don't want the libxc compat * layers, regardless of what CFLAGS we may have been given. + * + * Also, check if xengnttab_grant_copy_segment_t is defined and + * grant copy operation is implemented. + */ +#undef XC_WANT_COMPAT_EVTCHN_API +#undef XC_WANT_COMPAT_GNTTAB_API +#undef XC_WANT_COMPAT_MAP_FOREIGN_API +#include <xenctrl.h> +#include <xenstore.h> +#include <xenevtchn.h> +#include <xengnttab.h> +#include <xenforeignmemory.h> +#include <stdint.h> +#include <xen/hvm/hvm_info_table.h> +#if !defined(HVM_MAX_VCPUS) +# error HVM_MAX_VCPUS not defined +#endif +int main(void) { + xc_interface *xc = NULL; + xenforeignmemory_handle *xfmem; + xenevtchn_handle *xe; + xengnttab_handle *xg; + xen_domain_handle_t handle; + xengnttab_grant_copy_segment_t* seg = NULL; + + xs_daemon_open(); + + xc = xc_interface_open(0, 0, 0); + xc_hvm_set_mem_type(0, 0, HVMMEM_ram_ro, 0, 0); + xc_domain_add_to_physmap(0, 0, XENMAPSPACE_gmfn, 0, 0); + xc_hvm_inject_msi(xc, 0, 0xf0000000, 0x00000000); + xc_hvm_create_ioreq_server(xc, 0, HVM_IOREQSRV_BUFIOREQ_ATOMIC, NULL); + xc_domain_create(xc, 0, handle, 0, NULL, NULL); + + xfmem = xenforeignmemory_open(0, 0); + xenforeignmemory_map(xfmem, 0, 0, 0, 0, 0); + + xe = xenevtchn_open(0, 0); + xenevtchn_fd(xe); + + xg = xengnttab_open(0, 0); + xengnttab_grant_copy(xg, 0, seg); + + return 0; +} +EOF + compile_prog "" "$xen_libs $xen_stable_libs" + then + xen_ctrl_version=480 + xen=yes + elif + cat > $TMPC <<EOF && +/* + * If we have stable libs the we don't want the libxc compat + * layers, regardless of what CFLAGS we may have been given. */ #undef XC_WANT_COMPAT_EVTCHN_API #undef XC_WANT_COMPAT_GNTTAB_API diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt new file mode 100644 index 0000000000..76767cb34f --- /dev/null +++ b/docs/colo-proxy.txt @@ -0,0 +1,188 @@ +COLO-proxy +---------- +Copyright (c) 2016 Intel Corporation +Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. +Copyright (c) 2016 Fujitsu, Corp. + +This work is licensed under the terms of the GNU GPL, version 2 or later. +See the COPYING file in the top-level directory. + +This document gives an overview of COLO proxy's design. + +== Background == +COLO-proxy is a part of COLO project. It is used +to compare the network package to help COLO decide +whether to do checkpoint. With COLO-proxy's help, +COLO greatly improves the performance. + +The filter-redirector, filter-mirror, colo-compare +and filter-rewriter compose the COLO-proxy. + +== Architecture == + +COLO-Proxy is based on qemu netfilter and it's a plugin for qemu netfilter +(except colo-compare). It keep Secondary VM connect normally to +client and compare packets sent by PVM with sent by SVM. +If the packet difference, notify COLO-frame to do checkpoint and send +all primary packet has queued. Otherwise just send the queued primary +packet and drop the queued secondary packet. + +Below is a COLO proxy ascii figure: + + Primary qemu Secondary qemu ++--------------------------------------------------------------+ +----------------------------------------------------------------+ +| +----------------------------------------------------------+ | | +-----------------------------------------------------------+ | +| | | | | | | | +| | guest | | | | guest | | +| | | | | | | | +| +-------^--------------------------+-----------------------+ | | +---------------------+--------+----------------------------+ | +| | | | | ^ | | +| | | | | | | | +| | +------------------------------------------------------+ | | | | +|netfilter| | | | | | netfilter | | | +| +----------+ +----------------------------+ | | | +-----------------------------------------------------------+ | +| | | | | | out | | | | | | filter excute order | | +| | | | +-----------------------------+ | | | | | | +-------------------> | | +| | | | | | | | | | | | | | TCP | | +| | +-----+--+-+ +-----v----+ +-----v----+ |pri +----+----+sec| | | | +------------+ +---+----+---v+rewriter++ +------------+ | | +| | | | | | | | |in | |in | | | | | | | | | | | | | +| | | filter | | filter | | filter +------> colo <------+ +--------> filter +--> adjust | adjust +--> filter | | | +| | | mirror | |redirector| |redirector| | | compare | | | | | | redirector | | ack | seq | | redirector | | | +| | | | | | | | | | | | | | | | | | | | | | | | +| | +----^-----+ +----+-----+ +----------+ | +---------+ | | | | +------------+ +--------+--------------+ +---+--------+ | | +| | | tx | rx rx | | | | | tx all | rx | | +| | | | | | | | +-----------------------------------------------------------+ | +| | | +--------------+ | | | | | | +| | | filter excute order | | | | | | | +| | | +----------------> | | | +--------------------------------------------------------+ | +| +-----------------------------------------+ | | | +| | | | | | ++--------------------------------------------------------------+ +----------------------------------------------------------------+ + |guest receive | guest send + | | ++--------+----------------------------v------------------------+ +| | NOTE: filter direction is rx/tx/all +| tap | rx:receive packets sent to the netdev +| | tx:receive packets sent by the netdev ++--------------------------------------------------------------+ + +1.Guest receive packet route: + +Primary: + +Tap --> Mirror Client Filter +Mirror client will send packet to guest,at the +same time, copy and forward packet to secondary +mirror server. + +Secondary: + +Mirror Server Filter --> TCP Rewriter +If receive packet is TCP packet,we will adjust ack +and update TCP checksum, then send to secondary +guest. Otherwise directly send to guest. + +2.Guest send packet route: + +Primary: + +Guest --> Redirect Server Filter +Redirect server filter receive primary guest packet +but do nothing, just pass to next filter. + +Redirect Server Filter --> COLO-Compare +COLO-compare receive primary guest packet then +waiting scondary redirect packet to compare it. +If packet same,send queued primary packet and clear +queued secondary packet, Otherwise send primary packet +and do checkpoint. + +COLO-Compare --> Another Redirector Filter +The redirector get packet from colo-compare by use +chardev socket. + +Redirector Filter --> Tap +Send the packet. + +Secondary: + +Guest --> TCP Rewriter Filter +If the packet is TCP packet,we will adjust seq +and update TCP checksum. Then send it to +redirect client filter. Otherwise directly send to +redirect client filter. + +Redirect Client Filter --> Redirect Server Filter +Forward packet to primary. + +== Components introduction == + +Filter-mirror is a netfilter plugin. +It gives qemu the ability to mirror +packets to a chardev. + +Filter-redirector is a netfilter plugin. +It gives qemu the ability to redirect net packet. +Redirector can redirect filter's net packet to outdev, +and redirect indev's packet to filter. + + filter + + + redirector | + +--------------+ + | | | + | | | + | | | + indev +---------+ +----------> outdev + | | | + | | | + | | | + +--------------+ + | + v + filter + +COLO-compare, we do packet comparing job. +Packets coming from the primary char indev will be sent to outdev. +Packets coming from the secondary char dev will be dropped after comparing. +COLO-comapre need two input chardev and one output chardev: +primary_in=chardev1-id (source: primary send packet) +secondary_in=chardev2-id (source: secondary send packet) +outdev=chardev3-id + +Filter-rewriter will rewrite some of secondary packet to make +secondary guest's tcp connection established successfully. +In this module we will rewrite tcp packet's ack to the secondary +from primary,and rewrite tcp packet's seq to the primary from +secondary. + +== Usage == + +Here, we use demo ip and port discribe more clearly. +Primary(ip:3.3.3.3): +-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown +-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 +-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait +-chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait +-chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait +-chardev socket,id=compare0-0,host=3.3.3.3,port=9001 +-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait +-chardev socket,id=compare_out0,host=3.3.3.3,port=9005 +-object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 +-object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out +-object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0 +-object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0 + +Secondary(ip:3.3.3.8): +-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown +-device e1000,netdev=hn0,mac=52:a4:00:12:78:66 +-chardev socket,id=red0,host=3.3.3.3,port=9003 +-chardev socket,id=red1,host=3.3.3.3,port=9004 +-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 +-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 + +Note: + a.COLO-proxy must work with COLO-frame and Block-replication. + b.Primary COLO must be started firstly, because COLO-proxy needs + chardev socket server running before secondary started. + c.Filter-rewriter only rewrite tcp packet. diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index 5debb3348c..d68e3dcdbd 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -436,7 +436,7 @@ static const NodeCreationPair add_fdt_node_functions[] = { * are dynamically instantiable and if so call the node creation * function. */ -static int add_fdt_node(SysBusDevice *sbdev, void *opaque) +static void add_fdt_node(SysBusDevice *sbdev, void *opaque) { int i, ret; @@ -445,7 +445,7 @@ static int add_fdt_node(SysBusDevice *sbdev, void *opaque) add_fdt_node_functions[i].typename)) { ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque); assert(!ret); - return 0; + return; } } error_report("Device %s can not be dynamically instantiated", diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c index 342868904d..5aa350a1bf 100644 --- a/hw/block/xen_disk.c +++ b/hw/block/xen_disk.c @@ -119,6 +119,9 @@ struct XenBlkDev { unsigned int persistent_gnt_count; unsigned int max_grants; + /* Grant copy */ + gboolean feature_grant_copy; + /* qemu block driver */ DriveInfo *dinfo; BlockBackend *blk; @@ -489,6 +492,106 @@ static int ioreq_map(struct ioreq *ioreq) return 0; } +#if CONFIG_XEN_CTRL_INTERFACE_VERSION >= 480 + +static void ioreq_free_copy_buffers(struct ioreq *ioreq) +{ + int i; + + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->page[i] = NULL; + } + + qemu_vfree(ioreq->pages); +} + +static int ioreq_init_copy_buffers(struct ioreq *ioreq) +{ + int i; + + if (ioreq->v.niov == 0) { + return 0; + } + + ioreq->pages = qemu_memalign(XC_PAGE_SIZE, ioreq->v.niov * XC_PAGE_SIZE); + + for (i = 0; i < ioreq->v.niov; i++) { + ioreq->page[i] = ioreq->pages + i * XC_PAGE_SIZE; + ioreq->v.iov[i].iov_base = ioreq->page[i]; + } + + return 0; +} + +static int ioreq_grant_copy(struct ioreq *ioreq) +{ + xengnttab_handle *gnt = ioreq->blkdev->xendev.gnttabdev; + xengnttab_grant_copy_segment_t segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int i, count, rc; + int64_t file_blk = ioreq->blkdev->file_blk; + + if (ioreq->v.niov == 0) { + return 0; + } + + count = ioreq->v.niov; + + for (i = 0; i < count; i++) { + if (ioreq->req.operation == BLKIF_OP_READ) { + segs[i].flags = GNTCOPY_dest_gref; + segs[i].dest.foreign.ref = ioreq->refs[i]; + segs[i].dest.foreign.domid = ioreq->domids[i]; + segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; + segs[i].source.virt = ioreq->v.iov[i].iov_base; + } else { + segs[i].flags = GNTCOPY_source_gref; + segs[i].source.foreign.ref = ioreq->refs[i]; + segs[i].source.foreign.domid = ioreq->domids[i]; + segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; + segs[i].dest.virt = ioreq->v.iov[i].iov_base; + } + segs[i].len = (ioreq->req.seg[i].last_sect + - ioreq->req.seg[i].first_sect + 1) * file_blk; + } + + rc = xengnttab_grant_copy(gnt, count, segs); + + if (rc) { + xen_be_printf(&ioreq->blkdev->xendev, 0, + "failed to copy data %d\n", rc); + ioreq->aio_errors++; + return -1; + } + + for (i = 0; i < count; i++) { + if (segs[i].status != GNTST_okay) { + xen_be_printf(&ioreq->blkdev->xendev, 3, + "failed to copy data %d for gref %d, domid %d\n", + segs[i].status, ioreq->refs[i], ioreq->domids[i]); + ioreq->aio_errors++; + rc = -1; + } + } + + return rc; +} +#else +static void ioreq_free_copy_buffers(struct ioreq *ioreq) +{ + abort(); +} + +static int ioreq_init_copy_buffers(struct ioreq *ioreq) +{ + abort(); +} + +static int ioreq_grant_copy(struct ioreq *ioreq) +{ + abort(); +} +#endif + static int ioreq_runio_qemu_aio(struct ioreq *ioreq); static void qemu_aio_complete(void *opaque, int ret) @@ -511,8 +614,31 @@ static void qemu_aio_complete(void *opaque, int ret) return; } + if (ioreq->blkdev->feature_grant_copy) { + switch (ioreq->req.operation) { + case BLKIF_OP_READ: + /* in case of failure ioreq->aio_errors is increased */ + if (ret == 0) { + ioreq_grant_copy(ioreq); + } + ioreq_free_copy_buffers(ioreq); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!ioreq->req.nr_segments) { + break; + } + ioreq_free_copy_buffers(ioreq); + break; + default: + break; + } + } + ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - ioreq_unmap(ioreq); + if (!ioreq->blkdev->feature_grant_copy) { + ioreq_unmap(ioreq); + } ioreq_finish(ioreq); switch (ioreq->req.operation) { case BLKIF_OP_WRITE: @@ -538,8 +664,18 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) { struct XenBlkDev *blkdev = ioreq->blkdev; - if (ioreq->req.nr_segments && ioreq_map(ioreq) == -1) { - goto err_no_map; + if (ioreq->blkdev->feature_grant_copy) { + ioreq_init_copy_buffers(ioreq); + if (ioreq->req.nr_segments && (ioreq->req.operation == BLKIF_OP_WRITE || + ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && + ioreq_grant_copy(ioreq)) { + ioreq_free_copy_buffers(ioreq); + goto err; + } + } else { + if (ioreq->req.nr_segments && ioreq_map(ioreq)) { + goto err; + } } ioreq->aio_inflight++; @@ -582,6 +718,9 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) } default: /* unknown operation (shouldn't happen -- parse catches this) */ + if (!ioreq->blkdev->feature_grant_copy) { + ioreq_unmap(ioreq); + } goto err; } @@ -590,8 +729,6 @@ static int ioreq_runio_qemu_aio(struct ioreq *ioreq) return 0; err: - ioreq_unmap(ioreq); -err_no_map: ioreq_finish(ioreq); ioreq->status = BLKIF_RSP_ERROR; return -1; @@ -1034,6 +1171,12 @@ static int blk_connect(struct XenDevice *xendev) xen_be_bind_evtchn(&blkdev->xendev); + blkdev->feature_grant_copy = + (xengnttab_grant_copy(blkdev->xendev.gnttabdev, 0, NULL) == 0); + + xen_be_printf(&blkdev->xendev, 3, "grant copy operation %s\n", + blkdev->feature_grant_copy ? "enabled" : "disabled"); + xen_be_printf(&blkdev->xendev, 1, "ok: proto %s, ring-ref %d, " "remote port %d, local port %d\n", blkdev->xendev.protocol, blkdev->ring_ref, diff --git a/hw/core/machine.c b/hw/core/machine.c index 00fbe3e880..afd84accbf 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -332,7 +332,7 @@ static bool machine_get_enforce_config_section(Object *obj, Error **errp) return ms->enforce_config_section; } -static int error_on_sysbus_device(SysBusDevice *sbdev, void *opaque) +static void error_on_sysbus_device(SysBusDevice *sbdev, void *opaque) { error_report("Option '-device %s' cannot be handled by this machine", object_class_get_name(object_get_class(OBJECT(sbdev)))); diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 36f84ab72f..329ac670c0 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -74,7 +74,7 @@ hwaddr platform_bus_get_mmio_addr(PlatformBusDevice *pbus, SysBusDevice *sbdev, return object_property_get_int(OBJECT(sbdev_mr), "addr", NULL); } -static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) +static void platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) { PlatformBusDevice *pbus = opaque; qemu_irq sbirq; @@ -93,8 +93,6 @@ static int platform_bus_count_irqs(SysBusDevice *sbdev, void *opaque) } } } - - return 0; } /* @@ -168,7 +166,7 @@ static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, * For each sysbus device, look for unassigned IRQ lines as well as * unassociated MMIO regions. Connect them to the platform bus if available. */ -static int link_sysbus_device(SysBusDevice *sbdev, void *opaque) +static void link_sysbus_device(SysBusDevice *sbdev, void *opaque) { PlatformBusDevice *pbus = opaque; int i; @@ -180,8 +178,6 @@ static int link_sysbus_device(SysBusDevice *sbdev, void *opaque) for (i = 0; sysbus_has_mmio(sbdev, i); i++) { platform_bus_map_mmio(pbus, sbdev, i); } - - return 0; } static void platform_bus_init_notify(Notifier *notifier, void *data) diff --git a/hw/input/ps2.c b/hw/input/ps2.c index a8aa36f5c0..0d14de08a6 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "hw/hw.h" #include "hw/input/ps2.h" #include "ui/console.h" @@ -94,12 +95,10 @@ typedef struct { typedef struct { PS2State common; int scan_enabled; - /* QEMU uses translated PC scancodes internally. To avoid multiple - conversions we do the translation (if any) in the PS/2 emulation - not the keyboard controller. */ int translate; int scancode_set; /* 1=XT, 2=AT, 3=PS/2 */ int ledstate; + bool need_high_bit; } PS2KbdState; typedef struct { @@ -116,26 +115,430 @@ typedef struct { uint8_t mouse_buttons; } PS2MouseState; -/* Table to convert from PC scancodes to raw scancodes. */ -static const unsigned char ps2_raw_keycode[128] = { - 0, 118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89, 124, 17, 41, 88, 5, 6, 4, 12, 3, - 11, 2, 10, 1, 9, 119, 126, 108, 117, 125, 123, 107, 115, 116, 121, 105, -114, 122, 112, 113, 127, 96, 97, 120, 7, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111, - 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110 +/* Table to convert from QEMU codes to scancodes. */ +static const uint16_t qcode_to_keycode_set1[Q_KEY_CODE__MAX] = { + [0 ... Q_KEY_CODE__MAX - 1] = 0, + + [Q_KEY_CODE_A] = 0x1e, + [Q_KEY_CODE_B] = 0x30, + [Q_KEY_CODE_C] = 0x2e, + [Q_KEY_CODE_D] = 0x20, + [Q_KEY_CODE_E] = 0x12, + [Q_KEY_CODE_F] = 0x21, + [Q_KEY_CODE_G] = 0x22, + [Q_KEY_CODE_H] = 0x23, + [Q_KEY_CODE_I] = 0x17, + [Q_KEY_CODE_J] = 0x24, + [Q_KEY_CODE_K] = 0x25, + [Q_KEY_CODE_L] = 0x26, + [Q_KEY_CODE_M] = 0x32, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_O] = 0x18, + [Q_KEY_CODE_P] = 0x19, + [Q_KEY_CODE_Q] = 0x10, + [Q_KEY_CODE_R] = 0x13, + [Q_KEY_CODE_S] = 0x1f, + [Q_KEY_CODE_T] = 0x14, + [Q_KEY_CODE_U] = 0x16, + [Q_KEY_CODE_V] = 0x2f, + [Q_KEY_CODE_W] = 0x11, + [Q_KEY_CODE_X] = 0x2d, + [Q_KEY_CODE_Y] = 0x15, + [Q_KEY_CODE_Z] = 0x2c, + [Q_KEY_CODE_0] = 0x0b, + [Q_KEY_CODE_1] = 0x02, + [Q_KEY_CODE_2] = 0x03, + [Q_KEY_CODE_3] = 0x04, + [Q_KEY_CODE_4] = 0x05, + [Q_KEY_CODE_5] = 0x06, + [Q_KEY_CODE_6] = 0x07, + [Q_KEY_CODE_7] = 0x08, + [Q_KEY_CODE_8] = 0x09, + [Q_KEY_CODE_9] = 0x0a, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x29, + [Q_KEY_CODE_MINUS] = 0x0c, + [Q_KEY_CODE_EQUAL] = 0x0d, + [Q_KEY_CODE_BACKSLASH] = 0x2b, + [Q_KEY_CODE_BACKSPACE] = 0x0e, + [Q_KEY_CODE_SPC] = 0x39, + [Q_KEY_CODE_TAB] = 0x0f, + [Q_KEY_CODE_CAPS_LOCK] = 0x3a, + [Q_KEY_CODE_SHIFT] = 0x2a, + [Q_KEY_CODE_CTRL] = 0x1d, + [Q_KEY_CODE_META_L] = 0xe05b, + [Q_KEY_CODE_ALT] = 0x38, + [Q_KEY_CODE_SHIFT_R] = 0x36, + [Q_KEY_CODE_CTRL_R] = 0xe01d, + [Q_KEY_CODE_META_R] = 0xe05c, + [Q_KEY_CODE_ALT_R] = 0xe038, + [Q_KEY_CODE_MENU] = 0xe05d, + [Q_KEY_CODE_RET] = 0x1c, + [Q_KEY_CODE_ESC] = 0x01, + [Q_KEY_CODE_F1] = 0x3b, + [Q_KEY_CODE_F2] = 0x3c, + [Q_KEY_CODE_F3] = 0x3d, + [Q_KEY_CODE_F4] = 0x3e, + [Q_KEY_CODE_F5] = 0x3f, + [Q_KEY_CODE_F6] = 0x40, + [Q_KEY_CODE_F7] = 0x41, + [Q_KEY_CODE_F8] = 0x42, + [Q_KEY_CODE_F9] = 0x43, + [Q_KEY_CODE_F10] = 0x44, + [Q_KEY_CODE_F11] = 0x57, + [Q_KEY_CODE_F12] = 0x58, + /* special handling for Q_KEY_CODE_PRINT */ + [Q_KEY_CODE_SCROLL_LOCK] = 0x46, + /* special handling for Q_KEY_CODE_PAUSE */ + [Q_KEY_CODE_BRACKET_LEFT] = 0x1a, + [Q_KEY_CODE_INSERT] = 0xe052, + [Q_KEY_CODE_HOME] = 0xe047, + [Q_KEY_CODE_PGUP] = 0xe049, + [Q_KEY_CODE_DELETE] = 0xe053, + [Q_KEY_CODE_END] = 0xe04f, + [Q_KEY_CODE_PGDN] = 0xe051, + [Q_KEY_CODE_UP] = 0xe048, + [Q_KEY_CODE_LEFT] = 0xe04b, + [Q_KEY_CODE_DOWN] = 0xe050, + [Q_KEY_CODE_RIGHT] = 0xe04d, + [Q_KEY_CODE_NUM_LOCK] = 0x45, + [Q_KEY_CODE_KP_DIVIDE] = 0xe035, + [Q_KEY_CODE_KP_MULTIPLY] = 0x37, + [Q_KEY_CODE_KP_SUBTRACT] = 0x4a, + [Q_KEY_CODE_KP_ADD] = 0x4e, + [Q_KEY_CODE_KP_ENTER] = 0xe01c, + [Q_KEY_CODE_KP_DECIMAL] = 0x53, + [Q_KEY_CODE_KP_0] = 0x52, + [Q_KEY_CODE_KP_1] = 0x4f, + [Q_KEY_CODE_KP_2] = 0x50, + [Q_KEY_CODE_KP_3] = 0x51, + [Q_KEY_CODE_KP_4] = 0x4b, + [Q_KEY_CODE_KP_5] = 0x4c, + [Q_KEY_CODE_KP_6] = 0x4d, + [Q_KEY_CODE_KP_7] = 0x47, + [Q_KEY_CODE_KP_8] = 0x48, + [Q_KEY_CODE_KP_9] = 0x49, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x1b, + [Q_KEY_CODE_SEMICOLON] = 0x27, + [Q_KEY_CODE_APOSTROPHE] = 0x28, + [Q_KEY_CODE_COMMA] = 0x33, + [Q_KEY_CODE_DOT] = 0x34, + [Q_KEY_CODE_SLASH] = 0x35, + +#if 0 + [Q_KEY_CODE_POWER] = 0x0e5e, + [Q_KEY_CODE_SLEEP] = 0x0e5f, + [Q_KEY_CODE_WAKE] = 0x0e63, + + [Q_KEY_CODE_AUDIONEXT] = 0xe019, + [Q_KEY_CODE_AUDIOPREV] = 0xe010, + [Q_KEY_CODE_AUDIOSTOP] = 0xe024, + [Q_KEY_CODE_AUDIOPLAY] = 0xe022, + [Q_KEY_CODE_AUDIOMUTE] = 0xe020, + [Q_KEY_CODE_VOLUMEUP] = 0xe030, + [Q_KEY_CODE_VOLUMEDOWN] = 0xe02e, + [Q_KEY_CODE_MEDIASELECT] = 0xe06d, + [Q_KEY_CODE_MAIL] = 0xe06c, + [Q_KEY_CODE_CALCULATOR] = 0xe021, + [Q_KEY_CODE_COMPUTER] = 0xe06b, + [Q_KEY_CODE_AC_SEARCH] = 0xe065, + [Q_KEY_CODE_AC_HOME] = 0xe032, + [Q_KEY_CODE_AC_BACK] = 0xe06a, + [Q_KEY_CODE_AC_FORWARD] = 0xe069, + [Q_KEY_CODE_AC_STOP] = 0xe068, + [Q_KEY_CODE_AC_REFRESH] = 0xe067, + [Q_KEY_CODE_AC_BOOKMARKS] = 0xe066, +#endif + + [Q_KEY_CODE_ASTERISK] = 0x37, + [Q_KEY_CODE_LESS] = 0x56, + [Q_KEY_CODE_RO] = 0x73, + [Q_KEY_CODE_KP_COMMA] = 0x7e, }; -static const unsigned char ps2_raw_keycode_set3[128] = { - 0, 8, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85, 102, 13, - 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 17, 28, 27, - 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 92, 26, 34, 33, 42, - 50, 49, 58, 65, 73, 74, 89, 126, 25, 41, 20, 7, 15, 23, 31, 39, - 47, 2, 63, 71, 79, 118, 95, 108, 117, 125, 132, 107, 115, 116, 124, 105, -114, 122, 112, 113, 127, 96, 97, 86, 94, 15, 23, 31, 39, 47, 55, 63, - 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87, 111, - 19, 25, 57, 81, 83, 92, 95, 98, 99, 100, 101, 103, 104, 106, 109, 110 + +static const uint16_t qcode_to_keycode_set2[Q_KEY_CODE__MAX] = { + [0 ... Q_KEY_CODE__MAX - 1] = 0, + + [Q_KEY_CODE_A] = 0x1c, + [Q_KEY_CODE_B] = 0x32, + [Q_KEY_CODE_C] = 0x21, + [Q_KEY_CODE_D] = 0x23, + [Q_KEY_CODE_E] = 0x24, + [Q_KEY_CODE_F] = 0x2b, + [Q_KEY_CODE_G] = 0x34, + [Q_KEY_CODE_H] = 0x33, + [Q_KEY_CODE_I] = 0x43, + [Q_KEY_CODE_J] = 0x3b, + [Q_KEY_CODE_K] = 0x42, + [Q_KEY_CODE_L] = 0x4b, + [Q_KEY_CODE_M] = 0x3a, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_O] = 0x44, + [Q_KEY_CODE_P] = 0x4d, + [Q_KEY_CODE_Q] = 0x15, + [Q_KEY_CODE_R] = 0x2d, + [Q_KEY_CODE_S] = 0x1b, + [Q_KEY_CODE_T] = 0x2c, + [Q_KEY_CODE_U] = 0x3c, + [Q_KEY_CODE_V] = 0x2a, + [Q_KEY_CODE_W] = 0x1d, + [Q_KEY_CODE_X] = 0x22, + [Q_KEY_CODE_Y] = 0x35, + [Q_KEY_CODE_Z] = 0x1a, + [Q_KEY_CODE_0] = 0x45, + [Q_KEY_CODE_1] = 0x16, + [Q_KEY_CODE_2] = 0x1e, + [Q_KEY_CODE_3] = 0x26, + [Q_KEY_CODE_4] = 0x25, + [Q_KEY_CODE_5] = 0x2e, + [Q_KEY_CODE_6] = 0x36, + [Q_KEY_CODE_7] = 0x3d, + [Q_KEY_CODE_8] = 0x3e, + [Q_KEY_CODE_9] = 0x46, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x0e, + [Q_KEY_CODE_MINUS] = 0x4e, + [Q_KEY_CODE_EQUAL] = 0x55, + [Q_KEY_CODE_BACKSLASH] = 0x5d, + [Q_KEY_CODE_BACKSPACE] = 0x66, + [Q_KEY_CODE_SPC] = 0x29, + [Q_KEY_CODE_TAB] = 0x0d, + [Q_KEY_CODE_CAPS_LOCK] = 0x58, + [Q_KEY_CODE_SHIFT] = 0x12, + [Q_KEY_CODE_CTRL] = 0x14, + [Q_KEY_CODE_META_L] = 0xe01f, + [Q_KEY_CODE_ALT] = 0x11, + [Q_KEY_CODE_SHIFT_R] = 0x59, + [Q_KEY_CODE_CTRL_R] = 0xe014, + [Q_KEY_CODE_META_R] = 0xe027, + [Q_KEY_CODE_ALT_R] = 0xe011, + [Q_KEY_CODE_MENU] = 0xe02f, + [Q_KEY_CODE_RET] = 0x5a, + [Q_KEY_CODE_ESC] = 0x76, + [Q_KEY_CODE_F1] = 0x05, + [Q_KEY_CODE_F2] = 0x06, + [Q_KEY_CODE_F3] = 0x04, + [Q_KEY_CODE_F4] = 0x0c, + [Q_KEY_CODE_F5] = 0x03, + [Q_KEY_CODE_F6] = 0x0b, + [Q_KEY_CODE_F7] = 0x83, + [Q_KEY_CODE_F8] = 0x0a, + [Q_KEY_CODE_F9] = 0x01, + [Q_KEY_CODE_F10] = 0x09, + [Q_KEY_CODE_F11] = 0x78, + [Q_KEY_CODE_F12] = 0x07, + /* special handling for Q_KEY_CODE_PRINT */ + [Q_KEY_CODE_SCROLL_LOCK] = 0x7e, + /* special handling for Q_KEY_CODE_PAUSE */ + [Q_KEY_CODE_BRACKET_LEFT] = 0x54, + [Q_KEY_CODE_INSERT] = 0xe070, + [Q_KEY_CODE_HOME] = 0xe06c, + [Q_KEY_CODE_PGUP] = 0xe07d, + [Q_KEY_CODE_DELETE] = 0xe071, + [Q_KEY_CODE_END] = 0xe069, + [Q_KEY_CODE_PGDN] = 0xe07a, + [Q_KEY_CODE_UP] = 0xe075, + [Q_KEY_CODE_LEFT] = 0xe06b, + [Q_KEY_CODE_DOWN] = 0xe072, + [Q_KEY_CODE_RIGHT] = 0xe074, + [Q_KEY_CODE_NUM_LOCK] = 0x77, + [Q_KEY_CODE_KP_DIVIDE] = 0xe04a, + [Q_KEY_CODE_KP_MULTIPLY] = 0x7c, + [Q_KEY_CODE_KP_SUBTRACT] = 0x7b, + [Q_KEY_CODE_KP_ADD] = 0x79, + [Q_KEY_CODE_KP_ENTER] = 0xe05a, + [Q_KEY_CODE_KP_DECIMAL] = 0x71, + [Q_KEY_CODE_KP_0] = 0x70, + [Q_KEY_CODE_KP_1] = 0x69, + [Q_KEY_CODE_KP_2] = 0x72, + [Q_KEY_CODE_KP_3] = 0x7a, + [Q_KEY_CODE_KP_4] = 0x6b, + [Q_KEY_CODE_KP_5] = 0x73, + [Q_KEY_CODE_KP_6] = 0x74, + [Q_KEY_CODE_KP_7] = 0x6c, + [Q_KEY_CODE_KP_8] = 0x75, + [Q_KEY_CODE_KP_9] = 0x7d, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x5b, + [Q_KEY_CODE_SEMICOLON] = 0x4c, + [Q_KEY_CODE_APOSTROPHE] = 0x52, + [Q_KEY_CODE_COMMA] = 0x41, + [Q_KEY_CODE_DOT] = 0x49, + [Q_KEY_CODE_SLASH] = 0x4a, + +#if 0 + [Q_KEY_CODE_POWER] = 0x0e37, + [Q_KEY_CODE_SLEEP] = 0x0e3f, + [Q_KEY_CODE_WAKE] = 0x0e5e, + + [Q_KEY_CODE_AUDIONEXT] = 0xe04d, + [Q_KEY_CODE_AUDIOPREV] = 0xe015, + [Q_KEY_CODE_AUDIOSTOP] = 0xe03b, + [Q_KEY_CODE_AUDIOPLAY] = 0xe034, + [Q_KEY_CODE_AUDIOMUTE] = 0xe023, + [Q_KEY_CODE_VOLUMEUP] = 0xe032, + [Q_KEY_CODE_VOLUMEDOWN] = 0xe021, + [Q_KEY_CODE_MEDIASELECT] = 0xe050, + [Q_KEY_CODE_MAIL] = 0xe048, + [Q_KEY_CODE_CALCULATOR] = 0xe02b, + [Q_KEY_CODE_COMPUTER] = 0xe040, + [Q_KEY_CODE_AC_SEARCH] = 0xe010, + [Q_KEY_CODE_AC_HOME] = 0xe03a, + [Q_KEY_CODE_AC_BACK] = 0xe038, + [Q_KEY_CODE_AC_FORWARD] = 0xe030, + [Q_KEY_CODE_AC_STOP] = 0xe028, + [Q_KEY_CODE_AC_REFRESH] = 0xe020, + [Q_KEY_CODE_AC_BOOKMARKS] = 0xe018, +#endif + + [Q_KEY_CODE_ALTGR] = 0x08, + [Q_KEY_CODE_ALTGR_R] = 0xe008, + [Q_KEY_CODE_ASTERISK] = 0x7c, + [Q_KEY_CODE_LESS] = 0x61, + [Q_KEY_CODE_SYSRQ] = 0x7f, + [Q_KEY_CODE_RO] = 0x51, + [Q_KEY_CODE_KP_COMMA] = 0x6d, +}; + +static const uint16_t qcode_to_keycode_set3[Q_KEY_CODE__MAX] = { + [0 ... Q_KEY_CODE__MAX - 1] = 0, + + [Q_KEY_CODE_A] = 0x1c, + [Q_KEY_CODE_B] = 0x32, + [Q_KEY_CODE_C] = 0x21, + [Q_KEY_CODE_D] = 0x23, + [Q_KEY_CODE_E] = 0x24, + [Q_KEY_CODE_F] = 0x2b, + [Q_KEY_CODE_G] = 0x34, + [Q_KEY_CODE_H] = 0x33, + [Q_KEY_CODE_I] = 0x43, + [Q_KEY_CODE_J] = 0x3b, + [Q_KEY_CODE_K] = 0x42, + [Q_KEY_CODE_L] = 0x4b, + [Q_KEY_CODE_M] = 0x3a, + [Q_KEY_CODE_N] = 0x31, + [Q_KEY_CODE_O] = 0x44, + [Q_KEY_CODE_P] = 0x4d, + [Q_KEY_CODE_Q] = 0x15, + [Q_KEY_CODE_R] = 0x2d, + [Q_KEY_CODE_S] = 0x1b, + [Q_KEY_CODE_T] = 0x2c, + [Q_KEY_CODE_U] = 0x3c, + [Q_KEY_CODE_V] = 0x2a, + [Q_KEY_CODE_W] = 0x1d, + [Q_KEY_CODE_X] = 0x22, + [Q_KEY_CODE_Y] = 0x35, + [Q_KEY_CODE_Z] = 0x1a, + [Q_KEY_CODE_0] = 0x45, + [Q_KEY_CODE_1] = 0x16, + [Q_KEY_CODE_2] = 0x1e, + [Q_KEY_CODE_3] = 0x26, + [Q_KEY_CODE_4] = 0x25, + [Q_KEY_CODE_5] = 0x2e, + [Q_KEY_CODE_6] = 0x36, + [Q_KEY_CODE_7] = 0x3d, + [Q_KEY_CODE_8] = 0x3e, + [Q_KEY_CODE_9] = 0x46, + [Q_KEY_CODE_GRAVE_ACCENT] = 0x0e, + [Q_KEY_CODE_MINUS] = 0x4e, + [Q_KEY_CODE_EQUAL] = 0x55, + [Q_KEY_CODE_BACKSLASH] = 0x5c, + [Q_KEY_CODE_BACKSPACE] = 0x66, + [Q_KEY_CODE_SPC] = 0x29, + [Q_KEY_CODE_TAB] = 0x0d, + [Q_KEY_CODE_CAPS_LOCK] = 0x14, + [Q_KEY_CODE_SHIFT] = 0x12, + [Q_KEY_CODE_CTRL] = 0x11, + [Q_KEY_CODE_META_L] = 0x8b, + [Q_KEY_CODE_ALT] = 0x19, + [Q_KEY_CODE_SHIFT_R] = 0x59, + [Q_KEY_CODE_CTRL_R] = 0x58, + [Q_KEY_CODE_META_R] = 0x8c, + [Q_KEY_CODE_ALT_R] = 0x39, + [Q_KEY_CODE_MENU] = 0x8d, + [Q_KEY_CODE_RET] = 0x5a, + [Q_KEY_CODE_ESC] = 0x08, + [Q_KEY_CODE_F1] = 0x07, + [Q_KEY_CODE_F2] = 0x0f, + [Q_KEY_CODE_F3] = 0x17, + [Q_KEY_CODE_F4] = 0x1f, + [Q_KEY_CODE_F5] = 0x27, + [Q_KEY_CODE_F6] = 0x2f, + [Q_KEY_CODE_F7] = 0x37, + [Q_KEY_CODE_F8] = 0x3f, + [Q_KEY_CODE_F9] = 0x47, + [Q_KEY_CODE_F10] = 0x4f, + [Q_KEY_CODE_F11] = 0x56, + [Q_KEY_CODE_F12] = 0x5e, + [Q_KEY_CODE_PRINT] = 0x57, + [Q_KEY_CODE_SCROLL_LOCK] = 0x5f, + [Q_KEY_CODE_PAUSE] = 0x62, + [Q_KEY_CODE_BRACKET_LEFT] = 0x54, + [Q_KEY_CODE_INSERT] = 0x67, + [Q_KEY_CODE_HOME] = 0x6e, + [Q_KEY_CODE_PGUP] = 0x6f, + [Q_KEY_CODE_DELETE] = 0x64, + [Q_KEY_CODE_END] = 0x65, + [Q_KEY_CODE_PGDN] = 0x6d, + [Q_KEY_CODE_UP] = 0x63, + [Q_KEY_CODE_LEFT] = 0x61, + [Q_KEY_CODE_DOWN] = 0x60, + [Q_KEY_CODE_RIGHT] = 0x6a, + [Q_KEY_CODE_NUM_LOCK] = 0x76, + [Q_KEY_CODE_KP_DIVIDE] = 0x4a, + [Q_KEY_CODE_KP_MULTIPLY] = 0x7e, + [Q_KEY_CODE_KP_SUBTRACT] = 0x4e, + [Q_KEY_CODE_KP_ADD] = 0x7c, + [Q_KEY_CODE_KP_ENTER] = 0x79, + [Q_KEY_CODE_KP_DECIMAL] = 0x71, + [Q_KEY_CODE_KP_0] = 0x70, + [Q_KEY_CODE_KP_1] = 0x69, + [Q_KEY_CODE_KP_2] = 0x72, + [Q_KEY_CODE_KP_3] = 0x7a, + [Q_KEY_CODE_KP_4] = 0x6b, + [Q_KEY_CODE_KP_5] = 0x73, + [Q_KEY_CODE_KP_6] = 0x74, + [Q_KEY_CODE_KP_7] = 0x6c, + [Q_KEY_CODE_KP_8] = 0x75, + [Q_KEY_CODE_KP_9] = 0x7d, + [Q_KEY_CODE_BRACKET_RIGHT] = 0x5b, + [Q_KEY_CODE_SEMICOLON] = 0x4c, + [Q_KEY_CODE_APOSTROPHE] = 0x52, + [Q_KEY_CODE_COMMA] = 0x41, + [Q_KEY_CODE_DOT] = 0x49, + [Q_KEY_CODE_SLASH] = 0x4a, +}; + +static uint8_t translate_table[256] = { + 0xff, 0x43, 0x41, 0x3f, 0x3d, 0x3b, 0x3c, 0x58, + 0x64, 0x44, 0x42, 0x40, 0x3e, 0x0f, 0x29, 0x59, + 0x65, 0x38, 0x2a, 0x70, 0x1d, 0x10, 0x02, 0x5a, + 0x66, 0x71, 0x2c, 0x1f, 0x1e, 0x11, 0x03, 0x5b, + 0x67, 0x2e, 0x2d, 0x20, 0x12, 0x05, 0x04, 0x5c, + 0x68, 0x39, 0x2f, 0x21, 0x14, 0x13, 0x06, 0x5d, + 0x69, 0x31, 0x30, 0x23, 0x22, 0x15, 0x07, 0x5e, + 0x6a, 0x72, 0x32, 0x24, 0x16, 0x08, 0x09, 0x5f, + 0x6b, 0x33, 0x25, 0x17, 0x18, 0x0b, 0x0a, 0x60, + 0x6c, 0x34, 0x35, 0x26, 0x27, 0x19, 0x0c, 0x61, + 0x6d, 0x73, 0x28, 0x74, 0x1a, 0x0d, 0x62, 0x6e, + 0x3a, 0x36, 0x1c, 0x1b, 0x75, 0x2b, 0x63, 0x76, + 0x55, 0x56, 0x77, 0x78, 0x79, 0x7a, 0x0e, 0x7b, + 0x7c, 0x4f, 0x7d, 0x4b, 0x47, 0x7e, 0x7f, 0x6f, + 0x52, 0x53, 0x50, 0x4c, 0x4d, 0x48, 0x01, 0x45, + 0x57, 0x4e, 0x51, 0x4a, 0x37, 0x49, 0x46, 0x54, + 0x80, 0x81, 0x82, 0x41, 0x54, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, }; void ps2_queue(void *opaque, int b) @@ -152,44 +555,130 @@ void ps2_queue(void *opaque, int b) s->update_irq(s->update_arg, 1); } -/* - keycode is expressed as follow: - bit 7 - 0 key pressed, 1 = key released - bits 6-0 - translated scancode set 2 - */ +/* keycode is the untranslated scancode in the current scancode set. */ static void ps2_put_keycode(void *opaque, int keycode) { PS2KbdState *s = opaque; trace_ps2_put_keycode(opaque, keycode); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - /* XXX: add support for scancode set 1 */ - if (!s->translate && keycode < 0xe0 && s->scancode_set > 1) { - if (keycode & 0x80) { - ps2_queue(&s->common, 0xf0); - } - if (s->scancode_set == 2) { - keycode = ps2_raw_keycode[keycode & 0x7f]; - } else if (s->scancode_set == 3) { - keycode = ps2_raw_keycode_set3[keycode & 0x7f]; + + if (s->translate) { + if (keycode == 0xf0) { + s->need_high_bit = true; + } else if (s->need_high_bit) { + ps2_queue(&s->common, translate_table[keycode] | 0x80); + s->need_high_bit = false; + } else { + ps2_queue(&s->common, translate_table[keycode]); } - } - ps2_queue(&s->common, keycode); + } else { + ps2_queue(&s->common, keycode); + } } static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, InputEvent *evt) { PS2KbdState *s = (PS2KbdState *)dev; - int scancodes[3], i, count; InputKeyEvent *key = evt->u.key.data; + int qcode; + uint16_t keycode; qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); - count = qemu_input_key_value_to_scancode(key->key, - key->down, - scancodes); - for (i = 0; i < count; i++) { - ps2_put_keycode(s, scancodes[i]); + assert(evt->type == INPUT_EVENT_KIND_KEY); + qcode = qemu_input_key_value_to_qcode(key->key); + + if (s->scancode_set == 1) { + if (qcode == Q_KEY_CODE_PAUSE) { + if (key->down) { + ps2_put_keycode(s, 0xe1); + ps2_put_keycode(s, 0x1d); + ps2_put_keycode(s, 0x45); + ps2_put_keycode(s, 0x91); + ps2_put_keycode(s, 0x9d); + ps2_put_keycode(s, 0xc5); + } + } else if (qcode == Q_KEY_CODE_PRINT) { + if (key->down) { + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0x2a); + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0x37); + } else { + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0xb7); + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0xaa); + } + } else { + keycode = qcode_to_keycode_set1[qcode]; + if (keycode) { + if (keycode & 0xff00) { + ps2_put_keycode(s, keycode >> 8); + } + if (!key->down) { + keycode |= 0x80; + } + ps2_put_keycode(s, keycode & 0xff); + } else { + qemu_log_mask(LOG_UNIMP, + "ps2: ignoring key with qcode %d\n", qcode); + } + } + } else if (s->scancode_set == 2) { + if (qcode == Q_KEY_CODE_PAUSE) { + if (key->down) { + ps2_put_keycode(s, 0xe1); + ps2_put_keycode(s, 0x14); + ps2_put_keycode(s, 0x77); + ps2_put_keycode(s, 0xe1); + ps2_put_keycode(s, 0xf0); + ps2_put_keycode(s, 0x14); + ps2_put_keycode(s, 0xf0); + ps2_put_keycode(s, 0x77); + } + } else if (qcode == Q_KEY_CODE_PRINT) { + if (key->down) { + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0x12); + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0x7c); + } else { + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0xf0); + ps2_put_keycode(s, 0x7c); + ps2_put_keycode(s, 0xe0); + ps2_put_keycode(s, 0xf0); + ps2_put_keycode(s, 0x12); + } + } else { + keycode = qcode_to_keycode_set2[qcode]; + if (keycode) { + if (keycode & 0xff00) { + ps2_put_keycode(s, keycode >> 8); + } + if (!key->down) { + ps2_put_keycode(s, 0xf0); + } + ps2_put_keycode(s, keycode & 0xff); + } else { + qemu_log_mask(LOG_UNIMP, + "ps2: ignoring key with qcode %d\n", qcode); + } + } + } else if (s->scancode_set == 3) { + keycode = qcode_to_keycode_set3[qcode]; + if (keycode) { + /* FIXME: break code should be configured on a key by key basis */ + if (!key->down) { + ps2_put_keycode(s, 0xf0); + } + ps2_put_keycode(s, keycode); + } else { + qemu_log_mask(LOG_UNIMP, + "ps2: ignoring key with qcode %d\n", qcode); + } } } @@ -290,22 +779,19 @@ void ps2_write_keyboard(void *opaque, int val) ps2_queue(&s->common, KBD_REPLY_POR); break; default: - ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_queue(&s->common, KBD_REPLY_RESEND); break; } break; case KBD_CMD_SCANCODE: if (val == 0) { - if (s->scancode_set == 1) - ps2_put_keycode(s, 0x43); - else if (s->scancode_set == 2) - ps2_put_keycode(s, 0x41); - else if (s->scancode_set == 3) - ps2_put_keycode(s, 0x3f); - } else { - if (val >= 1 && val <= 3) - s->scancode_set = val; ps2_queue(&s->common, KBD_REPLY_ACK); + ps2_put_keycode(s, s->scancode_set); + } else if (val >= 1 && val <= 3) { + s->scancode_set = val; + ps2_queue(&s->common, KBD_REPLY_ACK); + } else { + ps2_queue(&s->common, KBD_REPLY_RESEND); } s->common.write_cmd = -1; break; @@ -690,6 +1176,23 @@ static const VMStateDescription vmstate_ps2_keyboard_ledstate = { } }; +static bool ps2_keyboard_need_high_bit_needed(void *opaque) +{ + PS2KbdState *s = opaque; + return s->need_high_bit != 0; /* 0 is the usual state */ +} + +static const VMStateDescription vmstate_ps2_keyboard_need_high_bit = { + .name = "ps2kbd/need_high_bit", + .version_id = 1, + .minimum_version_id = 1, + .needed = ps2_keyboard_need_high_bit_needed, + .fields = (VMStateField[]) { + VMSTATE_BOOL(need_high_bit, PS2KbdState), + VMSTATE_END_OF_LIST() + } +}; + static int ps2_kbd_post_load(void* opaque, int version_id) { PS2KbdState *s = (PS2KbdState*)opaque; @@ -726,6 +1229,7 @@ static const VMStateDescription vmstate_ps2_keyboard = { }, .subsections = (const VMStateDescription*[]) { &vmstate_ps2_keyboard_ledstate, + &vmstate_ps2_keyboard_need_high_bit, NULL } }; diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index bad43f474e..4994e1ca00 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -400,7 +400,7 @@ static void e1000e_write_config(PCIDevice *pci_dev, uint32_t address, if (range_covers_byte(address, len, PCI_COMMAND) && (pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); + e1000e_start_recv(&s->core); } } diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index e0bd31c577..6505983c12 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -953,7 +953,7 @@ e1000e_has_rxbufs(E1000ECore *core, const E1000E_RingInfo *r, core->rx_desc_buf_size; } -static inline void +void e1000e_start_recv(E1000ECore *core) { int i; @@ -1710,7 +1710,8 @@ e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt) } /* Perform ACK receive detection */ - if (e1000e_is_tcp_ack(core, core->rx_pkt)) { + if (!(core->mac[RFCTL] & E1000_RFCTL_ACK_DIS) && + (e1000e_is_tcp_ack(core, core->rx_pkt))) { n |= E1000_ICS_ACK; } @@ -1807,6 +1808,7 @@ e1000e_core_set_link_status(E1000ECore *core) core->autoneg_timer); } else { e1000x_update_regs_on_link_up(core->mac, core->phy[0]); + e1000e_start_recv(core); } } @@ -2007,19 +2009,23 @@ e1000e_msix_notify_one(E1000ECore *core, uint32_t cause, uint32_t int_cfg) } if (core->mac[CTRL_EXT] & E1000_CTRL_EXT_EIAME) { - trace_e1000e_irq_ims_clear_eiame(core->mac[IAM], cause); - e1000e_clear_ims_bits(core, core->mac[IAM] & cause); + trace_e1000e_irq_iam_clear_eiame(core->mac[IAM], cause); + core->mac[IAM] &= ~cause; } trace_e1000e_irq_icr_clear_eiac(core->mac[ICR], core->mac[EIAC]); - if (core->mac[EIAC] & E1000_ICR_OTHER) { - effective_eiac = (core->mac[EIAC] & E1000_EIAC_MASK) | - E1000_ICR_OTHER_CAUSES; - } else { - effective_eiac = core->mac[EIAC] & E1000_EIAC_MASK; + effective_eiac = core->mac[EIAC] & cause; + + if (effective_eiac == E1000_ICR_OTHER) { + effective_eiac |= E1000_ICR_OTHER_CAUSES; } + core->mac[ICR] &= ~effective_eiac; + + if (!(core->mac[CTRL_EXT] & E1000_CTRL_EXT_IAME)) { + core->mac[IMS] &= ~effective_eiac; + } } static void @@ -2130,7 +2136,7 @@ e1000e_update_interrupt_state(E1000ECore *core) /* Set ICR[OTHER] for MSI-X */ if (is_msix) { - if (core->mac[ICR] & core->mac[IMS] & E1000_ICR_OTHER_CAUSES) { + if (core->mac[ICR] & E1000_ICR_OTHER_CAUSES) { core->mac[ICR] |= E1000_ICR_OTHER; trace_e1000e_irq_add_msi_other(core->mac[ICR]); } @@ -2168,7 +2174,7 @@ e1000e_update_interrupt_state(E1000ECore *core) } } -static inline void +static void e1000e_set_interrupt_cause(E1000ECore *core, uint32_t val) { trace_e1000e_irq_set_cause_entry(val, core->mac[ICR]); @@ -2187,6 +2193,8 @@ e1000e_autoneg_timer(void *opaque) E1000ECore *core = opaque; if (!qemu_get_queue(core->owner_nic)->link_down) { e1000x_update_regs_on_autoneg_done(core->mac, core->phy[0]); + e1000e_start_recv(core); + e1000e_update_flowctl_status(core); /* signal link status change to the guest */ e1000e_set_interrupt_cause(core, E1000_ICR_LSC); @@ -2344,7 +2352,7 @@ e1000e_set_pbaclr(E1000ECore *core, int index, uint32_t val) core->mac[PBACLR] = val & E1000_PBACLR_VALID_MASK; - if (msix_enabled(core->owner)) { + if (!msix_enabled(core->owner)) { return; } diff --git a/hw/net/e1000e_core.h b/hw/net/e1000e_core.h index 5f413a9e08..1ff6978ca1 100644 --- a/hw/net/e1000e_core.h +++ b/hw/net/e1000e_core.h @@ -144,3 +144,6 @@ e1000e_receive(E1000ECore *core, const uint8_t *buf, size_t size); ssize_t e1000e_receive_iov(E1000ECore *core, const struct iovec *iov, int iovcnt); + +void +e1000e_start_recv(E1000ECore *core); diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 1c415ab3b1..50c75642c6 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -429,7 +429,7 @@ static void imx_fec_do_tx(IMXFECState *s) frame_size += len; if (bd.flags & ENET_BD_L) { /* Last buffer in frame. */ - qemu_send_packet(qemu_get_queue(s->nic), frame, len); + qemu_send_packet(qemu_get_queue(s->nic), frame, frame_size); ptr = frame; frame_size = 0; s->regs[ENET_EIR] |= ENET_INT_TXF; diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index 0ee8ad9d66..dc61bac2fc 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -23,6 +23,7 @@ do { printf("mcf_fec: " fmt , ## __VA_ARGS__); } while (0) #define DPRINTF(fmt, ...) do {} while(0) #endif +#define FEC_MAX_DESC 1024 #define FEC_MAX_FRAME_SIZE 2032 typedef struct { @@ -149,7 +150,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s) uint32_t addr; mcf_fec_bd bd; int frame_size; - int len; + int len, descnt = 0; uint8_t frame[FEC_MAX_FRAME_SIZE]; uint8_t *ptr; @@ -157,7 +158,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s) ptr = frame; frame_size = 0; addr = s->tx_descriptor; - while (1) { + while (descnt++ < FEC_MAX_DESC) { mcf_fec_read_bd(&bd, addr); DPRINTF("tx_bd %x flags %04x len %d data %08x\n", addr, bd.flags, bd.length, bd.data); @@ -176,7 +177,7 @@ static void mcf_fec_do_tx(mcf_fec_state *s) if (bd.flags & FEC_BD_L) { /* Last buffer in frame. */ DPRINTF("Sending packet\n"); - qemu_send_packet(qemu_get_queue(s->nic), frame, len); + qemu_send_packet(qemu_get_queue(s->nic), frame, frame_size); ptr = frame; frame_size = 0; s->eir |= FEC_INT_TXF; diff --git a/hw/net/trace-events b/hw/net/trace-events index 47ab14ac71..1a5c909939 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -223,7 +223,7 @@ e1000e_irq_icr_read_entry(uint32_t icr) "Starting ICR read. Current ICR: 0x%x" e1000e_irq_icr_read_exit(uint32_t icr) "Ending ICR read. Current ICR: 0x%x" e1000e_irq_icr_clear_zero_ims(void) "Clearing ICR on read due to zero IMS" e1000e_irq_icr_clear_iame(void) "Clearing ICR on read due to IAME" -e1000e_irq_ims_clear_eiame(uint32_t iam, uint32_t cause) "Clearing IMS due to EIAME, IAM: 0x%X, cause: 0x%X" +e1000e_irq_iam_clear_eiame(uint32_t iam, uint32_t cause) "Clearing IMS due to EIAME, IAM: 0x%X, cause: 0x%X" e1000e_irq_icr_clear_eiac(uint32_t icr, uint32_t eiac) "Clearing ICR bits due to EIAC, ICR: 0x%X, EIAC: 0x%X" e1000e_irq_ims_clear_set_imc(uint32_t val) "Clearing IMS bits due to IMC write 0x%x" e1000e_irq_fire_delayed_interrupts(void) "Firing delayed interrupts" diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 01f1351554..6b8ae2c1fa 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -31,6 +31,11 @@ #define MAC_TABLE_ENTRIES 64 #define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ +/* previously fixed value */ +#define VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE 256 +/* for now, only allow larger queues; with virtio-1, guest can downsize */ +#define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE + /* * Calculate the number of bytes up to and including the given 'field' of * 'container'. @@ -1412,7 +1417,8 @@ static void virtio_net_add_queue(VirtIONet *n, int index) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - n->vqs[index].rx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_rx); + n->vqs[index].rx_vq = virtio_add_queue(vdev, n->net_conf.rx_queue_size, + virtio_net_handle_rx); if (n->net_conf.tx && !strcmp(n->net_conf.tx, "timer")) { n->vqs[index].tx_vq = virtio_add_queue(vdev, 256, virtio_net_handle_tx_timer); @@ -1720,6 +1726,22 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) virtio_net_set_config_size(n, n->host_features); virtio_init(vdev, "virtio-net", VIRTIO_ID_NET, n->config_size); + /* + * We set a lower limit on RX queue size to what it always was. + * Guests that want a smaller ring can always resize it without + * help from us (using virtio 1 and up). + */ + if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE || + n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE || + (n->net_conf.rx_queue_size & (n->net_conf.rx_queue_size - 1))) { + error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), " + "must be a power of 2 between %d and %d.", + n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE, + VIRTQUEUE_MAX_SIZE); + virtio_cleanup(vdev); + return; + } + n->max_queues = MAX(n->nic_conf.peers.queues, 1); if (n->max_queues * 2 + 1 > VIRTIO_QUEUE_MAX) { error_setg(errp, "Invalid number of queues (= %" PRIu32 "), " @@ -1880,6 +1902,8 @@ static Property virtio_net_properties[] = { TX_TIMER_INTERVAL), DEFINE_PROP_INT32("x-txburst", VirtIONet, net_conf.txburst, TX_BURST), DEFINE_PROP_STRING("tx", VirtIONet, net_conf.tx), + DEFINE_PROP_UINT16("rx_queue_size", VirtIONet, net_conf.rx_queue_size, + VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 0cd534df55..cf8b122afe 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -196,7 +196,7 @@ static int create_devtree_etsec(SysBusDevice *sbdev, PlatformDevtreeData *data) return 0; } -static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) +static void sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) { PlatformDevtreeData *data = opaque; bool matched = false; @@ -211,8 +211,6 @@ static int sysbus_device_create_devtree(SysBusDevice *sbdev, void *opaque) qdev_fw_name(DEVICE(sbdev))); exit(1); } - - return 0; } static void platform_bus_create_devtree(PPCE500Params *params, void *fdt, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index aa067aefdb..14b6821a94 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -1110,7 +1110,7 @@ static void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, } } -static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) +static void find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) { bool matched = false; @@ -1123,8 +1123,6 @@ static int find_unknown_sysbus_device(SysBusDevice *sbdev, void *opaque) qdev_fw_name(DEVICE(sbdev))); exit(1); } - - return 0; } static void ppc_spapr_reset(void) diff --git a/include/block/block_int.h b/include/block/block_int.h index ef3c047cb3..3e79228eb0 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -722,7 +722,7 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, * @errp: Error object. * * Start a mirroring operation on @bs. Clusters that are allocated - * in @bs will be written to @bs until the job is cancelled or + * in @bs will be written to @target until the job is cancelled or * manually completed. At the end of a successful mirroring job, * @bs will be switched to read from @target. */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 29a6c9b60b..47bdf10cfd 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -374,6 +374,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); .driver = TYPE_X86_CPU,\ .property = "l3-cache",\ .value = "off",\ + },\ + {\ + .driver = TYPE_X86_CPU,\ + .property = "full-cpuid-auto-level",\ + .value = "off",\ }, #define PC_COMPAT_2_6 \ diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index e73a5b21ac..e88bb6dae0 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -75,7 +75,7 @@ struct SysBusDevice { uint32_t pio[QDEV_MAX_PIO]; }; -typedef int FindSysbusDeviceFunc(SysBusDevice *sbdev, void *opaque); +typedef void FindSysbusDeviceFunc(SysBusDevice *sbdev, void *opaque); void sysbus_init_mmio(SysBusDevice *dev, MemoryRegion *memory); MemoryRegion *sysbus_mmio_get_region(SysBusDevice *dev, int n); diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 91ed97cfcd..0ced975c57 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -35,6 +35,7 @@ typedef struct virtio_net_conf uint32_t txtimer; int32_t txburst; char *tx; + uint16_t rx_queue_size; } virtio_net_conf; /* Maximum packet size we can receive from tap device: header + 64k */ diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index bd39287b8f..8e1580d526 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -424,4 +424,18 @@ static inline int xen_domain_create(xc_interface *xc, uint32_t ssidref, #endif #endif +/* Xen before 4.8 */ + +#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 480 + + +typedef void *xengnttab_grant_copy_segment_t; + +static inline int xengnttab_grant_copy(xengnttab_handle *xgt, uint32_t count, + xengnttab_grant_copy_segment_t *segs) +{ + return -ENOSYS; +} +#endif + #endif /* QEMU_HW_XEN_COMMON_H */ diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index 29a20782f0..e6a60d55fd 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -92,6 +92,19 @@ Coroutine *coroutine_fn qemu_coroutine_self(void); */ bool qemu_in_coroutine(void); +/** + * Return true if the coroutine is currently entered + * + * A coroutine is "entered" if it has not yielded from the current + * qemu_coroutine_enter() call used to run it. This does not mean that the + * coroutine is currently executing code since it may have transferred control + * to another coroutine using qemu_coroutine_enter(). + * + * When several coroutines enter each other there may be no way to know which + * ones have already been entered. In such situations this function can be + * used to avoid recursively entering coroutines. + */ +bool qemu_coroutine_entered(Coroutine *co); /** diff --git a/include/qemu/jhash.h b/include/qemu/jhash.h new file mode 100644 index 0000000000..7222242615 --- /dev/null +++ b/include/qemu/jhash.h @@ -0,0 +1,59 @@ +/* jhash.h: Jenkins hash support. + * + * Copyright (C) 2006. Bob Jenkins (bob_jenkins@burtleburtle.net) + * + * http://burtleburtle.net/bob/hash/ + * + * These are the credits from Bob's sources: + * + * lookup3.c, by Bob Jenkins, May 2006, Public Domain. + * + * These are functions for producing 32-bit hashes for hash table lookup. + * hashword(), hashlittle(), hashlittle2(), hashbig(), mix(), and final() + * are externally useful functions. Routines to test the hash are included + * if SELF_TEST is defined. You can use this free for any purpose. It's in + * the public domain. It has no warranty. + * + * Copyright (C) 2009-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu) + * + * I've modified Bob's hash to be useful in the Linux kernel, and + * any bugs present are my fault. + * Jozsef + */ + +#ifndef QEMU_JHASH_H__ +#define QEMU_JHASH_H__ + +#include "qemu/bitops.h" + +/* + * hashtable relation copy from linux kernel jhash + */ + +/* __jhash_mix -- mix 3 32-bit values reversibly. */ +#define __jhash_mix(a, b, c) \ +{ \ + a -= c; a ^= rol32(c, 4); c += b; \ + b -= a; b ^= rol32(a, 6); a += c; \ + c -= b; c ^= rol32(b, 8); b += a; \ + a -= c; a ^= rol32(c, 16); c += b; \ + b -= a; b ^= rol32(a, 19); a += c; \ + c -= b; c ^= rol32(b, 4); b += a; \ +} + +/* __jhash_final - final mixing of 3 32-bit values (a,b,c) into c */ +#define __jhash_final(a, b, c) \ +{ \ + c ^= b; c -= rol32(b, 14); \ + a ^= c; a -= rol32(c, 11); \ + b ^= a; b -= rol32(a, 25); \ + c ^= b; c -= rol32(b, 16); \ + a ^= c; a -= rol32(c, 4); \ + b ^= a; b -= rol32(a, 14); \ + c ^= b; c -= rol32(b, 24); \ +} + +/* An arbitrary initial parameter */ +#define JHASH_INITVAL 0xdeadbeef + +#endif /* QEMU_JHASH_H__ */ diff --git a/include/sysemu/char.h b/include/sysemu/char.h index ee7e55468f..0d0465ae0e 100644 --- a/include/sysemu/char.h +++ b/include/sysemu/char.h @@ -65,7 +65,8 @@ struct CharDriverState { int (*chr_sync_read)(struct CharDriverState *s, const uint8_t *buf, int len); GSource *(*chr_add_watch)(struct CharDriverState *s, GIOCondition cond); - void (*chr_update_read_handler)(struct CharDriverState *s); + void (*chr_update_read_handler)(struct CharDriverState *s, + GMainContext *context); int (*chr_ioctl)(struct CharDriverState *s, int cmd, void *arg); int (*get_msgfds)(struct CharDriverState *s, int* fds, int num); int (*set_msgfds)(struct CharDriverState *s, int *fds, int num); @@ -422,6 +423,14 @@ void qemu_chr_add_handlers(CharDriverState *s, IOEventHandler *fd_event, void *opaque); +/* This API can make handler run in the context what you pass to. */ +void qemu_chr_add_handlers_full(CharDriverState *s, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque, + GMainContext *context); + void qemu_chr_be_generic_open(CharDriverState *s); void qemu_chr_accept_input(CharDriverState *s); int qemu_chr_add_client(CharDriverState *s, int fd); diff --git a/include/sysemu/cpus.h b/include/sysemu/cpus.h index fe992a8946..3728a1ea7e 100644 --- a/include/sysemu/cpus.h +++ b/include/sysemu/cpus.h @@ -29,12 +29,9 @@ void qtest_clock_warp(int64_t dest); #ifndef CONFIG_USER_ONLY /* vl.c */ +/* *-user doesn't have configurable SMP topology */ extern int smp_cores; extern int smp_threads; -#else -/* *-user doesn't have configurable SMP topology */ -#define smp_cores 1 -#define smp_threads 1 #endif void list_cpus(FILE *f, fprintf_function cpu_fprintf, const char *optarg); diff --git a/include/ui/console.h b/include/ui/console.h index d9c13d20b1..e2589e2134 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -387,6 +387,7 @@ QemuConsole *qemu_console_lookup_by_device_name(const char *device_id, bool qemu_console_is_visible(QemuConsole *con); bool qemu_console_is_graphic(QemuConsole *con); bool qemu_console_is_fixedsize(QemuConsole *con); +bool qemu_console_is_gl_blocked(QemuConsole *con); char *qemu_console_get_label(QemuConsole *con); int qemu_console_get_index(QemuConsole *con); uint32_t qemu_console_get_head(QemuConsole *con); diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 42e0fdf775..184d4c373a 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -119,7 +119,10 @@ struct SimpleSpiceDisplay { /* opengl rendering */ QEMUBH *gl_unblock_bh; QEMUTimer *gl_unblock_timer; - int dmabuf_fd; + ConsoleGLState *gls; + int gl_updates; + bool have_scanout; + bool have_surface; #endif }; diff --git a/iothread.c b/iothread.c index fb08a60b4b..fbeb8deb38 100644 --- a/iothread.c +++ b/iothread.c @@ -75,6 +75,9 @@ static void iothread_instance_finalize(Object *obj) iothread_stop(obj, NULL); qemu_cond_destroy(&iothread->init_done_cond); qemu_mutex_destroy(&iothread->init_done_lock); + if (!iothread->ctx) { + return; + } aio_context_unref(iothread->ctx); } diff --git a/net/Makefile.objs b/net/Makefile.objs index b7c22fddbf..2a80df5fa7 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -16,3 +16,6 @@ common-obj-$(CONFIG_NETMAP) += netmap.o common-obj-y += filter.o common-obj-y += filter-buffer.o common-obj-y += filter-mirror.o +common-obj-y += colo-compare.o +common-obj-y += colo.o +common-obj-y += filter-rewriter.o diff --git a/net/colo-compare.c b/net/colo-compare.c new file mode 100644 index 0000000000..22b1da19f5 --- /dev/null +++ b/net/colo-compare.c @@ -0,0 +1,781 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "trace.h" +#include "qemu-common.h" +#include "qapi/qmp/qerror.h" +#include "qapi/error.h" +#include "net/net.h" +#include "net/eth.h" +#include "qom/object_interfaces.h" +#include "qemu/iov.h" +#include "qom/object.h" +#include "qemu/typedefs.h" +#include "net/queue.h" +#include "sysemu/char.h" +#include "qemu/sockets.h" +#include "qapi-visit.h" +#include "net/colo.h" + +#define TYPE_COLO_COMPARE "colo-compare" +#define COLO_COMPARE(obj) \ + OBJECT_CHECK(CompareState, (obj), TYPE_COLO_COMPARE) + +#define COMPARE_READ_LEN_MAX NET_BUFSIZE +#define MAX_QUEUE_SIZE 1024 + +/* TODO: Should be configurable */ +#define REGULAR_PACKET_CHECK_MS 3000 + +/* + + CompareState ++ + | | + +---------------+ +---------------+ +---------------+ + |conn list +--->conn +--------->conn | + +---------------+ +---------------+ +---------------+ + | | | | | | + +---------------+ +---v----+ +---v----+ +---v----+ +---v----+ + |primary | |secondary |primary | |secondary + |packet | |packet + |packet | |packet + + +--------+ +--------+ +--------+ +--------+ + | | | | + +---v----+ +---v----+ +---v----+ +---v----+ + |primary | |secondary |primary | |secondary + |packet | |packet + |packet | |packet + + +--------+ +--------+ +--------+ +--------+ + | | | | + +---v----+ +---v----+ +---v----+ +---v----+ + |primary | |secondary |primary | |secondary + |packet | |packet + |packet | |packet + + +--------+ +--------+ +--------+ +--------+ +*/ +typedef struct CompareState { + Object parent; + + char *pri_indev; + char *sec_indev; + char *outdev; + CharDriverState *chr_pri_in; + CharDriverState *chr_sec_in; + CharDriverState *chr_out; + SocketReadState pri_rs; + SocketReadState sec_rs; + + /* connection list: the connections belonged to this NIC could be found + * in this list. + * element type: Connection + */ + GQueue conn_list; + /* hashtable to save connection */ + GHashTable *connection_track_table; + /* compare thread, a thread for each NIC */ + QemuThread thread; + /* Timer used on the primary to find packets that are never matched */ + QEMUTimer *timer; + QemuMutex timer_check_lock; +} CompareState; + +typedef struct CompareClass { + ObjectClass parent_class; +} CompareClass; + +typedef struct CompareChardevProps { + bool is_socket; +} CompareChardevProps; + +enum { + PRIMARY_IN = 0, + SECONDARY_IN, +}; + +static int compare_chr_send(CharDriverState *out, + const uint8_t *buf, + uint32_t size); + +/* + * Return 0 on success, if return -1 means the pkt + * is unsupported(arp and ipv6) and will be sent later + */ +static int packet_enqueue(CompareState *s, int mode) +{ + ConnectionKey key; + Packet *pkt = NULL; + Connection *conn; + + if (mode == PRIMARY_IN) { + pkt = packet_new(s->pri_rs.buf, s->pri_rs.packet_len); + } else { + pkt = packet_new(s->sec_rs.buf, s->sec_rs.packet_len); + } + + if (parse_packet_early(pkt)) { + packet_destroy(pkt, NULL); + pkt = NULL; + return -1; + } + fill_connection_key(pkt, &key); + + conn = connection_get(s->connection_track_table, + &key, + &s->conn_list); + + if (!conn->processing) { + g_queue_push_tail(&s->conn_list, conn); + conn->processing = true; + } + + if (mode == PRIMARY_IN) { + if (g_queue_get_length(&conn->primary_list) <= + MAX_QUEUE_SIZE) { + g_queue_push_tail(&conn->primary_list, pkt); + } else { + error_report("colo compare primary queue size too big," + "drop packet"); + } + } else { + if (g_queue_get_length(&conn->secondary_list) <= + MAX_QUEUE_SIZE) { + g_queue_push_tail(&conn->secondary_list, pkt); + } else { + error_report("colo compare secondary queue size too big," + "drop packet"); + } + } + + return 0; +} + +/* + * The IP packets sent by primary and secondary + * will be compared in here + * TODO support ip fragment, Out-Of-Order + * return: 0 means packet same + * > 0 || < 0 means packet different + */ +static int colo_packet_compare(Packet *ppkt, Packet *spkt) +{ + trace_colo_compare_ip_info(ppkt->size, inet_ntoa(ppkt->ip->ip_src), + inet_ntoa(ppkt->ip->ip_dst), spkt->size, + inet_ntoa(spkt->ip->ip_src), + inet_ntoa(spkt->ip->ip_dst)); + + if (ppkt->size == spkt->size) { + return memcmp(ppkt->data, spkt->data, spkt->size); + } else { + return -1; + } +} + +/* + * Called from the compare thread on the primary + * for compare tcp packet + * compare_tcp copied from Dr. David Alan Gilbert's branch + */ +static int colo_packet_compare_tcp(Packet *spkt, Packet *ppkt) +{ + struct tcphdr *ptcp, *stcp; + int res; + char *sdebug, *ddebug; + + trace_colo_compare_main("compare tcp"); + if (ppkt->size != spkt->size) { + if (trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE)) { + trace_colo_compare_main("pkt size not same"); + } + return -1; + } + + ptcp = (struct tcphdr *)ppkt->transport_header; + stcp = (struct tcphdr *)spkt->transport_header; + + /* + * The 'identification' field in the IP header is *very* random + * it almost never matches. Fudge this by ignoring differences in + * unfragmented packets; they'll normally sort themselves out if different + * anyway, and it should recover at the TCP level. + * An alternative would be to get both the primary and secondary to rewrite + * somehow; but that would need some sync traffic to sync the state + */ + if (ntohs(ppkt->ip->ip_off) & IP_DF) { + spkt->ip->ip_id = ppkt->ip->ip_id; + /* and the sum will be different if the IDs were different */ + spkt->ip->ip_sum = ppkt->ip->ip_sum; + } + + res = memcmp(ppkt->data + ETH_HLEN, spkt->data + ETH_HLEN, + (spkt->size - ETH_HLEN)); + + if (res != 0 && trace_event_get_state(TRACE_COLO_COMPARE_MISCOMPARE)) { + sdebug = strdup(inet_ntoa(ppkt->ip->ip_src)); + ddebug = strdup(inet_ntoa(ppkt->ip->ip_dst)); + fprintf(stderr, "%s: src/dst: %s/%s p: seq/ack=%u/%u" + " s: seq/ack=%u/%u res=%d flags=%x/%x\n", + __func__, sdebug, ddebug, + (unsigned int)ntohl(ptcp->th_seq), + (unsigned int)ntohl(ptcp->th_ack), + (unsigned int)ntohl(stcp->th_seq), + (unsigned int)ntohl(stcp->th_ack), + res, ptcp->th_flags, stcp->th_flags); + + fprintf(stderr, "Primary len = %d\n", ppkt->size); + qemu_hexdump((char *)ppkt->data, stderr, "colo-compare", ppkt->size); + fprintf(stderr, "Secondary len = %d\n", spkt->size); + qemu_hexdump((char *)spkt->data, stderr, "colo-compare", spkt->size); + + g_free(sdebug); + g_free(ddebug); + } + + return res; +} + +/* + * Called from the compare thread on the primary + * for compare udp packet + */ +static int colo_packet_compare_udp(Packet *spkt, Packet *ppkt) +{ + int ret; + + trace_colo_compare_main("compare udp"); + ret = colo_packet_compare(ppkt, spkt); + + if (ret) { + trace_colo_compare_udp_miscompare("primary pkt size", ppkt->size); + qemu_hexdump((char *)ppkt->data, stderr, "colo-compare", ppkt->size); + trace_colo_compare_udp_miscompare("Secondary pkt size", spkt->size); + qemu_hexdump((char *)spkt->data, stderr, "colo-compare", spkt->size); + } + + return ret; +} + +/* + * Called from the compare thread on the primary + * for compare icmp packet + */ +static int colo_packet_compare_icmp(Packet *spkt, Packet *ppkt) +{ + int network_length; + + trace_colo_compare_main("compare icmp"); + network_length = ppkt->ip->ip_hl * 4; + if (ppkt->size != spkt->size || + ppkt->size < network_length + ETH_HLEN) { + return -1; + } + + if (colo_packet_compare(ppkt, spkt)) { + trace_colo_compare_icmp_miscompare("primary pkt size", + ppkt->size); + qemu_hexdump((char *)ppkt->data, stderr, "colo-compare", + ppkt->size); + trace_colo_compare_icmp_miscompare("Secondary pkt size", + spkt->size); + qemu_hexdump((char *)spkt->data, stderr, "colo-compare", + spkt->size); + return -1; + } else { + return 0; + } +} + +/* + * Called from the compare thread on the primary + * for compare other packet + */ +static int colo_packet_compare_other(Packet *spkt, Packet *ppkt) +{ + trace_colo_compare_main("compare other"); + trace_colo_compare_ip_info(ppkt->size, inet_ntoa(ppkt->ip->ip_src), + inet_ntoa(ppkt->ip->ip_dst), spkt->size, + inet_ntoa(spkt->ip->ip_src), + inet_ntoa(spkt->ip->ip_dst)); + return colo_packet_compare(ppkt, spkt); +} + +static int colo_old_packet_check_one(Packet *pkt, int64_t *check_time) +{ + int64_t now = qemu_clock_get_ms(QEMU_CLOCK_HOST); + + if ((now - pkt->creation_ms) > (*check_time)) { + trace_colo_old_packet_check_found(pkt->creation_ms); + return 0; + } else { + return 1; + } +} + +static void colo_old_packet_check_one_conn(void *opaque, + void *user_data) +{ + Connection *conn = opaque; + GList *result = NULL; + int64_t check_time = REGULAR_PACKET_CHECK_MS; + + result = g_queue_find_custom(&conn->primary_list, + &check_time, + (GCompareFunc)colo_old_packet_check_one); + + if (result) { + /* do checkpoint will flush old packet */ + /* TODO: colo_notify_checkpoint();*/ + } +} + +/* + * Look for old packets that the secondary hasn't matched, + * if we have some then we have to checkpoint to wake + * the secondary up. + */ +static void colo_old_packet_check(void *opaque) +{ + CompareState *s = opaque; + + g_queue_foreach(&s->conn_list, colo_old_packet_check_one_conn, NULL); +} + +/* + * Called from the compare thread on the primary + * for compare connection + */ +static void colo_compare_connection(void *opaque, void *user_data) +{ + CompareState *s = user_data; + Connection *conn = opaque; + Packet *pkt = NULL; + GList *result = NULL; + int ret; + + while (!g_queue_is_empty(&conn->primary_list) && + !g_queue_is_empty(&conn->secondary_list)) { + qemu_mutex_lock(&s->timer_check_lock); + pkt = g_queue_pop_tail(&conn->primary_list); + qemu_mutex_unlock(&s->timer_check_lock); + switch (conn->ip_proto) { + case IPPROTO_TCP: + result = g_queue_find_custom(&conn->secondary_list, + pkt, (GCompareFunc)colo_packet_compare_tcp); + break; + case IPPROTO_UDP: + result = g_queue_find_custom(&conn->secondary_list, + pkt, (GCompareFunc)colo_packet_compare_udp); + break; + case IPPROTO_ICMP: + result = g_queue_find_custom(&conn->secondary_list, + pkt, (GCompareFunc)colo_packet_compare_icmp); + break; + default: + result = g_queue_find_custom(&conn->secondary_list, + pkt, (GCompareFunc)colo_packet_compare_other); + break; + } + + if (result) { + ret = compare_chr_send(s->chr_out, pkt->data, pkt->size); + if (ret < 0) { + error_report("colo_send_primary_packet failed"); + } + trace_colo_compare_main("packet same and release packet"); + g_queue_remove(&conn->secondary_list, result->data); + packet_destroy(pkt, NULL); + } else { + /* + * If one packet arrive late, the secondary_list or + * primary_list will be empty, so we can't compare it + * until next comparison. + */ + trace_colo_compare_main("packet different"); + qemu_mutex_lock(&s->timer_check_lock); + g_queue_push_tail(&conn->primary_list, pkt); + qemu_mutex_unlock(&s->timer_check_lock); + /* TODO: colo_notify_checkpoint();*/ + break; + } + } +} + +static int compare_chr_send(CharDriverState *out, + const uint8_t *buf, + uint32_t size) +{ + int ret = 0; + uint32_t len = htonl(size); + + if (!size) { + return 0; + } + + ret = qemu_chr_fe_write_all(out, (uint8_t *)&len, sizeof(len)); + if (ret != sizeof(len)) { + goto err; + } + + ret = qemu_chr_fe_write_all(out, (uint8_t *)buf, size); + if (ret != size) { + goto err; + } + + return 0; + +err: + return ret < 0 ? ret : -EIO; +} + +static int compare_chr_can_read(void *opaque) +{ + return COMPARE_READ_LEN_MAX; +} + +/* + * Called from the main thread on the primary for packets + * arriving over the socket from the primary. + */ +static void compare_pri_chr_in(void *opaque, const uint8_t *buf, int size) +{ + CompareState *s = COLO_COMPARE(opaque); + int ret; + + ret = net_fill_rstate(&s->pri_rs, buf, size); + if (ret == -1) { + qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL); + error_report("colo-compare primary_in error"); + } +} + +/* + * Called from the main thread on the primary for packets + * arriving over the socket from the secondary. + */ +static void compare_sec_chr_in(void *opaque, const uint8_t *buf, int size) +{ + CompareState *s = COLO_COMPARE(opaque); + int ret; + + ret = net_fill_rstate(&s->sec_rs, buf, size); + if (ret == -1) { + qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL); + error_report("colo-compare secondary_in error"); + } +} + +static void *colo_compare_thread(void *opaque) +{ + GMainContext *worker_context; + GMainLoop *compare_loop; + CompareState *s = opaque; + + worker_context = g_main_context_new(); + + qemu_chr_add_handlers_full(s->chr_pri_in, compare_chr_can_read, + compare_pri_chr_in, NULL, s, worker_context); + qemu_chr_add_handlers_full(s->chr_sec_in, compare_chr_can_read, + compare_sec_chr_in, NULL, s, worker_context); + + compare_loop = g_main_loop_new(worker_context, FALSE); + + g_main_loop_run(compare_loop); + + g_main_loop_unref(compare_loop); + g_main_context_unref(worker_context); + return NULL; +} + +static char *compare_get_pri_indev(Object *obj, Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + + return g_strdup(s->pri_indev); +} + +static void compare_set_pri_indev(Object *obj, const char *value, Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + + g_free(s->pri_indev); + s->pri_indev = g_strdup(value); +} + +static char *compare_get_sec_indev(Object *obj, Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + + return g_strdup(s->sec_indev); +} + +static void compare_set_sec_indev(Object *obj, const char *value, Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + + g_free(s->sec_indev); + s->sec_indev = g_strdup(value); +} + +static char *compare_get_outdev(Object *obj, Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + + return g_strdup(s->outdev); +} + +static void compare_set_outdev(Object *obj, const char *value, Error **errp) +{ + CompareState *s = COLO_COMPARE(obj); + + g_free(s->outdev); + s->outdev = g_strdup(value); +} + +static void compare_pri_rs_finalize(SocketReadState *pri_rs) +{ + CompareState *s = container_of(pri_rs, CompareState, pri_rs); + + if (packet_enqueue(s, PRIMARY_IN)) { + trace_colo_compare_main("primary: unsupported packet in"); + compare_chr_send(s->chr_out, pri_rs->buf, pri_rs->packet_len); + } else { + /* compare connection */ + g_queue_foreach(&s->conn_list, colo_compare_connection, s); + } +} + +static void compare_sec_rs_finalize(SocketReadState *sec_rs) +{ + CompareState *s = container_of(sec_rs, CompareState, sec_rs); + + if (packet_enqueue(s, SECONDARY_IN)) { + trace_colo_compare_main("secondary: unsupported packet in"); + } else { + /* compare connection */ + g_queue_foreach(&s->conn_list, colo_compare_connection, s); + } +} + +static int compare_chardev_opts(void *opaque, + const char *name, const char *value, + Error **errp) +{ + CompareChardevProps *props = opaque; + + if (strcmp(name, "backend") == 0 && + strcmp(value, "socket") == 0) { + props->is_socket = true; + return 0; + } else if (strcmp(name, "host") == 0 || + (strcmp(name, "port") == 0) || + (strcmp(name, "server") == 0) || + (strcmp(name, "wait") == 0) || + (strcmp(name, "path") == 0)) { + return 0; + } else { + error_setg(errp, + "COLO-compare does not support a chardev with option %s=%s", + name, value); + return -1; + } +} + +/* + * Return 0 is success. + * Return 1 is failed. + */ +static int find_and_check_chardev(CharDriverState **chr, + char *chr_name, + Error **errp) +{ + CompareChardevProps props; + + *chr = qemu_chr_find(chr_name); + if (*chr == NULL) { + error_setg(errp, "Device '%s' not found", + chr_name); + return 1; + } + + memset(&props, 0, sizeof(props)); + if (qemu_opt_foreach((*chr)->opts, compare_chardev_opts, &props, errp)) { + return 1; + } + + if (!props.is_socket) { + error_setg(errp, "chardev \"%s\" is not a tcp socket", + chr_name); + return 1; + } + return 0; +} + +/* + * Check old packet regularly so it can watch for any packets + * that the secondary hasn't produced equivalents of. + */ +static void check_old_packet_regular(void *opaque) +{ + CompareState *s = opaque; + + timer_mod(s->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + REGULAR_PACKET_CHECK_MS); + /* if have old packet we will notify checkpoint */ + /* + * TODO: Make timer handler run in compare thread + * like qemu_chr_add_handlers_full. + */ + qemu_mutex_lock(&s->timer_check_lock); + colo_old_packet_check(s); + qemu_mutex_unlock(&s->timer_check_lock); +} + +/* + * Called from the main thread on the primary + * to setup colo-compare. + */ +static void colo_compare_complete(UserCreatable *uc, Error **errp) +{ + CompareState *s = COLO_COMPARE(uc); + char thread_name[64]; + static int compare_id; + + if (!s->pri_indev || !s->sec_indev || !s->outdev) { + error_setg(errp, "colo compare needs 'primary_in' ," + "'secondary_in','outdev' property set"); + return; + } else if (!strcmp(s->pri_indev, s->outdev) || + !strcmp(s->sec_indev, s->outdev) || + !strcmp(s->pri_indev, s->sec_indev)) { + error_setg(errp, "'indev' and 'outdev' could not be same " + "for compare module"); + return; + } + + if (find_and_check_chardev(&s->chr_pri_in, s->pri_indev, errp)) { + return; + } + + if (find_and_check_chardev(&s->chr_sec_in, s->sec_indev, errp)) { + return; + } + + if (find_and_check_chardev(&s->chr_out, s->outdev, errp)) { + return; + } + + qemu_chr_fe_claim_no_fail(s->chr_pri_in); + + qemu_chr_fe_claim_no_fail(s->chr_sec_in); + + qemu_chr_fe_claim_no_fail(s->chr_out); + + net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize); + net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize); + + g_queue_init(&s->conn_list); + qemu_mutex_init(&s->timer_check_lock); + + s->connection_track_table = g_hash_table_new_full(connection_key_hash, + connection_key_equal, + g_free, + connection_destroy); + + sprintf(thread_name, "colo-compare %d", compare_id); + qemu_thread_create(&s->thread, thread_name, + colo_compare_thread, s, + QEMU_THREAD_JOINABLE); + compare_id++; + + /* A regular timer to kick any packets that the secondary doesn't match */ + s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, /* Only when guest runs */ + check_old_packet_regular, s); + timer_mod(s->timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + + REGULAR_PACKET_CHECK_MS); + + return; +} + +static void colo_compare_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + + ucc->complete = colo_compare_complete; +} + +static void colo_compare_init(Object *obj) +{ + object_property_add_str(obj, "primary_in", + compare_get_pri_indev, compare_set_pri_indev, + NULL); + object_property_add_str(obj, "secondary_in", + compare_get_sec_indev, compare_set_sec_indev, + NULL); + object_property_add_str(obj, "outdev", + compare_get_outdev, compare_set_outdev, + NULL); +} + +static void colo_compare_finalize(Object *obj) +{ + CompareState *s = COLO_COMPARE(obj); + + if (s->chr_pri_in) { + qemu_chr_add_handlers(s->chr_pri_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_release(s->chr_pri_in); + } + if (s->chr_sec_in) { + qemu_chr_add_handlers(s->chr_sec_in, NULL, NULL, NULL, NULL); + qemu_chr_fe_release(s->chr_sec_in); + } + if (s->chr_out) { + qemu_chr_fe_release(s->chr_out); + } + + g_queue_free(&s->conn_list); + + if (qemu_thread_is_self(&s->thread)) { + /* compare connection */ + g_queue_foreach(&s->conn_list, colo_compare_connection, s); + qemu_thread_join(&s->thread); + } + + if (s->timer) { + timer_del(s->timer); + } + + qemu_mutex_destroy(&s->timer_check_lock); + + g_free(s->pri_indev); + g_free(s->sec_indev); + g_free(s->outdev); +} + +static const TypeInfo colo_compare_info = { + .name = TYPE_COLO_COMPARE, + .parent = TYPE_OBJECT, + .instance_size = sizeof(CompareState), + .instance_init = colo_compare_init, + .instance_finalize = colo_compare_finalize, + .class_size = sizeof(CompareClass), + .class_init = colo_compare_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + +static void register_types(void) +{ + type_register_static(&colo_compare_info); +} + +type_init(register_types); diff --git a/net/colo.c b/net/colo.c new file mode 100644 index 0000000000..6a6eacd2dc --- /dev/null +++ b/net/colo.c @@ -0,0 +1,211 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "net/colo.h" + +uint32_t connection_key_hash(const void *opaque) +{ + const ConnectionKey *key = opaque; + uint32_t a, b, c; + + /* Jenkins hash */ + a = b = c = JHASH_INITVAL + sizeof(*key); + a += key->src.s_addr; + b += key->dst.s_addr; + c += (key->src_port | key->dst_port << 16); + __jhash_mix(a, b, c); + + a += key->ip_proto; + __jhash_final(a, b, c); + + return c; +} + +int connection_key_equal(const void *key1, const void *key2) +{ + return memcmp(key1, key2, sizeof(ConnectionKey)) == 0; +} + +int parse_packet_early(Packet *pkt) +{ + int network_length; + static const uint8_t vlan[] = {0x81, 0x00}; + uint8_t *data = pkt->data; + uint16_t l3_proto; + ssize_t l2hdr_len = eth_get_l2_hdr_length(data); + + if (pkt->size < ETH_HLEN) { + trace_colo_proxy_main("pkt->size < ETH_HLEN"); + return 1; + } + + /* + * TODO: support vlan. + */ + if (!memcmp(&data[12], vlan, sizeof(vlan))) { + trace_colo_proxy_main("COLO-proxy don't support vlan"); + return 1; + } + + pkt->network_header = data + l2hdr_len; + + const struct iovec l2vec = { + .iov_base = (void *) data, + .iov_len = l2hdr_len + }; + l3_proto = eth_get_l3_proto(&l2vec, 1, l2hdr_len); + + if (l3_proto != ETH_P_IP) { + return 1; + } + + network_length = pkt->ip->ip_hl * 4; + if (pkt->size < l2hdr_len + network_length) { + trace_colo_proxy_main("pkt->size < network_header + network_length"); + return 1; + } + pkt->transport_header = pkt->network_header + network_length; + + return 0; +} + +void fill_connection_key(Packet *pkt, ConnectionKey *key) +{ + uint32_t tmp_ports; + + memset(key, 0, sizeof(*key)); + key->ip_proto = pkt->ip->ip_p; + + switch (key->ip_proto) { + case IPPROTO_TCP: + case IPPROTO_UDP: + case IPPROTO_DCCP: + case IPPROTO_ESP: + case IPPROTO_SCTP: + case IPPROTO_UDPLITE: + tmp_ports = *(uint32_t *)(pkt->transport_header); + key->src = pkt->ip->ip_src; + key->dst = pkt->ip->ip_dst; + key->src_port = ntohs(tmp_ports & 0xffff); + key->dst_port = ntohs(tmp_ports >> 16); + break; + case IPPROTO_AH: + tmp_ports = *(uint32_t *)(pkt->transport_header + 4); + key->src = pkt->ip->ip_src; + key->dst = pkt->ip->ip_dst; + key->src_port = ntohs(tmp_ports & 0xffff); + key->dst_port = ntohs(tmp_ports >> 16); + break; + default: + break; + } +} + +void reverse_connection_key(ConnectionKey *key) +{ + struct in_addr tmp_ip; + uint16_t tmp_port; + + tmp_ip = key->src; + key->src = key->dst; + key->dst = tmp_ip; + + tmp_port = key->src_port; + key->src_port = key->dst_port; + key->dst_port = tmp_port; +} + +Connection *connection_new(ConnectionKey *key) +{ + Connection *conn = g_slice_new(Connection); + + conn->ip_proto = key->ip_proto; + conn->processing = false; + conn->offset = 0; + conn->syn_flag = 0; + g_queue_init(&conn->primary_list); + g_queue_init(&conn->secondary_list); + + return conn; +} + +void connection_destroy(void *opaque) +{ + Connection *conn = opaque; + + g_queue_foreach(&conn->primary_list, packet_destroy, NULL); + g_queue_free(&conn->primary_list); + g_queue_foreach(&conn->secondary_list, packet_destroy, NULL); + g_queue_free(&conn->secondary_list); + g_slice_free(Connection, conn); +} + +Packet *packet_new(const void *data, int size) +{ + Packet *pkt = g_slice_new(Packet); + + pkt->data = g_memdup(data, size); + pkt->size = size; + pkt->creation_ms = qemu_clock_get_ms(QEMU_CLOCK_HOST); + + return pkt; +} + +void packet_destroy(void *opaque, void *user_data) +{ + Packet *pkt = opaque; + + g_free(pkt->data); + g_slice_free(Packet, pkt); +} + +/* + * Clear hashtable, stop this hash growing really huge + */ +void connection_hashtable_reset(GHashTable *connection_track_table) +{ + g_hash_table_remove_all(connection_track_table); +} + +/* if not found, create a new connection and add to hash table */ +Connection *connection_get(GHashTable *connection_track_table, + ConnectionKey *key, + GQueue *conn_list) +{ + Connection *conn = g_hash_table_lookup(connection_track_table, key); + + if (conn == NULL) { + ConnectionKey *new_key = g_memdup(key, sizeof(*key)); + + conn = connection_new(key); + + if (g_hash_table_size(connection_track_table) > HASHTABLE_MAX_SIZE) { + trace_colo_proxy_main("colo proxy connection hashtable full," + " clear it"); + connection_hashtable_reset(connection_track_table); + /* + * clear the conn_list + */ + while (!g_queue_is_empty(conn_list)) { + connection_destroy(g_queue_pop_head(conn_list)); + } + } + + g_hash_table_insert(connection_track_table, new_key, conn); + } + + return conn; +} diff --git a/net/colo.h b/net/colo.h new file mode 100644 index 0000000000..7c524f3a1c --- /dev/null +++ b/net/colo.h @@ -0,0 +1,88 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#ifndef QEMU_COLO_PROXY_H +#define QEMU_COLO_PROXY_H + +#include "slirp/slirp.h" +#include "qemu/jhash.h" +#include "qemu/timer.h" + +#define HASHTABLE_MAX_SIZE 16384 + +#ifndef IPPROTO_DCCP +#define IPPROTO_DCCP 33 +#endif + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + +#ifndef IPPROTO_UDPLITE +#define IPPROTO_UDPLITE 136 +#endif + +typedef struct Packet { + void *data; + union { + uint8_t *network_header; + struct ip *ip; + }; + uint8_t *transport_header; + int size; + /* Time of packet creation, in wall clock ms */ + int64_t creation_ms; +} Packet; + +typedef struct ConnectionKey { + /* (src, dst) must be grouped, in the same way than in IP header */ + struct in_addr src; + struct in_addr dst; + uint16_t src_port; + uint16_t dst_port; + uint8_t ip_proto; +} QEMU_PACKED ConnectionKey; + +typedef struct Connection { + /* connection primary send queue: element type: Packet */ + GQueue primary_list; + /* connection secondary send queue: element type: Packet */ + GQueue secondary_list; + /* flag to enqueue unprocessed_connections */ + bool processing; + uint8_t ip_proto; + /* offset = secondary_seq - primary_seq */ + tcp_seq offset; + /* + * we use this flag update offset func + * run once in independent tcp connection + */ + int syn_flag; +} Connection; + +uint32_t connection_key_hash(const void *opaque); +int connection_key_equal(const void *opaque1, const void *opaque2); +int parse_packet_early(Packet *pkt); +void fill_connection_key(Packet *pkt, ConnectionKey *key); +void reverse_connection_key(ConnectionKey *key); +Connection *connection_new(ConnectionKey *key); +void connection_destroy(void *opaque); +Connection *connection_get(GHashTable *connection_track_table, + ConnectionKey *key, + GQueue *conn_list); +void connection_hashtable_reset(GHashTable *connection_track_table); +Packet *packet_new(const void *data, int size); +void packet_destroy(void *opaque, void *user_data); + +#endif /* QEMU_COLO_PROXY_H */ diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c new file mode 100644 index 0000000000..89abe72d4e --- /dev/null +++ b/net/filter-rewriter.c @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2016 FUJITSU LIMITED + * Copyright (c) 2016 Intel Corporation + * + * Author: Zhang Chen <zhangchen.fnst@cn.fujitsu.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "trace.h" +#include "net/colo.h" +#include "net/filter.h" +#include "net/net.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "qapi/qmp/qerror.h" +#include "qapi-visit.h" +#include "qom/object.h" +#include "qemu/main-loop.h" +#include "qemu/iov.h" +#include "net/checksum.h" + +#define FILTER_COLO_REWRITER(obj) \ + OBJECT_CHECK(RewriterState, (obj), TYPE_FILTER_REWRITER) + +#define TYPE_FILTER_REWRITER "filter-rewriter" + +typedef struct RewriterState { + NetFilterState parent_obj; + NetQueue *incoming_queue; + /* hashtable to save connection */ + GHashTable *connection_track_table; +} RewriterState; + +static void filter_rewriter_flush(NetFilterState *nf) +{ + RewriterState *s = FILTER_COLO_REWRITER(nf); + + if (!qemu_net_queue_flush(s->incoming_queue)) { + /* Unable to empty the queue, purge remaining packets */ + qemu_net_queue_purge(s->incoming_queue, nf->netdev); + } +} + +/* + * Return 1 on success, if return 0 means the pkt + * is not TCP packet + */ +static int is_tcp_packet(Packet *pkt) +{ + if (!parse_packet_early(pkt) && + pkt->ip->ip_p == IPPROTO_TCP) { + return 1; + } else { + return 0; + } +} + +/* handle tcp packet from primary guest */ +static int handle_primary_tcp_pkt(NetFilterState *nf, + Connection *conn, + Packet *pkt) +{ + struct tcphdr *tcp_pkt; + + tcp_pkt = (struct tcphdr *)pkt->transport_header; + if (trace_event_get_state(TRACE_COLO_FILTER_REWRITER_DEBUG)) { + char *sdebug, *ddebug; + sdebug = strdup(inet_ntoa(pkt->ip->ip_src)); + ddebug = strdup(inet_ntoa(pkt->ip->ip_dst)); + trace_colo_filter_rewriter_pkt_info(__func__, sdebug, ddebug, + ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), + tcp_pkt->th_flags); + trace_colo_filter_rewriter_conn_offset(conn->offset); + g_free(sdebug); + g_free(ddebug); + } + + if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { + /* + * we use this flag update offset func + * run once in independent tcp connection + */ + conn->syn_flag = 1; + } + + if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK)) { + if (conn->syn_flag) { + /* + * offset = secondary_seq - primary seq + * ack packet sent by guest from primary node, + * so we use th_ack - 1 get primary_seq + */ + conn->offset -= (ntohl(tcp_pkt->th_ack) - 1); + conn->syn_flag = 0; + } + /* handle packets to the secondary from the primary */ + tcp_pkt->th_ack = htonl(ntohl(tcp_pkt->th_ack) + conn->offset); + + net_checksum_calculate((uint8_t *)pkt->data, pkt->size); + } + + return 0; +} + +/* handle tcp packet from secondary guest */ +static int handle_secondary_tcp_pkt(NetFilterState *nf, + Connection *conn, + Packet *pkt) +{ + struct tcphdr *tcp_pkt; + + tcp_pkt = (struct tcphdr *)pkt->transport_header; + + if (trace_event_get_state(TRACE_COLO_FILTER_REWRITER_DEBUG)) { + char *sdebug, *ddebug; + sdebug = strdup(inet_ntoa(pkt->ip->ip_src)); + ddebug = strdup(inet_ntoa(pkt->ip->ip_dst)); + trace_colo_filter_rewriter_pkt_info(__func__, sdebug, ddebug, + ntohl(tcp_pkt->th_seq), ntohl(tcp_pkt->th_ack), + tcp_pkt->th_flags); + trace_colo_filter_rewriter_conn_offset(conn->offset); + g_free(sdebug); + g_free(ddebug); + } + + if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { + /* + * save offset = secondary_seq and then + * in handle_primary_tcp_pkt make offset + * = secondary_seq - primary_seq + */ + conn->offset = ntohl(tcp_pkt->th_seq); + } + + if ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK) { + /* handle packets to the primary from the secondary*/ + tcp_pkt->th_seq = htonl(ntohl(tcp_pkt->th_seq) - conn->offset); + + net_checksum_calculate((uint8_t *)pkt->data, pkt->size); + } + + return 0; +} + +static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, + NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + NetPacketSent *sent_cb) +{ + RewriterState *s = FILTER_COLO_REWRITER(nf); + Connection *conn; + ConnectionKey key; + Packet *pkt; + ssize_t size = iov_size(iov, iovcnt); + char *buf = g_malloc0(size); + + iov_to_buf(iov, iovcnt, 0, buf, size); + pkt = packet_new(buf, size); + + /* + * if we get tcp packet + * we will rewrite it to make secondary guest's + * connection established successfully + */ + if (pkt && is_tcp_packet(pkt)) { + + fill_connection_key(pkt, &key); + + if (sender == nf->netdev) { + /* + * We need make tcp TX and RX packet + * into one connection. + */ + reverse_connection_key(&key); + } + conn = connection_get(s->connection_track_table, + &key, + NULL); + + if (sender == nf->netdev) { + /* NET_FILTER_DIRECTION_TX */ + if (!handle_primary_tcp_pkt(nf, conn, pkt)) { + qemu_net_queue_send(s->incoming_queue, sender, 0, + (const uint8_t *)pkt->data, pkt->size, NULL); + packet_destroy(pkt, NULL); + pkt = NULL; + /* + * We block the packet here,after rewrite pkt + * and will send it + */ + return 1; + } + } else { + /* NET_FILTER_DIRECTION_RX */ + if (!handle_secondary_tcp_pkt(nf, conn, pkt)) { + qemu_net_queue_send(s->incoming_queue, sender, 0, + (const uint8_t *)pkt->data, pkt->size, NULL); + packet_destroy(pkt, NULL); + pkt = NULL; + /* + * We block the packet here,after rewrite pkt + * and will send it + */ + return 1; + } + } + } + + packet_destroy(pkt, NULL); + pkt = NULL; + return 0; +} + +static void colo_rewriter_cleanup(NetFilterState *nf) +{ + RewriterState *s = FILTER_COLO_REWRITER(nf); + + /* flush packets */ + if (s->incoming_queue) { + filter_rewriter_flush(nf); + g_free(s->incoming_queue); + } +} + +static void colo_rewriter_setup(NetFilterState *nf, Error **errp) +{ + RewriterState *s = FILTER_COLO_REWRITER(nf); + + s->connection_track_table = g_hash_table_new_full(connection_key_hash, + connection_key_equal, + g_free, + connection_destroy); + s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf); +} + +static void colo_rewriter_class_init(ObjectClass *oc, void *data) +{ + NetFilterClass *nfc = NETFILTER_CLASS(oc); + + nfc->setup = colo_rewriter_setup; + nfc->cleanup = colo_rewriter_cleanup; + nfc->receive_iov = colo_rewriter_receive_iov; +} + +static const TypeInfo colo_rewriter_info = { + .name = TYPE_FILTER_REWRITER, + .parent = TYPE_NETFILTER, + .class_init = colo_rewriter_class_init, + .instance_size = sizeof(RewriterState), +}; + +static void register_types(void) +{ + type_register_static(&colo_rewriter_info); +} + +type_init(register_types); @@ -690,9 +690,13 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, buffer = iov[0].iov_base; offset = iov[0].iov_len; } else { - buf = g_new(uint8_t, NET_BUFSIZE); + offset = iov_size(iov, iovcnt); + if (offset > NET_BUFSIZE) { + return -1; + } + buf = g_malloc(offset); buffer = buf; - offset = iov_to_buf(iov, iovcnt, 0, buf, NET_BUFSIZE); + offset = iov_to_buf(iov, iovcnt, 0, buf, offset); } if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) { @@ -1179,6 +1183,7 @@ void hmp_host_net_remove(Monitor *mon, const QDict *qdict) qemu_del_net_client(nc->peer); qemu_del_net_client(nc); + qemu_opts_del(qemu_opts_find(qemu_find_opts("net"), device)); } void netdev_add(QemuOpts *opts, Error **errp) @@ -857,7 +857,9 @@ free_fail: return -1; } - fd = net_bridge_run_helper(tap->helper, DEFAULT_BRIDGE_INTERFACE, + fd = net_bridge_run_helper(tap->helper, + tap->has_br ? + tap->br : DEFAULT_BRIDGE_INTERFACE, errp); if (fd == -1) { return -1; diff --git a/qapi-schema.json b/qapi-schema.json index e50706111c..c3dcf11a4a 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2636,6 +2636,8 @@ # # @downscript: #optional script to shut down the interface # +# @br: #optional bridge name (since 2.8) +# # @helper: #optional command to execute to configure bridge # # @sndbuf: #optional send buffer limit. Understands [TGMKkb] suffixes. @@ -2665,6 +2667,7 @@ '*fds': 'str', '*script': 'str', '*downscript': 'str', + '*br': 'str', '*helper': 'str', '*sndbuf': 'size', '*vnet_hdr': 'bool', diff --git a/qemu-char.c b/qemu-char.c index 8826419d7d..fb456cec34 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -449,11 +449,12 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) static void remove_fd_in_watch(CharDriverState *chr); -void qemu_chr_add_handlers(CharDriverState *s, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - void *opaque) +void qemu_chr_add_handlers_full(CharDriverState *s, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque, + GMainContext *context) { int fe_open; @@ -467,8 +468,9 @@ void qemu_chr_add_handlers(CharDriverState *s, s->chr_read = fd_read; s->chr_event = fd_event; s->handler_opaque = opaque; - if (fe_open && s->chr_update_read_handler) - s->chr_update_read_handler(s); + if (fe_open && s->chr_update_read_handler) { + s->chr_update_read_handler(s, context); + } if (!s->explicit_fe_open) { qemu_chr_fe_set_open(s, fe_open); @@ -481,6 +483,16 @@ void qemu_chr_add_handlers(CharDriverState *s, } } +void qemu_chr_add_handlers(CharDriverState *s, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + void *opaque) +{ + qemu_chr_add_handlers_full(s, fd_can_read, fd_read, + fd_event, opaque, NULL); +} + static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { return len; @@ -722,7 +734,8 @@ static void mux_chr_event(void *opaque, int event) mux_chr_send_event(d, i, event); } -static void mux_chr_update_read_handler(CharDriverState *chr) +static void mux_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { MuxDriver *d = chr->opaque; @@ -736,8 +749,10 @@ static void mux_chr_update_read_handler(CharDriverState *chr) d->chr_event[d->mux_cnt] = chr->chr_event; /* Fix up the real driver with mux routines */ if (d->mux_cnt == 0) { - qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read, - mux_chr_event, chr); + qemu_chr_add_handlers_full(d->drv, mux_chr_can_read, + mux_chr_read, + mux_chr_event, + chr, context); } if (d->focus != -1) { mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT); @@ -853,6 +868,7 @@ typedef struct IOWatchPoll IOCanReadHandler *fd_can_read; GSourceFunc fd_read; void *opaque; + GMainContext *context; } IOWatchPoll; static IOWatchPoll *io_watch_poll_from_source(GSource *source) @@ -860,7 +876,8 @@ static IOWatchPoll *io_watch_poll_from_source(GSource *source) return container_of(source, IOWatchPoll, parent); } -static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) +static gboolean io_watch_poll_prepare(GSource *source, + gint *timeout_) { IOWatchPoll *iwp = io_watch_poll_from_source(source); bool now_active = iwp->fd_can_read(iwp->opaque) > 0; @@ -873,7 +890,7 @@ static gboolean io_watch_poll_prepare(GSource *source, gint *timeout_) iwp->src = qio_channel_create_watch( iwp->ioc, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); g_source_set_callback(iwp->src, iwp->fd_read, iwp->opaque, NULL); - g_source_attach(iwp->src, NULL); + g_source_attach(iwp->src, iwp->context); } else { g_source_destroy(iwp->src); g_source_unref(iwp->src); @@ -920,19 +937,22 @@ static GSourceFuncs io_watch_poll_funcs = { static guint io_add_watch_poll(QIOChannel *ioc, IOCanReadHandler *fd_can_read, QIOChannelFunc fd_read, - gpointer user_data) + gpointer user_data, + GMainContext *context) { IOWatchPoll *iwp; int tag; - iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, sizeof(IOWatchPoll)); + iwp = (IOWatchPoll *) g_source_new(&io_watch_poll_funcs, + sizeof(IOWatchPoll)); iwp->fd_can_read = fd_can_read; iwp->opaque = user_data; iwp->ioc = ioc; iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; + iwp->context = context; - tag = g_source_attach(&iwp->parent, NULL); + tag = g_source_attach(&iwp->parent, context); g_source_unref(&iwp->parent); return tag; } @@ -1064,7 +1084,8 @@ static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond) return qio_channel_create_watch(s->ioc_out, cond); } -static void fd_chr_update_read_handler(CharDriverState *chr) +static void fd_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { FDCharDriver *s = chr->opaque; @@ -1072,7 +1093,8 @@ static void fd_chr_update_read_handler(CharDriverState *chr) if (s->ioc_in) { chr->fd_in_tag = io_add_watch_poll(s->ioc_in, fd_chr_read_poll, - fd_chr_read, chr); + fd_chr_read, chr, + context); } } @@ -1319,7 +1341,8 @@ static void pty_chr_update_read_handler_locked(CharDriverState *chr) } } -static void pty_chr_update_read_handler(CharDriverState *chr) +static void pty_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { qemu_mutex_lock(&chr->chr_write_lock); pty_chr_update_read_handler_locked(chr); @@ -1423,7 +1446,8 @@ static void pty_chr_state(CharDriverState *chr, int connected) if (!chr->fd_in_tag) { chr->fd_in_tag = io_add_watch_poll(s->ioc, pty_chr_read_poll, - pty_chr_read, chr); + pty_chr_read, + chr, NULL); } } } @@ -2565,7 +2589,8 @@ static gboolean udp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static void udp_chr_update_read_handler(CharDriverState *chr) +static void udp_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { NetCharDriver *s = chr->opaque; @@ -2573,7 +2598,8 @@ static void udp_chr_update_read_handler(CharDriverState *chr) if (s->ioc) { chr->fd_in_tag = io_add_watch_poll(s->ioc, udp_chr_read_poll, - udp_chr_read, chr); + udp_chr_read, chr, + context); } } @@ -2976,12 +3002,14 @@ static void tcp_chr_connect(void *opaque) if (s->ioc) { chr->fd_in_tag = io_add_watch_poll(s->ioc, tcp_chr_read_poll, - tcp_chr_read, chr); + tcp_chr_read, + chr, NULL); } qemu_chr_be_generic_open(chr); } -static void tcp_chr_update_read_handler(CharDriverState *chr) +static void tcp_chr_update_read_handler(CharDriverState *chr, + GMainContext *context) { TCPCharDriver *s = chr->opaque; @@ -2993,7 +3021,8 @@ static void tcp_chr_update_read_handler(CharDriverState *chr) if (s->ioc) { chr->fd_in_tag = io_add_watch_poll(s->ioc, tcp_chr_read_poll, - tcp_chr_read, chr); + tcp_chr_read, chr, + context); } } diff --git a/qemu-options.hx b/qemu-options.hx index 0b621bb99e..01f01dfadc 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1598,10 +1598,11 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " configure a host TAP network backend with ID 'str'\n" #else "-netdev tap,id=str[,fd=h][,fds=x:y:...:z][,ifname=name][,script=file][,downscript=dfile]\n" - " [,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off]\n" + " [,br=bridge][,helper=helper][,sndbuf=nbytes][,vnet_hdr=on|off][,vhost=on|off]\n" " [,vhostfd=h][,vhostfds=x:y:...:z][,vhostforce=on|off][,queues=n]\n" " [,poll-us=n]\n" " configure a host TAP network backend with ID 'str'\n" + " connected to a bridge (default=" DEFAULT_BRIDGE_INTERFACE ")\n" " use network scripts 'file' (default=" DEFAULT_NETWORK_SCRIPT ")\n" " to configure it and 'dfile' (default=" DEFAULT_NETWORK_DOWN_SCRIPT ")\n" " to deconfigure it\n" @@ -1888,8 +1889,8 @@ processed and applied to -net user. Mixing them with the new configuration syntax gives undefined results. Their use for new applications is discouraged as they will be removed from future versions. -@item -netdev tap,id=@var{id}[,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,helper=@var{helper}] -@itemx -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,helper=@var{helper}] +@item -netdev tap,id=@var{id}[,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}] +@itemx -net tap[,vlan=@var{n}][,name=@var{name}][,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}] Connect the host TAP network interface @var{name} to VLAN @var{n}. Use the network script @var{file} to configure it and the network script @@ -1900,8 +1901,9 @@ automatically provides one. The default network configure script is to disable script execution. If running QEMU as an unprivileged user, use the network helper -@var{helper} to configure the TAP interface. The default network -helper executable is @file{/path/to/qemu-bridge-helper}. +@var{helper} to configure the TAP interface and attach it to the bridge. +The default network helper executable is @file{/path/to/qemu-bridge-helper} +and the default bridge device is @file{br0}. @option{fd}=@var{h} can be used to specify the handle of an already opened host TAP interface. @@ -3887,6 +3889,19 @@ Create a filter-redirector we need to differ outdev id from indev id, id can not be the same. we can just use indev or outdev, but at least one of indev or outdev need to be specified. +@item -object filter-rewriter,id=@var{id},netdev=@var{netdevid},rewriter-mode=@var{mode}[,queue=@var{all|rx|tx}] + +Filter-rewriter is a part of COLO project.It will rewrite tcp packet to +secondary from primary to keep secondary tcp connection,and rewrite +tcp packet to primary from secondary make tcp packet can be handled by +client. + +usage: +colo secondary: +-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 +-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 +-object filter-rewriter,id=rew0,netdev=hn0,queue=all + @item -object filter-dump,id=@var{id},netdev=@var{dev},file=@var{filename}][,maxlen=@var{len}] Dump the network traffic on netdev @var{dev} to the file specified by @@ -3894,6 +3909,45 @@ Dump the network traffic on netdev @var{dev} to the file specified by The file format is libpcap, so it can be analyzed with tools such as tcpdump or Wireshark. +@item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid}, +outdev=@var{chardevid} + +Colo-compare gets packet from primary_in@var{chardevid} and secondary_in@var{chardevid}, than compare primary packet with +secondary packet. If the packets are same, we will output primary +packet to outdev@var{chardevid}, else we will notify colo-frame +do checkpoint and send primary packet to outdev@var{chardevid}. + +we must use it with the help of filter-mirror and filter-redirector. + +@example + +primary: +-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown +-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66 +-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait +-chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait +-chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait +-chardev socket,id=compare0-0,host=3.3.3.3,port=9001 +-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait +-chardev socket,id=compare_out0,host=3.3.3.3,port=9005 +-object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0 +-object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out +-object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0 +-object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0 + +secondary: +-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown +-device e1000,netdev=hn0,mac=52:a4:00:12:78:66 +-chardev socket,id=red0,host=3.3.3.3,port=9003 +-chardev socket,id=red1,host=3.3.3.3,port=9004 +-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0 +-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1 + +@end example + +If you want to know the detail of above command line, you can read +the colo-compare git log. + @item -object secret,id=@var{id},data=@var{string},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}] @item -object secret,id=@var{id},file=@var{filename},format=@var{raw|base64}[,keyid=@var{secretid},iv=@var{string}] diff --git a/target-cris/cpu.c b/target-cris/cpu.c index c5a656bb62..d680cfb52b 100644 --- a/target-cris/cpu.c +++ b/target-cris/cpu.c @@ -246,6 +246,16 @@ static void crisv11_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_read_register = crisv10_cpu_gdb_read_register; } +static void crisv17_cpu_class_init(ObjectClass *oc, void *data) +{ + CPUClass *cc = CPU_CLASS(oc); + CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); + + ccc->vr = 17; + cc->do_interrupt = crisv10_cpu_do_interrupt; + cc->gdb_read_register = crisv10_cpu_gdb_read_register; +} + static void crisv32_cpu_class_init(ObjectClass *oc, void *data) { CRISCPUClass *ccc = CRIS_CPU_CLASS(oc); @@ -273,6 +283,10 @@ static const TypeInfo cris_cpu_model_type_infos[] = { .parent = TYPE_CRIS_CPU, .class_init = crisv11_cpu_class_init, }, { + .name = TYPE("crisv17"), + .parent = TYPE_CRIS_CPU, + .class_init = crisv17_cpu_class_init, + }, { .name = TYPE("crisv32"), .parent = TYPE_CRIS_CPU, .class_init = crisv32_cpu_class_init, diff --git a/target-cris/crisv10-decode.h b/target-cris/crisv10-decode.h index 587fbdd278..bdb4b6d318 100644 --- a/target-cris/crisv10-decode.h +++ b/target-cris/crisv10-decode.h @@ -92,6 +92,7 @@ #define CRISV10_IND_JUMP_M 4 #define CRISV10_IND_DIP 5 #define CRISV10_IND_JUMP_R 6 +#define CRISV17_IND_ADDC 6 #define CRISV10_IND_BOUND 7 #define CRISV10_IND_BCC_M 7 #define CRISV10_IND_MOVE_M_SPR 8 diff --git a/target-cris/translate.c b/target-cris/translate.c index f4a8d7d000..b5ab0a5fb2 100644 --- a/target-cris/translate.c +++ b/target-cris/translate.c @@ -140,14 +140,14 @@ static void gen_BUG(DisasContext *dc, const char *file, int line) cpu_abort(CPU(dc->cpu), "%s:%d\n", file, line); } -static const char *regnames[] = +static const char *regnames_v32[] = { "$r0", "$r1", "$r2", "$r3", "$r4", "$r5", "$r6", "$r7", "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$sp", "$acr", }; -static const char *pregnames[] = +static const char *pregnames_v32[] = { "$bz", "$vr", "$pid", "$srs", "$wz", "$exs", "$eda", "$mof", @@ -3327,12 +3327,20 @@ void cris_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, { CRISCPU *cpu = CRIS_CPU(cs); CPUCRISState *env = &cpu->env; + const char **regnames; + const char **pregnames; int i; - uint32_t srs; if (!env || !f) { return; } + if (env->pregs[PR_VR] < 32) { + pregnames = pregnames_v10; + regnames = regnames_v10; + } else { + pregnames = pregnames_v32; + regnames = regnames_v32; + } cpu_fprintf(f, "PC=%x CCS=%x btaken=%d btarget=%x\n" "cc_op=%d cc_src=%d cc_dest=%d cc_result=%x cc_mask=%x\n", @@ -3354,14 +3362,16 @@ void cris_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "\n"); } } - srs = env->pregs[PR_SRS]; - cpu_fprintf(f, "\nsupport function regs bank %x:\n", srs); - if (srs < ARRAY_SIZE(env->sregs)) { - for (i = 0; i < 16; i++) { - cpu_fprintf(f, "s%2.2d=%8.8x ", - i, env->sregs[srs][i]); - if ((i + 1) % 4 == 0) { - cpu_fprintf(f, "\n"); + if (env->pregs[PR_VR] >= 32) { + uint32_t srs = env->pregs[PR_SRS]; + cpu_fprintf(f, "\nsupport function regs bank %x:\n", srs); + if (srs < ARRAY_SIZE(env->sregs)) { + for (i = 0; i < 16; i++) { + cpu_fprintf(f, "s%2.2d=%8.8x ", + i, env->sregs[srs][i]); + if ((i + 1) % 4 == 0) { + cpu_fprintf(f, "\n"); + } } } } @@ -3406,12 +3416,12 @@ void cris_initialize_tcg(void) for (i = 0; i < 16; i++) { cpu_R[i] = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, regs[i]), - regnames[i]); + regnames_v32[i]); } for (i = 0; i < 16; i++) { cpu_PR[i] = tcg_global_mem_new(cpu_env, offsetof(CPUCRISState, pregs[i]), - pregnames[i]); + pregnames_v32[i]); } } diff --git a/target-cris/translate_v10.c b/target-cris/translate_v10.c index 4707a18e77..4a0b485d8e 100644 --- a/target-cris/translate_v10.c +++ b/target-cris/translate_v10.c @@ -1094,6 +1094,29 @@ static unsigned int dec10_ind(CPUCRISState *env, DisasContext *dc) insn_len = dec10_bdap_m(env, dc, size); break; default: + /* + * ADDC for v17: + * + * Instruction format: ADDC [Rs],Rd + * + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+ + * |Destination(Rd)| 1 0 0 1 1 0 1 0 | Source(Rs)| + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+--+--+ + * + * Instruction format: ADDC [Rs+],Rd + * + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+ + * |Destination(Rd)| 1 1 0 1 1 0 1 0 | Source(Rs)| + * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-+ + */ + if (dc->opcode == CRISV17_IND_ADDC && dc->size == 2 && + env->pregs[PR_VR] == 17) { + LOG_DIS("addc op=%d %d\n", dc->src, dc->dst); + cris_cc_mask(dc, CC_MASK_NZVC); + insn_len += dec10_ind_alu(env, dc, CC_OP_ADDC, size); + break; + } + LOG_DIS("pc=%x var-ind.%d %d r%d r%d\n", dc->pc, size, dc->opcode, dc->src, dc->dst); cpu_abort(CPU(dc->cpu), "Unhandled opcode"); diff --git a/target-i386/cpu.c b/target-i386/cpu.c index db12728abf..333309b9a7 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -181,185 +181,6 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, dst[CPUID_VENDOR_SZ] = '\0'; } -/* feature flags taken from "Intel Processor Identification and the CPUID - * Instruction" and AMD's "CPUID Specification". In cases of disagreement - * between feature naming conventions, aliases may be added. - */ -static const char *feature_name[] = { - "fpu", "vme", "de", "pse", - "tsc", "msr", "pae", "mce", - "cx8", "apic", NULL, "sep", - "mtrr", "pge", "mca", "cmov", - "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, - NULL, "ds" /* Intel dts */, "acpi", "mmx", - "fxsr", "sse", "sse2", "ss", - "ht" /* Intel htt */, "tm", "ia64", "pbe", -}; -static const char *ext_feature_name[] = { - "pni|sse3" /* Intel,AMD sse3 */, "pclmulqdq|pclmuldq", "dtes64", "monitor", - "ds_cpl", "vmx", "smx", "est", - "tm2", "ssse3", "cid", NULL, - "fma", "cx16", "xtpr", "pdcm", - NULL, "pcid", "dca", "sse4.1|sse4_1", - "sse4.2|sse4_2", "x2apic", "movbe", "popcnt", - "tsc-deadline", "aes", "xsave", "osxsave", - "avx", "f16c", "rdrand", "hypervisor", -}; -/* Feature names that are already defined on feature_name[] but are set on - * CPUID[8000_0001].EDX on AMD CPUs don't have their names on - * ext2_feature_name[]. They are copied automatically to cpuid_ext2_features - * if and only if CPU vendor is AMD. - */ -static const char *ext2_feature_name[] = { - NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, - NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, - NULL /* cx8 */ /* AMD CMPXCHG8B */, NULL /* apic */, NULL, "syscall", - NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */, - NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */, - "nx|xd", NULL, "mmxext", NULL /* mmx */, - NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb" /* AMD Page1GB */, "rdtscp", - NULL, "lm|i64", "3dnowext", "3dnow", -}; -static const char *ext3_feature_name[] = { - "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD ExtApicSpace */, - "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse", - "3dnowprefetch", "osvw", "ibs", "xop", - "skinit", "wdt", NULL, "lwp", - "fma4", "tce", NULL, "nodeid_msr", - NULL, "tbm", "topoext", "perfctr_core", - "perfctr_nb", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *ext4_feature_name[] = { - NULL, NULL, "xstore", "xstore-en", - NULL, NULL, "xcrypt", "xcrypt-en", - "ace2", "ace2-en", "phe", "phe-en", - "pmm", "pmm-en", NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *kvm_feature_name[] = { - "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", - "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", "kvm_pv_unhalt", - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - "kvmclock-stable-bit", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *hyperv_priv_feature_name[] = { - NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */, - NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */, - NULL /* hv_msr_apic_access */, NULL /* hv_msr_hypercall_access */, - NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */, - NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */, - NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *hyperv_ident_feature_name[] = { - NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */, - NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */, - NULL /* hv_post_messages */, NULL /* hv_signal_events */, - NULL /* hv_create_port */, NULL /* hv_connect_port */, - NULL /* hv_access_stats */, NULL, NULL, NULL /* hv_debugging */, - NULL /* hv_cpu_power_management */, NULL /* hv_configure_profiler */, - NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *hyperv_misc_feature_name[] = { - NULL /* hv_mwait */, NULL /* hv_guest_debugging */, - NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */, - NULL /* hv_hypercall_params_xmm */, NULL /* hv_guest_idle_state */, - NULL, NULL, - NULL, NULL, NULL /* hv_guest_crash_msr */, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *svm_feature_name[] = { - "npt", "lbrv", "svm_lock", "nrip_save", - "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists", - NULL, NULL, "pause_filter", NULL, - "pfthreshold", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *cpuid_7_0_ebx_feature_name[] = { - "fsgsbase", "tsc_adjust", NULL, "bmi1", - "hle", "avx2", NULL, "smep", - "bmi2", "erms", "invpcid", "rtm", - NULL, NULL, "mpx", NULL, - "avx512f", "avx512dq", "rdseed", "adx", - "smap", "avx512ifma", "pcommit", "clflushopt", - "clwb", NULL, "avx512pf", "avx512er", - "avx512cd", NULL, "avx512bw", "avx512vl", -}; - -static const char *cpuid_7_0_ecx_feature_name[] = { - NULL, "avx512vbmi", "umip", "pku", - "ospke", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, "rdpid", NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *cpuid_apm_edx_feature_name[] = { - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - "invtsc", NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *cpuid_xsave_feature_name[] = { - "xsaveopt", "xsavec", "xgetbv1", "xsaves", - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - -static const char *cpuid_6_feature_name[] = { - NULL, NULL, "arat", NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -}; - #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE) #define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \ CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC) @@ -425,7 +246,12 @@ static const char *cpuid_6_feature_name[] = { CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ typedef struct FeatureWordInfo { - const char **feat_names; + /* feature flags names are taken from "Intel Processor Identification and + * the CPUID Instruction" and AMD's "CPUID Specification". + * In cases of disagreement between feature naming conventions, + * aliases may be added. + */ + const char *feat_names[32]; uint32_t cpuid_eax; /* Input EAX for CPUID */ bool cpuid_needs_ecx; /* CPUID instruction uses ECX as input */ uint32_t cpuid_ecx; /* Input ECX value for CPUID */ @@ -436,85 +262,245 @@ typedef struct FeatureWordInfo { static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { - .feat_names = feature_name, + .feat_names = { + "fpu", "vme", "de", "pse", + "tsc", "msr", "pae", "mce", + "cx8", "apic", NULL, "sep", + "mtrr", "pge", "mca", "cmov", + "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, + NULL, "ds" /* Intel dts */, "acpi", "mmx", + "fxsr", "sse", "sse2", "ss", + "ht" /* Intel htt */, "tm", "ia64", "pbe", + }, .cpuid_eax = 1, .cpuid_reg = R_EDX, .tcg_features = TCG_FEATURES, }, [FEAT_1_ECX] = { - .feat_names = ext_feature_name, + .feat_names = { + "pni|sse3" /* Intel,AMD sse3 */, "pclmulqdq|pclmuldq", "dtes64", "monitor", + "ds_cpl", "vmx", "smx", "est", + "tm2", "ssse3", "cid", NULL, + "fma", "cx16", "xtpr", "pdcm", + NULL, "pcid", "dca", "sse4.1|sse4_1", + "sse4.2|sse4_2", "x2apic", "movbe", "popcnt", + "tsc-deadline", "aes", "xsave", "osxsave", + "avx", "f16c", "rdrand", "hypervisor", + }, .cpuid_eax = 1, .cpuid_reg = R_ECX, .tcg_features = TCG_EXT_FEATURES, }, + /* Feature names that are already defined on feature_name[] but + * are set on CPUID[8000_0001].EDX on AMD CPUs don't have their + * names on feat_names below. They are copied automatically + * to features[FEAT_8000_0001_EDX] if and only if CPU vendor is AMD. + */ [FEAT_8000_0001_EDX] = { - .feat_names = ext2_feature_name, + .feat_names = { + NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, + NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, + NULL /* cx8 */, NULL /* apic */, NULL, "syscall", + NULL /* mtrr */, NULL /* pge */, NULL /* mca */, NULL /* cmov */, + NULL /* pat */, NULL /* pse36 */, NULL, NULL /* Linux mp */, + "nx|xd", NULL, "mmxext", NULL /* mmx */, + NULL /* fxsr */, "fxsr_opt|ffxsr", "pdpe1gb", "rdtscp", + NULL, "lm|i64", "3dnowext", "3dnow", + }, .cpuid_eax = 0x80000001, .cpuid_reg = R_EDX, .tcg_features = TCG_EXT2_FEATURES, }, [FEAT_8000_0001_ECX] = { - .feat_names = ext3_feature_name, + .feat_names = { + "lahf_lm", "cmp_legacy", "svm", "extapic", + "cr8legacy", "abm", "sse4a", "misalignsse", + "3dnowprefetch", "osvw", "ibs", "xop", + "skinit", "wdt", NULL, "lwp", + "fma4", "tce", NULL, "nodeid_msr", + NULL, "tbm", "topoext", "perfctr_core", + "perfctr_nb", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0x80000001, .cpuid_reg = R_ECX, .tcg_features = TCG_EXT3_FEATURES, }, [FEAT_C000_0001_EDX] = { - .feat_names = ext4_feature_name, + .feat_names = { + NULL, NULL, "xstore", "xstore-en", + NULL, NULL, "xcrypt", "xcrypt-en", + "ace2", "ace2-en", "phe", "phe-en", + "pmm", "pmm-en", NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0xC0000001, .cpuid_reg = R_EDX, .tcg_features = TCG_EXT4_FEATURES, }, [FEAT_KVM] = { - .feat_names = kvm_feature_name, + .feat_names = { + "kvmclock", "kvm_nopiodelay", "kvm_mmu", "kvmclock", + "kvm_asyncpf", "kvm_steal_time", "kvm_pv_eoi", "kvm_pv_unhalt", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + "kvmclock-stable-bit", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, .tcg_features = TCG_KVM_FEATURES, }, [FEAT_HYPERV_EAX] = { - .feat_names = hyperv_priv_feature_name, + .feat_names = { + NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */, + NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */, + NULL /* hv_msr_apic_access */, NULL /* hv_msr_hypercall_access */, + NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */, + NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */, + NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0x40000003, .cpuid_reg = R_EAX, }, [FEAT_HYPERV_EBX] = { - .feat_names = hyperv_ident_feature_name, + .feat_names = { + NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */, + NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */, + NULL /* hv_post_messages */, NULL /* hv_signal_events */, + NULL /* hv_create_port */, NULL /* hv_connect_port */, + NULL /* hv_access_stats */, NULL, NULL, NULL /* hv_debugging */, + NULL /* hv_cpu_power_management */, NULL /* hv_configure_profiler */, + NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0x40000003, .cpuid_reg = R_EBX, }, [FEAT_HYPERV_EDX] = { - .feat_names = hyperv_misc_feature_name, + .feat_names = { + NULL /* hv_mwait */, NULL /* hv_guest_debugging */, + NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */, + NULL /* hv_hypercall_params_xmm */, NULL /* hv_guest_idle_state */, + NULL, NULL, + NULL, NULL, NULL /* hv_guest_crash_msr */, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0x40000003, .cpuid_reg = R_EDX, }, [FEAT_SVM] = { - .feat_names = svm_feature_name, + .feat_names = { + "npt", "lbrv", "svm_lock", "nrip_save", + "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists", + NULL, NULL, "pause_filter", NULL, + "pfthreshold", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, .tcg_features = TCG_SVM_FEATURES, }, [FEAT_7_0_EBX] = { - .feat_names = cpuid_7_0_ebx_feature_name, + .feat_names = { + "fsgsbase", "tsc_adjust", NULL, "bmi1", + "hle", "avx2", NULL, "smep", + "bmi2", "erms", "invpcid", "rtm", + NULL, NULL, "mpx", NULL, + "avx512f", "avx512dq", "rdseed", "adx", + "smap", "avx512ifma", "pcommit", "clflushopt", + "clwb", NULL, "avx512pf", "avx512er", + "avx512cd", NULL, "avx512bw", "avx512vl", + }, .cpuid_eax = 7, .cpuid_needs_ecx = true, .cpuid_ecx = 0, .cpuid_reg = R_EBX, .tcg_features = TCG_7_0_EBX_FEATURES, }, [FEAT_7_0_ECX] = { - .feat_names = cpuid_7_0_ecx_feature_name, + .feat_names = { + NULL, "avx512vbmi", "umip", "pku", + "ospke", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, "rdpid", NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 7, .cpuid_needs_ecx = true, .cpuid_ecx = 0, .cpuid_reg = R_ECX, .tcg_features = TCG_7_0_ECX_FEATURES, }, [FEAT_8000_0007_EDX] = { - .feat_names = cpuid_apm_edx_feature_name, + .feat_names = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + "invtsc", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0x80000007, .cpuid_reg = R_EDX, .tcg_features = TCG_APM_FEATURES, .unmigratable_flags = CPUID_APM_INVTSC, }, [FEAT_XSAVE] = { - .feat_names = cpuid_xsave_feature_name, + .feat_names = { + "xsaveopt", "xsavec", "xgetbv1", "xsaves", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 0xd, .cpuid_needs_ecx = true, .cpuid_ecx = 1, .cpuid_reg = R_EAX, .tcg_features = TCG_XSAVE_FEATURES, }, [FEAT_6_EAX] = { - .feat_names = cpuid_6_feature_name, + .feat_names = { + NULL, NULL, "arat", NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, .cpuid_eax = 6, .cpuid_reg = R_EAX, .tcg_features = TCG_6_EAX_FEATURES, }, + [FEAT_XSAVE_COMP_LO] = { + .cpuid_eax = 0xD, + .cpuid_needs_ecx = true, .cpuid_ecx = 0, + .cpuid_reg = R_EAX, + .tcg_features = ~0U, + }, + [FEAT_XSAVE_COMP_HI] = { + .cpuid_eax = 0xD, + .cpuid_needs_ecx = true, .cpuid_ecx = 0, + .cpuid_reg = R_EDX, + .tcg_features = ~0U, + }, }; typedef struct X86RegisterInfo32 { @@ -574,6 +560,26 @@ static const ExtSaveArea x86_ext_save_areas[] = { .size = sizeof(XSavePKRU) }, }; +static uint32_t xsave_area_size(uint64_t mask) +{ + int i; + uint64_t ret = sizeof(X86LegacyXSaveArea) + sizeof(X86XSaveHeader); + + for (i = 2; i < ARRAY_SIZE(x86_ext_save_areas); i++) { + const ExtSaveArea *esa = &x86_ext_save_areas[i]; + if ((mask >> i) & 1) { + ret = MAX(ret, esa->offset + esa->size); + } + } + return ret; +} + +static inline uint64_t x86_cpu_xsave_components(X86CPU *cpu) +{ + return ((uint64_t)cpu->env.features[FEAT_XSAVE_COMP_HI]) << 32 | + cpu->env.features[FEAT_XSAVE_COMP_LO]; +} + const char *get_register_name_32(unsigned int reg) { if (reg >= CPU_NB_REGS32) { @@ -711,8 +717,7 @@ static void add_flagname_to_bitmaps(const char *flagname, FeatureWord w; for (w = 0; w < FEATURE_WORDS; w++) { FeatureWordInfo *wi = &feature_word_info[w]; - if (wi->feat_names && - lookup_feature(&words[w], flagname, NULL, wi->feat_names)) { + if (lookup_feature(&words[w], flagname, NULL, wi->feat_names)) { break; } } @@ -761,7 +766,6 @@ struct X86CPUDefinition { const char *name; uint32_t level; uint32_t xlevel; - uint32_t xlevel2; /* vendor is zero-terminated, 12 character ASCII string */ char vendor[CPUID_VENDOR_SZ + 1]; int family; @@ -1644,9 +1648,12 @@ static void host_x86_cpu_initfn(Object *obj) /* If KVM is disabled, x86_cpu_realizefn() will report an error later */ if (kvm_enabled()) { - env->cpuid_level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); - env->cpuid_xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); - env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); + env->cpuid_min_level = + kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); + env->cpuid_min_xlevel = + kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); + env->cpuid_min_xlevel2 = + kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); if (lmce_supported()) { object_property_set_bool(OBJECT(cpu), true, "lmce", &error_abort); @@ -2209,12 +2216,13 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) char host_vendor[CPUID_VENDOR_SZ + 1]; FeatureWord w; - object_property_set_int(OBJECT(cpu), def->level, "level", errp); + /* CPU models only set _minimum_ values for level/xlevel: */ + object_property_set_int(OBJECT(cpu), def->level, "min-level", errp); + object_property_set_int(OBJECT(cpu), def->xlevel, "min-xlevel", errp); + object_property_set_int(OBJECT(cpu), def->family, "family", errp); object_property_set_int(OBJECT(cpu), def->model, "model", errp); object_property_set_int(OBJECT(cpu), def->stepping, "stepping", errp); - object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", errp); - object_property_set_int(OBJECT(cpu), def->xlevel2, "xlevel2", errp); object_property_set_str(OBJECT(cpu), def->model_id, "model-id", errp); for (w = 0; w < FEATURE_WORDS; w++) { env->features[w] = def->features[w]; @@ -2495,13 +2503,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: - *eax = apicid_core_offset(smp_cores, smp_threads); - *ebx = smp_threads; + *eax = apicid_core_offset(cs->nr_cores, cs->nr_threads); + *ebx = cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; break; case 1: - *eax = apicid_pkg_offset(smp_cores, smp_threads); - *ebx = smp_cores * smp_threads; + *eax = apicid_pkg_offset(cs->nr_cores, cs->nr_threads); + *ebx = cs->nr_cores * cs->nr_threads; *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; break; default: @@ -2514,10 +2522,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx &= 0xffff; /* The count doesn't need to be reliable. */ break; case 0xD: { - KVMState *s = cs->kvm_state; - uint64_t ena_mask; - int i; - /* Processor Extended State */ *eax = 0; *ebx = 0; @@ -2526,36 +2530,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { break; } - 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(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 |= 1u << i; - } else { - *edx |= 1u << (i - 32); - } - *ecx = MAX(*ecx, esa->offset + esa->size); - } - } - *eax |= ena_mask & (XSTATE_FP_MASK | XSTATE_SSE_MASK); + *ecx = xsave_area_size(x86_cpu_xsave_components(cpu)); + *eax = env->features[FEAT_XSAVE_COMP_LO]; + *edx = env->features[FEAT_XSAVE_COMP_HI]; *ebx = *ecx; } else if (count == 1) { *eax = env->features[FEAT_XSAVE]; } 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) { + if ((x86_cpu_xsave_components(cpu) >> count) & 1) { + const ExtSaveArea *esa = &x86_ext_save_areas[count]; *eax = esa->size; *ebx = esa->offset; } @@ -2716,7 +2701,7 @@ static void x86_cpu_reset(CPUState *s) xcc->parent_reset(s); - memset(env, 0, offsetof(CPUX86State, cpuid_level)); + memset(env, 0, offsetof(CPUX86State, end_reset_fields)); tlb_flush(s, 1); @@ -2790,7 +2775,7 @@ static void x86_cpu_reset(CPUState *s) } 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) { + if (env->features[esa->feature] & esa->bits) { xcr0 |= 1ull << i; } } @@ -2953,6 +2938,61 @@ static uint32_t x86_host_phys_bits(void) return host_phys_bits; } +static void x86_cpu_adjust_level(X86CPU *cpu, uint32_t *min, uint32_t value) +{ + if (*min < value) { + *min = value; + } +} + +/* Increase cpuid_min_{level,xlevel,xlevel2} automatically, if appropriate */ +static void x86_cpu_adjust_feat_level(X86CPU *cpu, FeatureWord w) +{ + CPUX86State *env = &cpu->env; + FeatureWordInfo *fi = &feature_word_info[w]; + uint32_t eax = fi->cpuid_eax; + uint32_t region = eax & 0xF0000000; + + if (!env->features[w]) { + return; + } + + switch (region) { + case 0x00000000: + x86_cpu_adjust_level(cpu, &env->cpuid_min_level, eax); + break; + case 0x80000000: + x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, eax); + break; + case 0xC0000000: + x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel2, eax); + break; + } +} + +/* Calculate XSAVE components based on the configured CPU feature flags */ +static void x86_cpu_enable_xsave_components(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + int i; + uint64_t mask; + + if (!(env->features[FEAT_1_ECX] & CPUID_EXT_XSAVE)) { + return; + } + + mask = (XSTATE_FP_MASK | XSTATE_SSE_MASK); + 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) { + mask |= (1ULL << i); + } + } + + env->features[FEAT_XSAVE_COMP_LO] = mask; + env->features[FEAT_XSAVE_COMP_HI] = mask >> 32; +} + #define IS_INTEL_CPU(env) ((env)->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && \ (env)->cpuid_vendor2 == CPUID_VENDOR_INTEL_2 && \ (env)->cpuid_vendor3 == CPUID_VENDOR_INTEL_3) @@ -2998,8 +3038,40 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) cpu->env.features[w] &= ~minus_features[w]; } - if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) { - env->cpuid_level = 7; + if (!kvm_enabled() || !cpu->expose_kvm) { + env->features[FEAT_KVM] = 0; + } + + x86_cpu_enable_xsave_components(cpu); + + /* CPUID[EAX=7,ECX=0].EBX always increased level automatically: */ + x86_cpu_adjust_feat_level(cpu, FEAT_7_0_EBX); + if (cpu->full_cpuid_auto_level) { + x86_cpu_adjust_feat_level(cpu, FEAT_1_EDX); + x86_cpu_adjust_feat_level(cpu, FEAT_1_ECX); + x86_cpu_adjust_feat_level(cpu, FEAT_6_EAX); + x86_cpu_adjust_feat_level(cpu, FEAT_7_0_ECX); + x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX); + x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_ECX); + x86_cpu_adjust_feat_level(cpu, FEAT_8000_0007_EDX); + x86_cpu_adjust_feat_level(cpu, FEAT_C000_0001_EDX); + x86_cpu_adjust_feat_level(cpu, FEAT_SVM); + x86_cpu_adjust_feat_level(cpu, FEAT_XSAVE); + /* SVM requires CPUID[0x8000000A] */ + if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { + x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A); + } + } + + /* Set cpuid_*level* based on cpuid_min_*level, if not explicitly set */ + if (env->cpuid_level == UINT32_MAX) { + env->cpuid_level = env->cpuid_min_level; + } + if (env->cpuid_xlevel == UINT32_MAX) { + env->cpuid_xlevel = env->cpuid_min_xlevel; + } + if (env->cpuid_xlevel2 == UINT32_MAX) { + env->cpuid_xlevel2 = env->cpuid_min_xlevel2; } if (x86_cpu_filter_features(cpu) && cpu->enforce_cpuid) { @@ -3262,9 +3334,6 @@ static void x86_cpu_register_feature_bit_props(X86CPU *cpu, char **names; FeatureWordInfo *fi = &feature_word_info[w]; - if (!fi->feat_names) { - return; - } if (!fi->feat_names[bitnr]) { return; } @@ -3405,9 +3474,13 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("phys-bits", X86CPU, phys_bits, 0), DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), - DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0), - DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0), - DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0), + DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, UINT32_MAX), + DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, UINT32_MAX), + DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, UINT32_MAX), + DEFINE_PROP_UINT32("min-level", X86CPU, env.cpuid_min_level, 0), + DEFINE_PROP_UINT32("min-xlevel", X86CPU, env.cpuid_min_xlevel, 0), + DEFINE_PROP_UINT32("min-xlevel2", X86CPU, env.cpuid_min_xlevel2, 0), + DEFINE_PROP_BOOL("full-cpuid-auto-level", X86CPU, full_cpuid_auto_level, true), DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 27af9c3f9a..1cb32ae456 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -453,6 +453,8 @@ typedef enum FeatureWord { FEAT_SVM, /* CPUID[8000_000A].EDX */ FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */ FEAT_6_EAX, /* CPUID[6].EAX */ + FEAT_XSAVE_COMP_LO, /* CPUID[EAX=0xd,ECX=0].EAX */ + FEAT_XSAVE_COMP_HI, /* CPUID[EAX=0xd,ECX=0].EDX */ FEATURE_WORDS, } FeatureWord; @@ -1108,11 +1110,15 @@ typedef struct CPUX86State { CPU_COMMON /* Fields from here on are preserved across CPU reset. */ + struct {} end_reset_fields; /* processor features (e.g. for CPUID insn) */ - uint32_t cpuid_level; - uint32_t cpuid_xlevel; - uint32_t cpuid_xlevel2; + /* Minimum level/xlevel/xlevel2, based on CPU model + features */ + uint32_t cpuid_min_level, cpuid_min_xlevel, cpuid_min_xlevel2; + /* Maximum level/xlevel/xlevel2 value for auto-assignment: */ + uint32_t cpuid_max_level, cpuid_max_xlevel, cpuid_max_xlevel2; + /* Actual level/xlevel/xlevel2 value: */ + uint32_t cpuid_level, cpuid_xlevel, cpuid_xlevel2; uint32_t cpuid_vendor1; uint32_t cpuid_vendor2; uint32_t cpuid_vendor3; @@ -1217,6 +1223,9 @@ struct X86CPU { /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; + /* Enable auto level-increase for all CPUID leaves */ + bool full_cpuid_auto_level; + /* if true fill the top bits of the MTRR_PHYSMASKn variable range */ bool fill_mtrr_mask; diff --git a/target-i386/kvm.c b/target-i386/kvm.c index 1955a6b3a4..ee1f53e569 100644 --- a/target-i386/kvm.c +++ b/target-i386/kvm.c @@ -83,23 +83,17 @@ static bool has_msr_tsc_aux; static bool has_msr_tsc_adjust; static bool has_msr_tsc_deadline; static bool has_msr_feature_control; -static bool has_msr_async_pf_en; -static bool has_msr_pv_eoi_en; static bool has_msr_misc_enable; static bool has_msr_smbase; static bool has_msr_bndcfgs; -static bool has_msr_kvm_steal_time; static int lm_capable_kernel; static bool has_msr_hv_hypercall; -static bool has_msr_hv_vapic; -static bool has_msr_hv_tsc; static bool has_msr_hv_crash; static bool has_msr_hv_reset; static bool has_msr_hv_vpindex; static bool has_msr_hv_runtime; static bool has_msr_hv_synic; static bool has_msr_hv_stimer; -static bool has_msr_mtrr; static bool has_msr_xss; static bool has_msr_architectural_pmu; @@ -602,20 +596,22 @@ static int hyperv_handle_properties(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + if (cpu->hyperv_time && + kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) <= 0) { + cpu->hyperv_time = false; + } + if (cpu->hyperv_relaxed_timing) { env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE; } if (cpu->hyperv_vapic) { env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE; env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_APIC_ACCESS_AVAILABLE; - has_msr_hv_vapic = true; } - if (cpu->hyperv_time && - kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) { + if (cpu->hyperv_time) { env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE; env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE; env->features[FEAT_HYPERV_EAX] |= 0x200; - has_msr_hv_tsc = true; } if (cpu->hyperv_crash && has_msr_hv_crash) { env->features[FEAT_HYPERV_EDX] |= HV_X64_GUEST_CRASH_MSR_AVAILABLE; @@ -727,7 +723,7 @@ int kvm_arch_init_vcpu(CPUState *cs) if (cpu->hyperv_relaxed_timing) { c->eax |= HV_X64_RELAXED_TIMING_RECOMMENDED; } - if (has_msr_hv_vapic) { + if (cpu->hyperv_vapic) { c->eax |= HV_X64_APIC_ACCESS_RECOMMENDED; } c->ebx = cpu->hyperv_spinlock_attempts; @@ -753,12 +749,6 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; c->function = KVM_CPUID_FEATURES | kvm_base; c->eax = env->features[FEAT_KVM]; - - has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF); - - has_msr_pv_eoi_en = c->eax & (1 << KVM_FEATURE_PV_EOI); - - has_msr_kvm_steal_time = c->eax & (1 << KVM_FEATURE_STEAL_TIME); } cpu_x86_cpuid(env, 0, 0, &limit, &unused, &unused, &unused); @@ -973,9 +963,6 @@ int kvm_arch_init_vcpu(CPUState *cs) } cpu->kvm_msr_buf = g_malloc0(MSR_BUF_SIZE); - if (env->features[FEAT_1_EDX] & CPUID_MTRR) { - has_msr_mtrr = true; - } if (!(env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_RDTSCP)) { has_msr_tsc_aux = false; } @@ -1641,13 +1628,13 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, MSR_IA32_TSC, env->tsc); kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, env->system_time_msr); kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, env->wall_clock_msr); - if (has_msr_async_pf_en) { + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, env->async_pf_en_msr); } - if (has_msr_pv_eoi_en) { + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, env->pv_eoi_en_msr); } - if (has_msr_kvm_steal_time) { + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, env->steal_time_msr); } if (has_msr_architectural_pmu) { @@ -1683,11 +1670,11 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, HV_X64_MSR_HYPERCALL, env->msr_hv_hypercall); } - if (has_msr_hv_vapic) { + if (cpu->hyperv_vapic) { kvm_msr_entry_add(cpu, HV_X64_MSR_APIC_ASSIST_PAGE, env->msr_hv_vapic); } - if (has_msr_hv_tsc) { + if (cpu->hyperv_time) { kvm_msr_entry_add(cpu, HV_X64_MSR_REFERENCE_TSC, env->msr_hv_tsc); } if (has_msr_hv_crash) { @@ -1733,7 +1720,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level) env->msr_hv_stimer_count[j]); } } - if (has_msr_mtrr) { + if (env->features[FEAT_1_EDX] & CPUID_MTRR) { uint64_t phys_mask = MAKE_64BIT_MASK(0, cpu->phys_bits); kvm_msr_entry_add(cpu, MSR_MTRRdefType, env->mtrr_deftype); @@ -2050,13 +2037,13 @@ static int kvm_get_msrs(X86CPU *cpu) #endif kvm_msr_entry_add(cpu, MSR_KVM_SYSTEM_TIME, 0); kvm_msr_entry_add(cpu, MSR_KVM_WALL_CLOCK, 0); - if (has_msr_async_pf_en) { + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_ASYNC_PF)) { kvm_msr_entry_add(cpu, MSR_KVM_ASYNC_PF_EN, 0); } - if (has_msr_pv_eoi_en) { + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_PV_EOI)) { kvm_msr_entry_add(cpu, MSR_KVM_PV_EOI_EN, 0); } - if (has_msr_kvm_steal_time) { + if (env->features[FEAT_KVM] & (1 << KVM_FEATURE_STEAL_TIME)) { kvm_msr_entry_add(cpu, MSR_KVM_STEAL_TIME, 0); } if (has_msr_architectural_pmu) { @@ -2088,10 +2075,10 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, HV_X64_MSR_HYPERCALL, 0); kvm_msr_entry_add(cpu, HV_X64_MSR_GUEST_OS_ID, 0); } - if (has_msr_hv_vapic) { + if (cpu->hyperv_vapic) { kvm_msr_entry_add(cpu, HV_X64_MSR_APIC_ASSIST_PAGE, 0); } - if (has_msr_hv_tsc) { + if (cpu->hyperv_time) { kvm_msr_entry_add(cpu, HV_X64_MSR_REFERENCE_TSC, 0); } if (has_msr_hv_crash) { @@ -2123,7 +2110,7 @@ static int kvm_get_msrs(X86CPU *cpu) kvm_msr_entry_add(cpu, msr, 0); } } - if (has_msr_mtrr) { + if (env->features[FEAT_1_EDX] & CPUID_MTRR) { kvm_msr_entry_add(cpu, MSR_MTRRdefType, 0); kvm_msr_entry_add(cpu, MSR_MTRRfix64K_00000, 0); kvm_msr_entry_add(cpu, MSR_MTRRfix16K_80000, 0); diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c index 407ccb93a3..b66b40b82f 100644 --- a/target-ppc/translate_init.c +++ b/target-ppc/translate_init.c @@ -9943,7 +9943,8 @@ static void ppc_cpu_unrealizefn(DeviceState *dev, Error **errp) int ppc_get_compat_smt_threads(PowerPCCPU *cpu) { - int ret = MIN(smp_threads, kvmppc_smt_threads()); + CPUState *cs = CPU(cpu); + int ret = MIN(cs->nr_threads, kvmppc_smt_threads()); switch (cpu->cpu_version) { case CPU_POWERPC_LOGICAL_2_05: diff --git a/tests/.gitignore b/tests/.gitignore index 24ac6cfa77..0f0c79b1a9 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -75,6 +75,7 @@ test-visitor-serialization test-vmstate test-write-threshold test-x86-cpuid +test-x86-cpuid-compat test-xbzrle test-netfilter test-filter-mirror diff --git a/tests/Makefile.include b/tests/Makefile.include index 2aa78c1820..8162f6f735 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -243,6 +243,7 @@ check-qtest-i386-y += tests/test-netfilter$(EXESUF) check-qtest-i386-y += tests/test-filter-mirror$(EXESUF) check-qtest-i386-y += tests/test-filter-redirector$(EXESUF) check-qtest-i386-y += tests/postcopy-test$(EXESUF) +check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF) check-qtest-x86_64-y += $(check-qtest-i386-y) gcov-files-i386-y += i386-softmmu/hw/timer/mc146818rtc.c gcov-files-x86_64-y = $(subst i386-softmmu/,x86_64-softmmu/,$(gcov-files-i386-y)) @@ -663,6 +664,7 @@ tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-o tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y) tests/test-filter-mirror$(EXESUF): tests/test-filter-mirror.o $(qtest-obj-y) tests/test-filter-redirector$(EXESUF): tests/test-filter-redirector.o $(qtest-obj-y) +tests/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o hw/core/ptimer.o diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 37ff860c16..105bccecaa 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -147,7 +147,7 @@ void qvring_init(const QGuestAllocator *alloc, QVirtQueue *vq, uint64_t addr) for (i = 0; i < vq->size - 1; i++) { /* vq->desc[i].addr */ - writew(vq->desc + (16 * i), 0); + writeq(vq->desc + (16 * i), 0); /* vq->desc[i].next */ writew(vq->desc + (16 * i) + 14, i + 1); } diff --git a/tests/tcg/cris/Makefile b/tests/tcg/cris/Makefile index d34bfd8f7a..6b3dba446c 100644 --- a/tests/tcg/cris/Makefile +++ b/tests/tcg/cris/Makefile @@ -23,6 +23,7 @@ SYS = sys.o TESTCASES += check_abs.tst TESTCASES += check_addc.tst TESTCASES += check_addcm.tst +TESTCASES += check_addcv17.tst TESTCASES += check_addo.tst TESTCASES += check_addoq.tst TESTCASES += check_addi.tst @@ -108,14 +109,12 @@ TESTCASES += check_stat4.ctst TESTCASES += check_openpf1.ctst TESTCASES += check_openpf2.ctst TESTCASES += check_openpf3.ctst -TESTCASES += check_openpf4.ctst TESTCASES += check_openpf5.ctst TESTCASES += check_mapbrk.ctst TESTCASES += check_mmap1.ctst TESTCASES += check_mmap2.ctst TESTCASES += check_mmap3.ctst TESTCASES += check_sigalrm.ctst -TESTCASES += check_time1.ctst TESTCASES += check_time2.ctst TESTCASES += check_settls1.ctst @@ -136,13 +135,27 @@ all: build %.ctst: %.o $(CC) $(CFLAGS) $(LDLIBS) $< -o $@ + +sysv10.o: sys.c + $(CC) $(CFLAGS) -mcpu=v10 -c $< -o $@ + +crtv10.o: crt.s + $(AS) $(ASFLAGS) -mcpu=v10 -c $< -o $@ + +check_addcv17.tst: ASFLAGS += -mcpu=v10 +check_addcv17.tst: CRT := crtv10.o +check_addcv17.tst: SYS := sysv10.o +check_addcv17.tst: crtv10.o sysv10.o + build: $(CRT) $(SYS) $(TESTCASES) check: $(CRT) $(SYS) $(TESTCASES) @echo -e "\nQEMU simulator." for case in $(TESTCASES); do \ echo -n "$$case "; \ - $(SIM) ./$$case; \ + SIMARGS=; \ + case $$case in *v17*) SIMARGS="-cpu crisv17";; esac; \ + $(SIM) $$SIMARGS ./$$case; \ done check-g: $(CRT) $(SYS) $(TESTCASES) @echo -e "\nGDB simulator." @@ -152,4 +165,4 @@ check-g: $(CRT) $(SYS) $(TESTCASES) done clean: - $(RM) -fr $(TESTCASES) $(CRT) $(SYS) + $(RM) -fr $(TESTCASES) *.o diff --git a/tests/tcg/cris/check_abs.c b/tests/tcg/cris/check_abs.c index 9770a8d9ef..08b67b6ef0 100644 --- a/tests/tcg/cris/check_abs.c +++ b/tests/tcg/cris/check_abs.c @@ -4,14 +4,14 @@ #include "sys.h" #include "crisutils.h" -static inline int cris_abs(int n) +static always_inline int cris_abs(int n) { int r; asm ("abs\t%1, %0\n" : "=r" (r) : "r" (n)); return r; } -static inline void +static always_inline void verify_abs(int val, int res, const int n, const int z, const int v, const int c) { diff --git a/tests/tcg/cris/check_addc.c b/tests/tcg/cris/check_addc.c index facd1bea2d..fc3fb1faa8 100644 --- a/tests/tcg/cris/check_addc.c +++ b/tests/tcg/cris/check_addc.c @@ -4,7 +4,7 @@ #include "sys.h" #include "crisutils.h" -static inline int cris_addc(int a, const int b) +static always_inline int cris_addc(int a, const int b) { asm ("addc\t%1, %0\n" : "+r" (a) : "r" (b)); return a; diff --git a/tests/tcg/cris/check_addcm.c b/tests/tcg/cris/check_addcm.c index 7928bc9999..b355ba164f 100644 --- a/tests/tcg/cris/check_addcm.c +++ b/tests/tcg/cris/check_addcm.c @@ -5,14 +5,14 @@ #include "crisutils.h" /* need to avoid acr as source here. */ -static inline int cris_addc_m(int a, const int *b) +static always_inline int cris_addc_m(int a, const int *b) { asm volatile ("addc [%1], %0\n" : "+r" (a) : "r" (b)); return a; } /* 'b' is a crisv32 constrain to avoid postinc with $acr. */ -static inline int cris_addc_pi_m(int a, int **b) +static always_inline int cris_addc_pi_m(int a, int **b) { asm volatile ("addc [%1+], %0\n" : "+r" (a), "+b" (*b)); return a; diff --git a/tests/tcg/cris/check_addcv17.s b/tests/tcg/cris/check_addcv17.s new file mode 100644 index 0000000000..52ef7a9716 --- /dev/null +++ b/tests/tcg/cris/check_addcv17.s @@ -0,0 +1,65 @@ +# mach: crisv17 + + .include "testutils.inc" + + .macro addc Rs Rd inc=0 +# Create the instruction manually since there is no assembler support yet + .word (\Rd << 12) | \Rs | (\inc << 10) | 0x09a0 + .endm + + start + + .data +mem1: + .dword 0x0 +mem2: + .dword 0x12345678 + + .text + move.d mem1,r4 + clearf nzvc + addc 4 3 + test_cc 0 1 0 0 + checkr3 0 + + move.d mem1,r4 + clearf nzvc + ax + addc 4 3 + test_cc 0 0 0 0 + checkr3 0 + + move.d mem1,r4 + clearf nzvc + setf c + addc 4 3 + test_cc 0 0 0 0 + checkr3 1 + + move.d mem2,r4 + moveq 2, r3 + clearf nzvc + setf c + addc 4 3 + test_cc 0 0 0 0 + checkr3 1234567b + + move.d mem2,r5 + clearf nzvc + cmp.d r4,r5 + test_cc 0 1 0 0 + + move.d mem2,r4 + moveq 2, r3 + clearf nzvc + addc 4 3 inc=1 + test_cc 0 0 0 0 + checkr3 1234567a + + move.d mem2,r5 + clearf nzvc + addq 4,r5 + cmp.d r4,r5 + test_cc 0 1 0 0 + + quit diff --git a/tests/tcg/cris/check_bound.c b/tests/tcg/cris/check_bound.c index e8831754ec..d956ab9ade 100644 --- a/tests/tcg/cris/check_bound.c +++ b/tests/tcg/cris/check_bound.c @@ -4,21 +4,21 @@ #include "sys.h" #include "crisutils.h" -static inline int cris_bound_b(int v, int b) +static always_inline int cris_bound_b(int v, int b) { int r = v; asm ("bound.b\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; } -static inline int cris_bound_w(int v, int b) +static always_inline int cris_bound_w(int v, int b) { int r = v; asm ("bound.w\t%1, %0\n" : "+r" (r) : "ri" (b)); return r; } -static inline int cris_bound_d(int v, int b) +static always_inline int cris_bound_d(int v, int b) { int r = v; asm ("bound.d\t%1, %0\n" : "+r" (r) : "ri" (b)); diff --git a/tests/tcg/cris/check_ftag.c b/tests/tcg/cris/check_ftag.c index 908773a38a..aaa5c97115 100644 --- a/tests/tcg/cris/check_ftag.c +++ b/tests/tcg/cris/check_ftag.c @@ -4,22 +4,22 @@ #include "sys.h" #include "crisutils.h" -static inline void cris_ftag_i(unsigned int x) +static always_inline void cris_ftag_i(unsigned int x) { register unsigned int v asm("$r10") = x; asm ("ftagi\t[%0]\n" : : "r" (v) ); } -static inline void cris_ftag_d(unsigned int x) +static always_inline void cris_ftag_d(unsigned int x) { register unsigned int v asm("$r10") = x; asm ("ftagd\t[%0]\n" : : "r" (v) ); } -static inline void cris_fidx_i(unsigned int x) +static always_inline void cris_fidx_i(unsigned int x) { register unsigned int v asm("$r10") = x; asm ("fidxi\t[%0]\n" : : "r" (v) ); } -static inline void cris_fidx_d(unsigned int x) +static always_inline void cris_fidx_d(unsigned int x) { register unsigned int v asm("$r10") = x; asm ("fidxd\t[%0]\n" : : "r" (v) ); diff --git a/tests/tcg/cris/check_int64.c b/tests/tcg/cris/check_int64.c index fc600176e2..69caec1bb2 100644 --- a/tests/tcg/cris/check_int64.c +++ b/tests/tcg/cris/check_int64.c @@ -5,12 +5,12 @@ #include "crisutils.h" -static inline int64_t add64(const int64_t a, const int64_t b) +static always_inline int64_t add64(const int64_t a, const int64_t b) { return a + b; } -static inline int64_t sub64(const int64_t a, const int64_t b) +static always_inline int64_t sub64(const int64_t a, const int64_t b) { return a - b; } diff --git a/tests/tcg/cris/check_lz.c b/tests/tcg/cris/check_lz.c index 69c2e6d4ec..bf051a6b55 100644 --- a/tests/tcg/cris/check_lz.c +++ b/tests/tcg/cris/check_lz.c @@ -3,7 +3,7 @@ #include <stdint.h> #include "sys.h" -static inline int cris_lz(int x) +static always_inline int cris_lz(int x) { int r; asm ("lz\t%1, %0\n" : "=r" (r) : "r" (x)); diff --git a/tests/tcg/cris/check_openpf4.c b/tests/tcg/cris/check_openpf4.c deleted file mode 100644 index 8bbee41a64..0000000000 --- a/tests/tcg/cris/check_openpf4.c +++ /dev/null @@ -1,5 +0,0 @@ -/* Basic file operations, now *with* sysroot. -#sim: --sysroot=@exedir@ -*/ -#define PREFIX "/" -#include "check_openpf3.c" diff --git a/tests/tcg/cris/check_swap.c b/tests/tcg/cris/check_swap.c index f851cbcef1..9a68c1e5d7 100644 --- a/tests/tcg/cris/check_swap.c +++ b/tests/tcg/cris/check_swap.c @@ -9,7 +9,7 @@ #define B 2 #define R 1 -static inline int cris_swap(const int mode, int x) +static always_inline int cris_swap(const int mode, int x) { switch (mode) { diff --git a/tests/tcg/cris/check_time1.c b/tests/tcg/cris/check_time1.c deleted file mode 100644 index 3fcf0e1535..0000000000 --- a/tests/tcg/cris/check_time1.c +++ /dev/null @@ -1,46 +0,0 @@ -/* Basic time functionality test: check that milliseconds are - incremented for each syscall (does not work on host). */ -#include <stdio.h> -#include <time.h> -#include <sys/time.h> -#include <string.h> -#include <stdlib.h> - -void err (const char *s) -{ - perror (s); - abort (); -} - -int -main (void) -{ - struct timeval t_m = {0, 0}; - struct timezone t_z = {0, 0}; - struct timeval t_m1 = {0, 0}; - int i; - - if (gettimeofday (&t_m, &t_z) != 0) - err ("gettimeofday"); - - for (i = 1; i < 10000; i++) - if (gettimeofday (&t_m1, NULL) != 0) - err ("gettimeofday 1"); - else - if (t_m1.tv_sec * 1000000 + t_m1.tv_usec - != (t_m.tv_sec * 1000000 + t_m.tv_usec + i * 1000)) - { - fprintf (stderr, "t0 (%ld, %ld), i %d, t1 (%ld, %ld)\n", - t_m.tv_sec, t_m.tv_usec, i, t_m1.tv_sec, t_m1.tv_usec); - abort (); - } - - if (time (NULL) != t_m1.tv_sec) - { - fprintf (stderr, "time != gettod\n"); - abort (); - } - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/crisutils.h b/tests/tcg/cris/crisutils.h index 3456b9d50d..bbbe6c5540 100644 --- a/tests/tcg/cris/crisutils.h +++ b/tests/tcg/cris/crisutils.h @@ -13,57 +13,57 @@ void _err(void) { _fail(tst_cc_loc); } -static inline void cris_tst_cc_n1(void) +static always_inline void cris_tst_cc_n1(void) { asm volatile ("bpl _err\n" "nop\n"); } -static inline void cris_tst_cc_n0(void) +static always_inline void cris_tst_cc_n0(void) { asm volatile ("bmi _err\n" "nop\n"); } -static inline void cris_tst_cc_z1(void) +static always_inline void cris_tst_cc_z1(void) { asm volatile ("bne _err\n" "nop\n"); } -static inline void cris_tst_cc_z0(void) +static always_inline void cris_tst_cc_z0(void) { asm volatile ("beq _err\n" "nop\n"); } -static inline void cris_tst_cc_v1(void) +static always_inline void cris_tst_cc_v1(void) { asm volatile ("bvc _err\n" "nop\n"); } -static inline void cris_tst_cc_v0(void) +static always_inline void cris_tst_cc_v0(void) { asm volatile ("bvs _err\n" "nop\n"); } -static inline void cris_tst_cc_c1(void) +static always_inline void cris_tst_cc_c1(void) { asm volatile ("bcc _err\n" "nop\n"); } -static inline void cris_tst_cc_c0(void) +static always_inline void cris_tst_cc_c0(void) { asm volatile ("bcs _err\n" "nop\n"); } -static inline void cris_tst_mov_cc(int n, int z) +static always_inline void cris_tst_mov_cc(int n, int z) { if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); asm volatile ("" : : "g" (_err)); } -static inline void cris_tst_cc(const int n, const int z, +static always_inline void cris_tst_cc(const int n, const int z, const int v, const int c) { if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); diff --git a/tests/tcg/cris/sys.c b/tests/tcg/cris/sys.c index 551c5dd7cb..21f08c0747 100644 --- a/tests/tcg/cris/sys.c +++ b/tests/tcg/cris/sys.c @@ -33,19 +33,27 @@ void *memset (void *s, int c, size_t n) { } void exit (int status) { - asm volatile ("moveq 1, $r9\n" /* NR_exit. */ - "break 13\n"); + register unsigned int callno asm ("r9") = 1; /* NR_exit */ + + asm volatile ("break 13\n" + : + : "r" (callno) + : "memory" ); while(1) ; } ssize_t write (int fd, const void *buf, size_t count) { - int r; - asm ("move.d %0, $r10\n" - "move.d %1, $r11\n" - "move.d %2, $r12\n" - "moveq 4, $r9\n" /* NR_write. */ - "break 13\n" : : "r" (fd), "r" (buf), "r" (count) : "memory"); - asm ("move.d $r10, %0\n" : "=r" (r)); + register unsigned int callno asm ("r9") = 4; /* NR_write */ + register unsigned int r10 asm ("r10") = fd; + register const void *r11 asm ("r11") = buf; + register size_t r12 asm ("r12") = count; + register unsigned int r asm ("r10"); + + asm volatile ("break 13\n" + : "=r" (r) + : "r" (callno), "0" (r10), "r" (r11), "r" (r12) + : "memory"); + return r; } diff --git a/tests/tcg/cris/sys.h b/tests/tcg/cris/sys.h index c5f88e1a29..3dd47bb673 100644 --- a/tests/tcg/cris/sys.h +++ b/tests/tcg/cris/sys.h @@ -3,6 +3,8 @@ #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) +#define always_inline inline __attribute__((always_inline)) + #define CURRENT_LOCATION __FILE__ ":" TOSTRING(__LINE__) #define err() \ diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index 6431dd6d7c..abd97c23c1 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -53,6 +53,47 @@ static void test_self(void) } /* + * Check that qemu_coroutine_entered() works + */ + +static void coroutine_fn verify_entered_step_2(void *opaque) +{ + Coroutine *caller = (Coroutine *)opaque; + + g_assert(qemu_coroutine_entered(caller)); + g_assert(qemu_coroutine_entered(qemu_coroutine_self())); + qemu_coroutine_yield(); + + /* Once more to check it still works after yielding */ + g_assert(qemu_coroutine_entered(caller)); + g_assert(qemu_coroutine_entered(qemu_coroutine_self())); + qemu_coroutine_yield(); +} + +static void coroutine_fn verify_entered_step_1(void *opaque) +{ + Coroutine *self = qemu_coroutine_self(); + Coroutine *coroutine; + + g_assert(qemu_coroutine_entered(self)); + + coroutine = qemu_coroutine_create(verify_entered_step_2, self); + g_assert(!qemu_coroutine_entered(coroutine)); + qemu_coroutine_enter(coroutine); + g_assert(!qemu_coroutine_entered(coroutine)); + qemu_coroutine_enter(coroutine); +} + +static void test_entered(void) +{ + Coroutine *coroutine; + + coroutine = qemu_coroutine_create(verify_entered_step_1, NULL); + g_assert(!qemu_coroutine_entered(coroutine)); + qemu_coroutine_enter(coroutine); +} + +/* * Check that coroutines may nest multiple levels */ @@ -389,6 +430,7 @@ int main(int argc, char **argv) g_test_add_func("/basic/yield", test_yield); g_test_add_func("/basic/nesting", test_nesting); g_test_add_func("/basic/self", test_self); + g_test_add_func("/basic/entered", test_entered); g_test_add_func("/basic/in_coroutine", test_in_coroutine); g_test_add_func("/basic/order", test_order); if (g_test_perf()) { diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c new file mode 100644 index 0000000000..83162a44c0 --- /dev/null +++ b/tests/test-x86-cpuid-compat.c @@ -0,0 +1,171 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qint.h" +#include "libqtest.h" + +static char *get_cpu0_qom_path(void) +{ + QDict *resp; + QList *ret; + QDict *cpu0; + char *path; + + resp = qmp("{'execute': 'query-cpus', 'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + ret = qdict_get_qlist(resp, "return"); + + cpu0 = qobject_to_qdict(qlist_peek(ret)); + path = g_strdup(qdict_get_str(cpu0, "qom_path")); + QDECREF(resp); + return path; +} + +static QObject *qom_get(const char *path, const char *prop) +{ + QDict *resp = qmp("{ 'execute': 'qom-get'," + " 'arguments': { 'path': %s," + " 'property': %s } }", + path, prop); + QObject *ret = qdict_get(resp, "return"); + qobject_incref(ret); + QDECREF(resp); + return ret; +} + +typedef struct CpuidTestArgs { + const char *cmdline; + const char *property; + int64_t expected_value; +} CpuidTestArgs; + +static void test_cpuid_prop(const void *data) +{ + const CpuidTestArgs *args = data; + char *path; + QInt *value; + + qtest_start(args->cmdline); + path = get_cpu0_qom_path(); + value = qobject_to_qint(qom_get(path, args->property)); + g_assert_cmpint(qint_get_int(value), ==, args->expected_value); + qtest_end(); + + QDECREF(value); + g_free(path); +} + +static void add_cpuid_test(const char *name, const char *cmdline, + const char *property, int64_t expected_value) +{ + CpuidTestArgs *args = g_new0(CpuidTestArgs, 1); + args->cmdline = cmdline; + args->property = property; + args->expected_value = expected_value; + qtest_add_data_func(name, args, test_cpuid_prop); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* Original level values for CPU models: */ + add_cpuid_test("x86/cpuid/phenom/level", + "-cpu phenom", "level", 5); + add_cpuid_test("x86/cpuid/Conroe/level", + "-cpu Conroe", "level", 10); + add_cpuid_test("x86/cpuid/SandyBridge/level", + "-cpu SandyBridge", "level", 0xd); + add_cpuid_test("x86/cpuid/486/xlevel", + "-cpu 486", "xlevel", 0); + add_cpuid_test("x86/cpuid/core2duo/xlevel", + "-cpu core2duo", "xlevel", 0x80000008); + add_cpuid_test("x86/cpuid/phenom/xlevel", + "-cpu phenom", "xlevel", 0x8000001A); + add_cpuid_test("x86/cpuid/athlon/xlevel", + "-cpu athlon", "xlevel", 0x80000008); + + /* If level is not large enough, it should increase automatically: */ + /* CPUID[6].EAX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/arat", + "-cpu 486,+arat", "level", 6); + /* CPUID[EAX=7,ECX=0].EBX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/fsgsbase", + "-cpu phenom,+fsgsbase", "level", 7); + /* CPUID[EAX=7,ECX=0].ECX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/avx512vbmi", + "-cpu phenom,+avx512vbmi", "level", 7); + /* CPUID[EAX=0xd,ECX=1].EAX: */ + add_cpuid_test("x86/cpuid/auto-level/phenom/xsaveopt", + "-cpu phenom,+xsaveopt", "level", 0xd); + /* CPUID[8000_0001].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/3dnow", + "-cpu 486,+3dnow", "xlevel", 0x80000001); + /* CPUID[8000_0001].ECX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/sse4a", + "-cpu 486,+sse4a", "xlevel", 0x80000001); + /* CPUID[8000_0007].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/invtsc", + "-cpu 486,+invtsc", "xlevel", 0x80000007); + /* CPUID[8000_000A].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/npt", + "-cpu 486,+npt", "xlevel", 0x8000000A); + /* CPUID[C000_0001].EDX: */ + add_cpuid_test("x86/cpuid/auto-xlevel2/phenom/xstore", + "-cpu phenom,+xstore", "xlevel2", 0xC0000001); + /* SVM needs CPUID[0x8000000A] */ + add_cpuid_test("x86/cpuid/auto-xlevel/athlon/svm", + "-cpu athlon,+svm", "xlevel", 0x8000000A); + + + /* If level is already large enough, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-level/SandyBridge/multiple", + "-cpu SandyBridge,+arat,+fsgsbase,+avx512vbmi", + "level", 0xd); + /* If level is explicitly set, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-level/486/fixed/0xF", + "-cpu 486,level=0xF,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", + "level", 0xF); + add_cpuid_test("x86/cpuid/auto-level/486/fixed/2", + "-cpu 486,level=2,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", + "level", 2); + add_cpuid_test("x86/cpuid/auto-level/486/fixed/0", + "-cpu 486,level=0,+arat,+fsgsbase,+avx512vbmi,+xsaveopt", + "level", 0); + + /* if xlevel is already large enough, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-xlevel/phenom/3dnow", + "-cpu phenom,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0x8000001A); + /* If xlevel is explicitly set, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/80000002", + "-cpu 486,xlevel=0x80000002,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0x80000002); + add_cpuid_test("x86/cpuid/auto-xlevel/486/fixed/8000001A", + "-cpu 486,xlevel=0x8000001A,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0x8000001A); + add_cpuid_test("x86/cpuid/auto-xlevel/phenom/fixed/0", + "-cpu 486,xlevel=0,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0); + + /* if xlevel2 is already large enough, it shouldn't change: */ + add_cpuid_test("x86/cpuid/auto-xlevel2/486/fixed", + "-cpu 486,xlevel2=0xC0000002,+xstore", + "xlevel2", 0xC0000002); + + /* Check compatibility of old machine-types that didn't + * auto-increase level/xlevel/xlevel2: */ + + add_cpuid_test("x86/cpuid/auto-level/pc-2.7", + "-machine pc-i440fx-2.7 -cpu 486,+arat,+avx512vbmi,+xsaveopt", + "level", 1); + add_cpuid_test("x86/cpuid/auto-xlevel/pc-2.7", + "-machine pc-i440fx-2.7 -cpu 486,+3dnow,+sse4a,+invtsc,+npt,+svm", + "xlevel", 0); + add_cpuid_test("x86/cpuid/auto-xlevel2/pc-2.7", + "-machine pc-i440fx-2.7 -cpu 486,+xstore", + "xlevel2", 0); + + return g_test_run(); +} diff --git a/trace-events b/trace-events index 8d59631ae8..1cb9d37ce4 100644 --- a/trace-events +++ b/trace-events @@ -139,6 +139,22 @@ memory_region_subpage_write(int cpu_index, void *mr, uint64_t offset, uint64_t v memory_region_tb_read(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr %#"PRIx64" value %#"PRIx64" size %u" memory_region_tb_write(int cpu_index, uint64_t addr, uint64_t value, unsigned size) "cpu %d addr %#"PRIx64" value %#"PRIx64" size %u" +# net/colo.c +colo_proxy_main(const char *chr) ": %s" + +# net/colo-compare.c +colo_compare_main(const char *chr) ": %s" +colo_compare_udp_miscompare(const char *sta, int size) ": %s = %d" +colo_compare_icmp_miscompare(const char *sta, int size) ": %s = %d" +colo_compare_ip_info(int psize, const char *sta, const char *stb, int ssize, const char *stc, const char *std) "ppkt size = %d, ip_src = %s, ip_dst = %s, spkt size = %d, ip_src = %s, ip_dst = %s" +colo_old_packet_check_found(int64_t old_time) "%" PRId64 +colo_compare_miscompare(void) "" + +# net/filter-rewriter.c +colo_filter_rewriter_debug(void) "" +colo_filter_rewriter_pkt_info(const char *func, const char *src, const char *dst, uint32_t seq, uint32_t ack, uint32_t flag) "%s: src/dst: %s/%s p: seq/ack=%u/%u flags=%x\n" +colo_filter_rewriter_conn_offset(uint32_t offset) ": offset=%u\n" + ### Guest events, keep at bottom # @vaddr: Access' virtual address. diff --git a/ui/console.c b/ui/console.c index 3940762851..fa3e658edd 100644 --- a/ui/console.c +++ b/ui/console.c @@ -123,6 +123,7 @@ struct QemuConsole { DisplaySurface *surface; int dcls; DisplayChangeListener *gl; + bool gl_block; /* Graphic console state. */ Object *device; @@ -264,10 +265,10 @@ void graphic_hw_update(QemuConsole *con) void graphic_hw_gl_block(QemuConsole *con, bool block) { - if (!con) { - con = active_console; - } - if (con && con->hw_ops->gl_block) { + assert(con != NULL); + + con->gl_block = block; + if (con->hw_ops->gl_block) { con->hw_ops->gl_block(con->hw, block); } } @@ -1879,6 +1880,12 @@ bool qemu_console_is_fixedsize(QemuConsole *con) return con && (con->console_type != TEXT_CONSOLE); } +bool qemu_console_is_gl_blocked(QemuConsole *con) +{ + assert(con != NULL); + return con->gl_block; +} + char *qemu_console_get_label(QemuConsole *con) { if (con->console_type == GRAPHIC_CONSOLE) { @@ -2101,6 +2108,13 @@ void qemu_console_resize(QemuConsole *s, int width, int height) DisplaySurface *surface; assert(s->console_type == GRAPHIC_CONSOLE); + + if (s->surface && + pixman_image_get_width(s->surface->image) == width && + pixman_image_get_height(s->surface->image) == height) { + return; + } + surface = qemu_create_displaysurface(width, height); dpy_gfx_replace_surface(s, surface); } diff --git a/ui/spice-display.c b/ui/spice-display.c index 99132b69b6..5e6f78a219 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -850,6 +850,74 @@ static void qemu_spice_gl_block_timer(void *opaque) fprintf(stderr, "WARNING: spice: no gl-draw-done within one second\n"); } +static void spice_gl_refresh(DisplayChangeListener *dcl) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + uint64_t cookie; + + if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) { + return; + } + + graphic_hw_update(dcl->con); + if (ssd->gl_updates && ssd->have_surface) { + qemu_spice_gl_block(ssd, true); + cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); + spice_qxl_gl_draw_async(&ssd->qxl, 0, 0, + surface_width(ssd->ds), + surface_height(ssd->ds), + cookie); + ssd->gl_updates = 0; + } +} + +static void spice_gl_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + + surface_gl_update_texture(ssd->gls, ssd->ds, x, y, w, h); + ssd->gl_updates++; +} + +static void spice_gl_switch(DisplayChangeListener *dcl, + struct DisplaySurface *new_surface) +{ + SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + EGLint stride, fourcc; + int fd; + + if (ssd->ds) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + } + ssd->ds = new_surface; + if (ssd->ds) { + surface_gl_create_texture(ssd->gls, ssd->ds); + fd = egl_get_fd_for_texture(ssd->ds->texture, + &stride, &fourcc); + if (fd < 0) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + return; + } + + dprint(1, "%s: %dx%d (stride %d/%d, fourcc 0x%x)\n", __func__, + surface_width(ssd->ds), surface_height(ssd->ds), + surface_stride(ssd->ds), stride, fourcc); + + /* note: spice server will close the fd */ + spice_qxl_gl_scanout(&ssd->qxl, fd, + surface_width(ssd->ds), + surface_height(ssd->ds), + stride, fourcc, false); + ssd->have_surface = true; + ssd->have_scanout = false; + + qemu_spice_gl_monitor_config(ssd, 0, 0, + surface_width(ssd->ds), + surface_height(ssd->ds)); + } +} + static QEMUGLContext qemu_spice_gl_create_context(DisplayChangeListener *dcl, QEMUGLParams *params) { @@ -887,6 +955,8 @@ static void qemu_spice_gl_scanout(DisplayChangeListener *dcl, /* note: spice server will close the fd */ spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, stride, fourcc, y_0_top); + ssd->have_surface = false; + ssd->have_scanout = (tex_id != 0); qemu_spice_gl_monitor_config(ssd, x, y, w, h); } @@ -897,6 +967,10 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); uint64_t cookie; + if (!ssd->have_scanout) { + return; + } + dprint(2, "%s: %dx%d+%d+%d\n", __func__, w, h, x, y); qemu_spice_gl_block(ssd, true); cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); @@ -904,13 +978,13 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, } static const DisplayChangeListenerOps display_listener_gl_ops = { - .dpy_name = "spice-egl", - .dpy_gfx_update = display_update, - .dpy_gfx_switch = display_switch, - .dpy_gfx_check_format = qemu_pixman_check_format, - .dpy_refresh = display_refresh, - .dpy_mouse_set = display_mouse_set, - .dpy_cursor_define = display_mouse_define, + .dpy_name = "spice-egl", + .dpy_gfx_update = spice_gl_update, + .dpy_gfx_switch = spice_gl_switch, + .dpy_gfx_check_format = console_gl_check_format, + .dpy_refresh = spice_gl_refresh, + .dpy_mouse_set = display_mouse_set, + .dpy_cursor_define = display_mouse_define, .dpy_gl_ctx_create = qemu_spice_gl_create_context, .dpy_gl_ctx_destroy = qemu_egl_destroy_context, @@ -933,10 +1007,12 @@ static void qemu_spice_display_init_one(QemuConsole *con) #ifdef HAVE_SPICE_GL if (display_opengl) { ssd->dcl.ops = &display_listener_gl_ops; - ssd->dmabuf_fd = -1; ssd->gl_unblock_bh = qemu_bh_new(qemu_spice_gl_unblock_bh, ssd); ssd->gl_unblock_timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_spice_gl_block_timer, ssd); + ssd->gls = console_gl_init_context(); + ssd->have_surface = false; + ssd->have_scanout = false; } #endif ssd->dcl.con = con; diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 49df85e763..1e53b1cf84 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -707,10 +707,8 @@ check_solid_tile32(VncState *vs, int x, int y, int w, int h, static bool check_solid_tile(VncState *vs, int x, int y, int w, int h, uint32_t* color, bool samecolor) { - switch (VNC_SERVER_FB_BYTES) { - case 4: - return check_solid_tile32(vs, x, y, w, h, color, samecolor); - } + QEMU_BUILD_BUG_ON(VNC_SERVER_FB_BYTES != 4); + return check_solid_tile32(vs, x, y, w, h, color, samecolor); } static void find_best_solid_area(VncState *vs, int x, int y, int w, int h, diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 3cbf225487..737bffa984 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -146,3 +146,8 @@ void coroutine_fn qemu_coroutine_yield(void) self->caller = NULL; qemu_coroutine_switch(self, to, COROUTINE_YIELD); } + +bool qemu_coroutine_entered(Coroutine *co) +{ + return co->caller; +} @@ -2846,7 +2846,9 @@ static bool object_create_initial(const char *type) if (g_str_equal(type, "filter-buffer") || g_str_equal(type, "filter-dump") || g_str_equal(type, "filter-mirror") || - g_str_equal(type, "filter-redirector")) { + g_str_equal(type, "filter-redirector") || + g_str_equal(type, "colo-compare") || + g_str_equal(type, "filter-rewriter")) { return false; } |