aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xconfigure40
-rw-r--r--hw/arm/allwinner-a10.c6
-rw-r--r--hw/arm/digic.c6
-rw-r--r--hw/arm/fsl-imx25.c6
-rw-r--r--hw/arm/fsl-imx31.c6
-rw-r--r--hw/arm/pxa2xx.c2
-rw-r--r--hw/arm/xlnx-zynqmp.c6
-rw-r--r--hw/display/Makefile.objs6
-rw-r--r--hw/display/cg3.c4
-rw-r--r--hw/display/tcx.c2
-rw-r--r--hw/display/virtio-gpu-3d.c598
-rw-r--r--hw/display/virtio-gpu-pci.c4
-rw-r--r--hw/display/virtio-gpu.c151
-rw-r--r--hw/input/Makefile.objs2
-rw-r--r--hw/misc/arm_integrator_debug.c2
-rw-r--r--hw/misc/macio/cuda.c2
-rw-r--r--hw/misc/macio/macio.c14
-rw-r--r--hw/net/e1000.c8
-rw-r--r--hw/net/vmxnet3.c19
-rw-r--r--hw/net/vmxnet3.h6
-rw-r--r--hw/net/vmxnet_tx_pkt.c19
-rw-r--r--hw/pci-host/versatile.c11
-rw-r--r--hw/pcmcia/pxa2xx.c6
-rw-r--r--hw/virtio/virtio-pci.c20
-rw-r--r--hw/virtio/virtio-pci.h4
-rw-r--r--include/hw/qdev-core.h13
-rw-r--r--include/hw/virtio/virtio-gpu.h22
-rw-r--r--include/net/filter.h77
-rw-r--r--include/net/net.h6
-rw-r--r--include/net/queue.h20
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--include/standard-headers/linux/input.h4
-rw-r--r--include/standard-headers/linux/virtio_gpu.h112
-rw-r--r--include/ui/console.h37
-rw-r--r--include/ui/egl-context.h14
-rw-r--r--include/ui/gtk.h39
-rw-r--r--include/ui/shader.h4
-rw-r--r--memory.c17
-rw-r--r--net/Makefile.objs2
-rw-r--r--net/filter-buffer.c186
-rw-r--r--net/filter.c233
-rw-r--r--net/net.c121
-rw-r--r--net/queue.c24
-rw-r--r--qapi-schema.json20
-rw-r--r--qdev-monitor.c10
-rw-r--r--qemu-options.hx17
-rw-r--r--qmp.c11
-rwxr-xr-xscripts/update-linux-headers.sh1
-rw-r--r--target-alpha/cpu.c7
-rw-r--r--target-arm/cpu.c11
-rw-r--r--target-cris/cpu.c7
-rw-r--r--target-i386/cpu.c8
-rw-r--r--target-lm32/cpu.c7
-rw-r--r--target-m68k/cpu.c7
-rw-r--r--target-microblaze/cpu.c6
-rw-r--r--target-mips/cpu.c7
-rw-r--r--target-moxie/cpu.c7
-rw-r--r--target-openrisc/cpu.c7
-rw-r--r--target-ppc/kvm.c4
-rw-r--r--target-s390x/cpu.c7
-rw-r--r--target-sh4/cpu.c7
-rw-r--r--target-sparc/cpu.c7
-rw-r--r--target-tilegx/cpu.c7
-rw-r--r--target-tricore/cpu.c6
-rw-r--r--target-unicore32/cpu.c7
-rw-r--r--target-xtensa/cpu.c7
-rw-r--r--tests/.gitignore1
-rw-r--r--tests/Makefile22
-rw-r--r--tests/device-introspect-test.c124
-rw-r--r--tests/drive_del-test.c22
-rw-r--r--tests/ide-test.c8
-rw-r--r--tests/libqtest.c38
-rw-r--r--tests/libqtest.h33
-rw-r--r--tests/test-netfilter.c200
-rw-r--r--trace-events9
-rw-r--r--ui/Makefile.objs6
-rw-r--r--ui/console-gl.c7
-rw-r--r--ui/console.c67
-rw-r--r--ui/egl-context.c34
-rw-r--r--ui/gtk-egl.c131
-rw-r--r--ui/gtk-gl-area.c223
-rw-r--r--ui/gtk.c137
-rw-r--r--ui/sdl2-2d.c13
-rw-r--r--ui/shader.c31
-rw-r--r--vl.c19
85 files changed, 3006 insertions, 186 deletions
diff --git a/configure b/configure
index 2d2a498ac4..f08327e10e 100755
--- a/configure
+++ b/configure
@@ -328,9 +328,11 @@ glusterfs_zerofill="no"
archipelago="no"
gtk=""
gtkabi=""
+gtk_gl="no"
gnutls=""
gnutls_hash=""
vte=""
+virglrenderer=""
tpm="yes"
libssh2=""
vhdx=""
@@ -1122,6 +1124,10 @@ for opt do
;;
--enable-vte) vte="yes"
;;
+ --disable-virglrenderer) virglrenderer="no"
+ ;;
+ --enable-virglrenderer) virglrenderer="yes"
+ ;;
--disable-tpm) tpm="no"
;;
--enable-tpm) tpm="yes"
@@ -3231,6 +3237,9 @@ if test "$opengl" != "no" ; then
opengl_cflags="$($pkg_config --cflags $opengl_pkgs) $x11_cflags"
opengl_libs="$($pkg_config --libs $opengl_pkgs) $x11_libs"
opengl=yes
+ if test "$gtk" = "yes" && $pkg_config --exists "$gtkpackage >= 3.16"; then
+ gtk_gl="yes"
+ fi
else
if test "$opengl" = "yes" ; then
feature_not_found "opengl" "Please install opengl (mesa) devel pkgs: $opengl_pkgs"
@@ -3967,6 +3976,27 @@ EOF
fi
##########################################
+# virgl renderer probe
+
+if test "$virglrenderer" != "no" ; then
+ cat > $TMPC << EOF
+#include <virglrenderer.h>
+int main(void) { virgl_renderer_poll(); return 0; }
+EOF
+ virgl_cflags=$($pkg_config --cflags virglrenderer 2>/dev/null)
+ virgl_libs=$($pkg_config --libs virglrenderer 2>/dev/null)
+ if $pkg_config virglrenderer >/dev/null 2>&1 && \
+ compile_prog "$virgl_cflags" "$virgl_libs" ; then
+ virglrenderer="yes"
+ else
+ if test "$virglrenderer" = "yes" ; then
+ feature_not_found "virglrenderer"
+ fi
+ virglrenderer="no"
+ fi
+fi
+
+##########################################
# check if we have fdatasync
fdatasync=no
@@ -4576,6 +4606,7 @@ fi
echo "pixman $pixman"
echo "SDL support $sdl"
echo "GTK support $gtk"
+echo "GTK GL support $gtk_gl"
echo "GNUTLS support $gnutls"
echo "GNUTLS hash $gnutls_hash"
echo "GNUTLS gcrypt $gnutls_gcrypt"
@@ -4583,6 +4614,7 @@ echo "GNUTLS nettle $gnutls_nettle ${gnutls_nettle+($nettle_version)}"
echo "libtasn1 $tasn1"
echo "VTE support $vte"
echo "curses support $curses"
+echo "virgl support $virglrenderer"
echo "curl support $curl"
echo "mingw32 support $mingw32"
echo "Audio drivers $audio_drv_list"
@@ -4934,6 +4966,9 @@ if test "$gtk" = "yes" ; then
echo "CONFIG_GTK=y" >> $config_host_mak
echo "CONFIG_GTKABI=$gtkabi" >> $config_host_mak
echo "GTK_CFLAGS=$gtk_cflags" >> $config_host_mak
+ if test "$gtk_gl" = "yes" ; then
+ echo "CONFIG_GTK_GL=y" >> $config_host_mak
+ fi
fi
if test "$gnutls" = "yes" ; then
echo "CONFIG_GNUTLS=y" >> $config_host_mak
@@ -4955,6 +4990,11 @@ if test "$vte" = "yes" ; then
echo "CONFIG_VTE=y" >> $config_host_mak
echo "VTE_CFLAGS=$vte_cflags" >> $config_host_mak
fi
+if test "$virglrenderer" = "yes" ; then
+ echo "CONFIG_VIRGL=y" >> $config_host_mak
+ echo "VIRGL_CFLAGS=$virgl_cflags" >> $config_host_mak
+ echo "VIRGL_LIBS=$virgl_libs" >> $config_host_mak
+fi
if test "$xen" = "yes" ; then
echo "CONFIG_XEN_BACKEND=y" >> $config_host_mak
echo "CONFIG_XEN_CTRL_INTERFACE_VERSION=$xen_ctrl_version" >> $config_host_mak
diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c
index ff249af335..43dc0a12de 100644
--- a/hw/arm/allwinner-a10.c
+++ b/hw/arm/allwinner-a10.c
@@ -103,6 +103,12 @@ static void aw_a10_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = aw_a10_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo aw_a10_type_info = {
diff --git a/hw/arm/digic.c b/hw/arm/digic.c
index ec8c330602..90f8190c48 100644
--- a/hw/arm/digic.c
+++ b/hw/arm/digic.c
@@ -97,6 +97,12 @@ static void digic_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = digic_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo digic_type_info = {
diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c
index 86fde42e34..e1cadac997 100644
--- a/hw/arm/fsl-imx25.c
+++ b/hw/arm/fsl-imx25.c
@@ -284,6 +284,12 @@ static void fsl_imx25_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = fsl_imx25_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo fsl_imx25_type_info = {
diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c
index 8e1ed4811b..53d4473250 100644
--- a/hw/arm/fsl-imx31.c
+++ b/hw/arm/fsl-imx31.c
@@ -258,6 +258,12 @@ static void fsl_imx31_class_init(ObjectClass *oc, void *data)
DeviceClass *dc = DEVICE_CLASS(oc);
dc->realize = fsl_imx31_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo fsl_imx31_type_info = {
diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c
index 164260a9b6..79d22d91e5 100644
--- a/hw/arm/pxa2xx.c
+++ b/hw/arm/pxa2xx.c
@@ -1958,7 +1958,7 @@ static void pxa2xx_fir_instance_init(Object *obj)
PXA2xxFIrState *s = PXA2XX_FIR(obj);
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_fir_ops, s,
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_fir_ops, s,
"pxa2xx-fir", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index a9097f9b72..b36ca3da74 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -271,6 +271,12 @@ static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data)
dc->props = xlnx_zynqmp_props;
dc->realize = xlnx_zynqmp_realize;
+
+ /*
+ * Reason: creates an ARM CPU, thus use after free(), see
+ * arm_cpu_class_init()
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo xlnx_zynqmp_type_info = {
diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs
index dd8ea76d17..f0cf431a0f 100644
--- a/hw/display/Makefile.objs
+++ b/hw/display/Makefile.objs
@@ -35,6 +35,10 @@ obj-$(CONFIG_VGA) += vga.o
common-obj-$(CONFIG_QXL) += qxl.o qxl-logger.o qxl-render.o
-obj-$(CONFIG_VIRTIO) += virtio-gpu.o
+obj-$(CONFIG_VIRTIO) += virtio-gpu.o virtio-gpu-3d.o
obj-$(CONFIG_VIRTIO_PCI) += virtio-gpu-pci.o
obj-$(CONFIG_VIRTIO_VGA) += virtio-vga.o
+virtio-gpu.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu.o-libs += $(VIRGL_LIBS)
+virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS)
+virtio-gpu-3d.o-libs += $(VIRGL_LIBS)
diff --git a/hw/display/cg3.c b/hw/display/cg3.c
index d2a0d97320..e309fbe92e 100644
--- a/hw/display/cg3.c
+++ b/hw/display/cg3.c
@@ -280,12 +280,12 @@ static void cg3_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
CG3State *s = CG3(obj);
- memory_region_init_ram(&s->rom, NULL, "cg3.prom", FCODE_MAX_ROM_SIZE,
+ memory_region_init_ram(&s->rom, obj, "cg3.prom", FCODE_MAX_ROM_SIZE,
&error_fatal);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
- memory_region_init_io(&s->reg, NULL, &cg3_reg_ops, s, "cg3.reg",
+ memory_region_init_io(&s->reg, obj, &cg3_reg_ops, s, "cg3.reg",
CG3_REG_SIZE);
sysbus_init_mmio(sbd, &s->reg);
}
diff --git a/hw/display/tcx.c b/hw/display/tcx.c
index 463580094a..bf119bc89a 100644
--- a/hw/display/tcx.c
+++ b/hw/display/tcx.c
@@ -944,7 +944,7 @@ static void tcx_initfn(Object *obj)
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
TCXState *s = TCX(obj);
- memory_region_init_ram(&s->rom, NULL, "tcx.prom", FCODE_MAX_ROM_SIZE,
+ memory_region_init_ram(&s->rom, OBJECT(s), "tcx.prom", FCODE_MAX_ROM_SIZE,
&error_fatal);
memory_region_set_readonly(&s->rom, true);
sysbus_init_mmio(sbd, &s->rom);
diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c
new file mode 100644
index 0000000000..28dccfdeca
--- /dev/null
+++ b/hw/display/virtio-gpu-3d.c
@@ -0,0 +1,598 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu-common.h"
+#include "qemu/iov.h"
+#include "trace.h"
+#include "hw/virtio/virtio.h"
+#include "hw/virtio/virtio-gpu.h"
+
+#ifdef CONFIG_VIRGL
+
+#include "virglrenderer.h"
+
+static struct virgl_renderer_callbacks virtio_gpu_3d_cbs;
+
+static void virgl_cmd_create_resource_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_create_2d c2d;
+ struct virgl_renderer_resource_create_args args;
+
+ VIRTIO_GPU_FILL_CMD(c2d);
+ trace_virtio_gpu_cmd_res_create_2d(c2d.resource_id, c2d.format,
+ c2d.width, c2d.height);
+
+ args.handle = c2d.resource_id;
+ args.target = 2;
+ args.format = c2d.format;
+ args.bind = (1 << 1);
+ args.width = c2d.width;
+ args.height = c2d.height;
+ args.depth = 1;
+ args.array_size = 1;
+ args.last_level = 0;
+ args.nr_samples = 0;
+ args.flags = VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP;
+ virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void virgl_cmd_create_resource_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_create_3d c3d;
+ struct virgl_renderer_resource_create_args args;
+
+ VIRTIO_GPU_FILL_CMD(c3d);
+ trace_virtio_gpu_cmd_res_create_3d(c3d.resource_id, c3d.format,
+ c3d.width, c3d.height, c3d.depth);
+
+ args.handle = c3d.resource_id;
+ args.target = c3d.target;
+ args.format = c3d.format;
+ args.bind = c3d.bind;
+ args.width = c3d.width;
+ args.height = c3d.height;
+ args.depth = c3d.depth;
+ args.array_size = c3d.array_size;
+ args.last_level = c3d.last_level;
+ args.nr_samples = c3d.nr_samples;
+ args.flags = c3d.flags;
+ virgl_renderer_resource_create(&args, NULL, 0);
+}
+
+static void virgl_cmd_resource_unref(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_unref unref;
+
+ VIRTIO_GPU_FILL_CMD(unref);
+ trace_virtio_gpu_cmd_res_unref(unref.resource_id);
+
+ virgl_renderer_resource_unref(unref.resource_id);
+}
+
+static void virgl_cmd_context_create(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_create cc;
+
+ VIRTIO_GPU_FILL_CMD(cc);
+ trace_virtio_gpu_cmd_ctx_create(cc.hdr.ctx_id,
+ cc.debug_name);
+
+ virgl_renderer_context_create(cc.hdr.ctx_id, cc.nlen,
+ cc.debug_name);
+}
+
+static void virgl_cmd_context_destroy(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_destroy cd;
+
+ VIRTIO_GPU_FILL_CMD(cd);
+ trace_virtio_gpu_cmd_ctx_destroy(cd.hdr.ctx_id);
+
+ virgl_renderer_context_destroy(cd.hdr.ctx_id);
+}
+
+static void virtio_gpu_rect_update(VirtIOGPU *g, int idx, int x, int y,
+ int width, int height)
+{
+ if (!g->scanout[idx].con) {
+ return;
+ }
+
+ dpy_gl_update(g->scanout[idx].con, x, y, width, height);
+}
+
+static void virgl_cmd_resource_flush(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_flush rf;
+ int i;
+
+ VIRTIO_GPU_FILL_CMD(rf);
+ trace_virtio_gpu_cmd_res_flush(rf.resource_id,
+ rf.r.width, rf.r.height, rf.r.x, rf.r.y);
+
+ for (i = 0; i < VIRTIO_GPU_MAX_SCANOUT; i++) {
+ if (g->scanout[i].resource_id != rf.resource_id) {
+ continue;
+ }
+ virtio_gpu_rect_update(g, i, rf.r.x, rf.r.y, rf.r.width, rf.r.height);
+ }
+}
+
+static void virgl_cmd_set_scanout(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_set_scanout ss;
+ struct virgl_renderer_resource_info info;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(ss);
+ trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
+ ss.r.width, ss.r.height, ss.r.x, ss.r.y);
+
+ if (ss.scanout_id >= VIRTIO_GPU_MAX_SCANOUT) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
+ __func__, ss.scanout_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
+ return;
+ }
+ g->enable = 1;
+
+ memset(&info, 0, sizeof(info));
+
+ if (ss.resource_id && ss.r.width && ss.r.height) {
+ ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
+ if (ret == -1) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: illegal resource specified %d\n",
+ __func__, ss.resource_id);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
+ return;
+ }
+ qemu_console_resize(g->scanout[ss.scanout_id].con,
+ ss.r.width, ss.r.height);
+ virgl_renderer_force_ctx_0();
+ dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id,
+ info.flags & 1 /* FIXME: Y_0_TOP */,
+ ss.r.x, ss.r.y, ss.r.width, ss.r.height);
+ } else {
+ if (ss.scanout_id != 0) {
+ dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
+ }
+ dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false,
+ 0, 0, 0, 0);
+ }
+ g->scanout[ss.scanout_id].resource_id = ss.resource_id;
+}
+
+static void virgl_cmd_submit_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_cmd_submit cs;
+ void *buf;
+ size_t s;
+
+ VIRTIO_GPU_FILL_CMD(cs);
+ trace_virtio_gpu_cmd_ctx_submit(cs.hdr.ctx_id, cs.size);
+
+ buf = g_malloc(cs.size);
+ s = iov_to_buf(cmd->elem.out_sg, cmd->elem.out_num,
+ sizeof(cs), buf, cs.size);
+ if (s != cs.size) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: size mismatch (%zd/%d)",
+ __func__, s, cs.size);
+ cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
+ return;
+ }
+
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->stats.req_3d++;
+ g->stats.bytes_3d += cs.size;
+ }
+
+ virgl_renderer_submit_cmd(buf, cs.hdr.ctx_id, cs.size / 4);
+
+ g_free(buf);
+}
+
+static void virgl_cmd_transfer_to_host_2d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_to_host_2d t2d;
+ struct virtio_gpu_box box;
+
+ VIRTIO_GPU_FILL_CMD(t2d);
+ trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
+
+ box.x = t2d.r.x;
+ box.y = t2d.r.y;
+ box.z = 0;
+ box.w = t2d.r.width;
+ box.h = t2d.r.height;
+ box.d = 1;
+
+ virgl_renderer_transfer_write_iov(t2d.resource_id,
+ 0,
+ 0,
+ 0,
+ 0,
+ (struct virgl_box *)&box,
+ t2d.offset, NULL, 0);
+}
+
+static void virgl_cmd_transfer_to_host_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_host_3d t3d;
+
+ VIRTIO_GPU_FILL_CMD(t3d);
+ trace_virtio_gpu_cmd_res_xfer_toh_3d(t3d.resource_id);
+
+ virgl_renderer_transfer_write_iov(t3d.resource_id,
+ t3d.hdr.ctx_id,
+ t3d.level,
+ t3d.stride,
+ t3d.layer_stride,
+ (struct virgl_box *)&t3d.box,
+ t3d.offset, NULL, 0);
+}
+
+static void
+virgl_cmd_transfer_from_host_3d(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_transfer_host_3d tf3d;
+
+ VIRTIO_GPU_FILL_CMD(tf3d);
+ trace_virtio_gpu_cmd_res_xfer_fromh_3d(tf3d.resource_id);
+
+ virgl_renderer_transfer_read_iov(tf3d.resource_id,
+ tf3d.hdr.ctx_id,
+ tf3d.level,
+ tf3d.stride,
+ tf3d.layer_stride,
+ (struct virgl_box *)&tf3d.box,
+ tf3d.offset, NULL, 0);
+}
+
+
+static void virgl_resource_attach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_attach_backing att_rb;
+ struct iovec *res_iovs;
+ int ret;
+
+ VIRTIO_GPU_FILL_CMD(att_rb);
+ trace_virtio_gpu_cmd_res_back_attach(att_rb.resource_id);
+
+ ret = virtio_gpu_create_mapping_iov(&att_rb, cmd, &res_iovs);
+ if (ret != 0) {
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ return;
+ }
+
+ virgl_renderer_resource_attach_iov(att_rb.resource_id,
+ res_iovs, att_rb.nr_entries);
+}
+
+static void virgl_resource_detach_backing(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_resource_detach_backing detach_rb;
+ struct iovec *res_iovs = NULL;
+ int num_iovs = 0;
+
+ VIRTIO_GPU_FILL_CMD(detach_rb);
+ trace_virtio_gpu_cmd_res_back_detach(detach_rb.resource_id);
+
+ virgl_renderer_resource_detach_iov(detach_rb.resource_id,
+ &res_iovs,
+ &num_iovs);
+ if (res_iovs == NULL || num_iovs == 0) {
+ return;
+ }
+ virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs);
+}
+
+
+static void virgl_cmd_ctx_attach_resource(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_resource att_res;
+
+ VIRTIO_GPU_FILL_CMD(att_res);
+ trace_virtio_gpu_cmd_ctx_res_attach(att_res.hdr.ctx_id,
+ att_res.resource_id);
+
+ virgl_renderer_ctx_attach_resource(att_res.hdr.ctx_id, att_res.resource_id);
+}
+
+static void virgl_cmd_ctx_detach_resource(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_ctx_resource det_res;
+
+ VIRTIO_GPU_FILL_CMD(det_res);
+ trace_virtio_gpu_cmd_ctx_res_detach(det_res.hdr.ctx_id,
+ det_res.resource_id);
+
+ virgl_renderer_ctx_detach_resource(det_res.hdr.ctx_id, det_res.resource_id);
+}
+
+static void virgl_cmd_get_capset_info(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_get_capset_info info;
+ struct virtio_gpu_resp_capset_info resp;
+
+ VIRTIO_GPU_FILL_CMD(info);
+
+ if (info.capset_index == 0) {
+ resp.capset_id = VIRTIO_GPU_CAPSET_VIRGL;
+ virgl_renderer_get_cap_set(resp.capset_id,
+ &resp.capset_max_version,
+ &resp.capset_max_size);
+ } else {
+ resp.capset_max_version = 0;
+ resp.capset_max_size = 0;
+ }
+ resp.hdr.type = VIRTIO_GPU_RESP_OK_CAPSET_INFO;
+ virtio_gpu_ctrl_response(g, cmd, &resp.hdr, sizeof(resp));
+}
+
+static void virgl_cmd_get_capset(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ struct virtio_gpu_get_capset gc;
+ struct virtio_gpu_resp_capset *resp;
+ uint32_t max_ver, max_size;
+ VIRTIO_GPU_FILL_CMD(gc);
+
+ virgl_renderer_get_cap_set(gc.capset_id, &max_ver,
+ &max_size);
+ resp = g_malloc(sizeof(*resp) + max_size);
+
+ resp->hdr.type = VIRTIO_GPU_RESP_OK_CAPSET;
+ virgl_renderer_fill_caps(gc.capset_id,
+ gc.capset_version,
+ (void *)resp->capset_data);
+ virtio_gpu_ctrl_response(g, cmd, &resp->hdr, sizeof(*resp) + max_size);
+ g_free(resp);
+}
+
+void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd)
+{
+ VIRTIO_GPU_FILL_CMD(cmd->cmd_hdr);
+
+ virgl_renderer_force_ctx_0();
+ switch (cmd->cmd_hdr.type) {
+ case VIRTIO_GPU_CMD_CTX_CREATE:
+ virgl_cmd_context_create(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_DESTROY:
+ virgl_cmd_context_destroy(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D:
+ virgl_cmd_create_resource_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_CREATE_3D:
+ virgl_cmd_create_resource_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SUBMIT_3D:
+ virgl_cmd_submit_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D:
+ virgl_cmd_transfer_to_host_2d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D:
+ virgl_cmd_transfer_to_host_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D:
+ virgl_cmd_transfer_from_host_3d(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING:
+ virgl_resource_attach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING:
+ virgl_resource_detach_backing(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_SET_SCANOUT:
+ virgl_cmd_set_scanout(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_FLUSH:
+ virgl_cmd_resource_flush(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_RESOURCE_UNREF:
+ virgl_cmd_resource_unref(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE:
+ /* TODO add security */
+ virgl_cmd_ctx_attach_resource(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE:
+ /* TODO add security */
+ virgl_cmd_ctx_detach_resource(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_GET_CAPSET_INFO:
+ virgl_cmd_get_capset_info(g, cmd);
+ break;
+ case VIRTIO_GPU_CMD_GET_CAPSET:
+ virgl_cmd_get_capset(g, cmd);
+ break;
+
+ case VIRTIO_GPU_CMD_GET_DISPLAY_INFO:
+ virtio_gpu_get_display_info(g, cmd);
+ break;
+ default:
+ cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC;
+ break;
+ }
+
+ if (cmd->finished) {
+ return;
+ }
+ if (cmd->error) {
+ fprintf(stderr, "%s: ctrl 0x%x, error 0x%x\n", __func__,
+ cmd->cmd_hdr.type, cmd->error);
+ virtio_gpu_ctrl_response_nodata(g, cmd, cmd->error);
+ return;
+ }
+ if (!(cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_FENCE)) {
+ virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+ return;
+ }
+
+ trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+ virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type);
+}
+
+static void virgl_write_fence(void *opaque, uint32_t fence)
+{
+ VirtIOGPU *g = opaque;
+ struct virtio_gpu_ctrl_command *cmd, *tmp;
+
+ QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) {
+ /*
+ * the guest can end up emitting fences out of order
+ * so we should check all fenced cmds not just the first one.
+ */
+ if (cmd->cmd_hdr.fence_id > fence) {
+ continue;
+ }
+ trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id);
+ virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA);
+ QTAILQ_REMOVE(&g->fenceq, cmd, next);
+ g_free(cmd);
+ g->inflight--;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ fprintf(stderr, "inflight: %3d (-)\r", g->inflight);
+ }
+ }
+}
+
+static virgl_renderer_gl_context
+virgl_create_context(void *opaque, int scanout_idx,
+ struct virgl_renderer_gl_ctx_param *params)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext ctx;
+ QEMUGLParams qparams;
+
+ qparams.major_ver = params->major_ver;
+ qparams.minor_ver = params->minor_ver;
+
+ ctx = dpy_gl_ctx_create(g->scanout[scanout_idx].con, &qparams);
+ return (virgl_renderer_gl_context)ctx;
+}
+
+static void virgl_destroy_context(void *opaque, virgl_renderer_gl_context ctx)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext qctx = (QEMUGLContext)ctx;
+
+ dpy_gl_ctx_destroy(g->scanout[0].con, qctx);
+}
+
+static int virgl_make_context_current(void *opaque, int scanout_idx,
+ virgl_renderer_gl_context ctx)
+{
+ VirtIOGPU *g = opaque;
+ QEMUGLContext qctx = (QEMUGLContext)ctx;
+
+ return dpy_gl_ctx_make_current(g->scanout[scanout_idx].con, qctx);
+}
+
+static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = {
+ .version = 1,
+ .write_fence = virgl_write_fence,
+ .create_gl_context = virgl_create_context,
+ .destroy_gl_context = virgl_destroy_context,
+ .make_current = virgl_make_context_current,
+};
+
+static void virtio_gpu_print_stats(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ if (g->stats.requests) {
+ fprintf(stderr, "stats: vq req %4d, %3d -- 3D %4d (%5d)\n",
+ g->stats.requests,
+ g->stats.max_inflight,
+ g->stats.req_3d,
+ g->stats.bytes_3d);
+ g->stats.requests = 0;
+ g->stats.max_inflight = 0;
+ g->stats.req_3d = 0;
+ g->stats.bytes_3d = 0;
+ } else {
+ fprintf(stderr, "stats: idle\r");
+ }
+ timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+}
+
+static void virtio_gpu_fence_poll(void *opaque)
+{
+ VirtIOGPU *g = opaque;
+
+ virgl_renderer_poll();
+ if (g->inflight) {
+ timer_mod(g->fence_poll, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
+ }
+}
+
+void virtio_gpu_virgl_fence_poll(VirtIOGPU *g)
+{
+ virtio_gpu_fence_poll(g);
+}
+
+void virtio_gpu_virgl_reset(VirtIOGPU *g)
+{
+ int i;
+
+ /* virgl_renderer_reset() ??? */
+ for (i = 0; i < g->conf.max_outputs; i++) {
+ if (i != 0) {
+ dpy_gfx_replace_surface(g->scanout[i].con, NULL);
+ }
+ dpy_gl_scanout(g->scanout[i].con, 0, false, 0, 0, 0, 0);
+ }
+}
+
+int virtio_gpu_virgl_init(VirtIOGPU *g)
+{
+ int ret;
+
+ ret = virgl_renderer_init(g, 0, &virtio_gpu_3d_cbs);
+ if (ret != 0) {
+ return ret;
+ }
+
+ g->fence_poll = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_fence_poll, g);
+
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->print_stats = timer_new_ms(QEMU_CLOCK_VIRTUAL,
+ virtio_gpu_print_stats, g);
+ timer_mod(g->print_stats, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000);
+ }
+ return 0;
+}
+
+#endif /* CONFIG_VIRGL */
diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c
index 5bc62cf344..eef137f813 100644
--- a/hw/display/virtio-gpu-pci.c
+++ b/hw/display/virtio-gpu-pci.c
@@ -6,8 +6,8 @@
* Authors:
* Dave Airlie
*
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
*
*/
#include "hw/pci/pci.h"
diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c
index a67d927f56..a836ce3859 100644
--- a/hw/display/virtio-gpu.c
+++ b/hw/display/virtio-gpu.c
@@ -7,7 +7,7 @@
* Dave Airlie <airlied@redhat.com>
* Gerd Hoffmann <kraxel@redhat.com>
*
- * This work is licensed under the terms of the GNU GPL, version 2.
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
@@ -22,6 +22,23 @@
static struct virtio_gpu_simple_resource*
virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id);
+#ifdef CONFIG_VIRGL
+#include "virglrenderer.h"
+#define VIRGL(_g, _virgl, _simple, ...) \
+ do { \
+ if (_g->use_virgl_renderer) { \
+ _virgl(__VA_ARGS__); \
+ } else { \
+ _simple(__VA_ARGS__); \
+ } \
+ } while (0)
+#else
+#define VIRGL(_g, _virgl, _simple, ...) \
+ do { \
+ _simple(__VA_ARGS__); \
+ } while (0)
+#endif
+
static void update_cursor_data_simple(VirtIOGPU *g,
struct virtio_gpu_scanout *s,
uint32_t resource_id)
@@ -45,16 +62,49 @@ static void update_cursor_data_simple(VirtIOGPU *g,
pixels * sizeof(uint32_t));
}
+#ifdef CONFIG_VIRGL
+
+static void update_cursor_data_virgl(VirtIOGPU *g,
+ struct virtio_gpu_scanout *s,
+ uint32_t resource_id)
+{
+ uint32_t width, height;
+ uint32_t pixels, *data;
+
+ data = virgl_renderer_get_cursor_data(resource_id, &width, &height);
+ if (!data) {
+ return;
+ }
+
+ if (width != s->current_cursor->width ||
+ height != s->current_cursor->height) {
+ return;
+ }
+
+ pixels = s->current_cursor->width * s->current_cursor->height;
+ memcpy(s->current_cursor->data, data, pixels * sizeof(uint32_t));
+ free(data);
+}
+
+#endif
+
static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
{
struct virtio_gpu_scanout *s;
+ bool move = cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR;
if (cursor->pos.scanout_id >= g->conf.max_outputs) {
return;
}
s = &g->scanout[cursor->pos.scanout_id];
- if (cursor->hdr.type != VIRTIO_GPU_CMD_MOVE_CURSOR) {
+ trace_virtio_gpu_update_cursor(cursor->pos.scanout_id,
+ cursor->pos.x,
+ cursor->pos.y,
+ move ? "move" : "update",
+ cursor->resource_id);
+
+ if (move) {
if (!s->current_cursor) {
s->current_cursor = cursor_alloc(64, 64);
}
@@ -63,7 +113,8 @@ static void update_cursor(VirtIOGPU *g, struct virtio_gpu_update_cursor *cursor)
s->current_cursor->hot_y = cursor->hot_y;
if (cursor->resource_id > 0) {
- update_cursor_data_simple(g, s, cursor->resource_id);
+ VIRGL(g, update_cursor_data_virgl, update_cursor_data_simple,
+ g, s, cursor->resource_id);
}
dpy_cursor_define(s->con, s->current_cursor);
}
@@ -92,9 +143,23 @@ static void virtio_gpu_set_config(VirtIODevice *vdev, const uint8_t *config)
static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features,
Error **errp)
{
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+ if (virtio_gpu_virgl_enabled(g->conf)) {
+ features |= (1 << VIRTIO_GPU_FEATURE_VIRGL);
+ }
return features;
}
+static void virtio_gpu_set_features(VirtIODevice *vdev, uint64_t features)
+{
+ static const uint32_t virgl = (1 << VIRTIO_GPU_FEATURE_VIRGL);
+ VirtIOGPU *g = VIRTIO_GPU(vdev);
+
+ g->use_virgl_renderer = ((features & virgl) == virgl);
+ trace_virtio_gpu_features(g->use_virgl_renderer);
+}
+
static void virtio_gpu_notify_event(VirtIOGPU *g, uint32_t event_type)
{
g->virtio_config.events_read |= event_type;
@@ -563,7 +628,6 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
__func__, ab->resource_id, i);
virtio_gpu_cleanup_mapping_iov(*iov, i);
g_free(ents);
- g_free(*iov);
*iov = NULL;
return -1;
}
@@ -580,12 +644,12 @@ void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count)
cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1,
iov[i].iov_len);
}
+ g_free(iov);
}
static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res)
{
virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt);
- g_free(res->iov);
res->iov = NULL;
res->iov_cnt = 0;
}
@@ -699,25 +763,43 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq)
return;
}
+#ifdef CONFIG_VIRGL
+ if (!g->renderer_inited && g->use_virgl_renderer) {
+ virtio_gpu_virgl_init(g);
+ g->renderer_inited = true;
+ }
+#endif
+
cmd = g_new(struct virtio_gpu_ctrl_command, 1);
while (virtqueue_pop(vq, &cmd->elem)) {
cmd->vq = vq;
cmd->error = 0;
cmd->finished = false;
- g->stats.requests++;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ g->stats.requests++;
+ }
- virtio_gpu_simple_process_cmd(g, cmd);
+ VIRGL(g, virtio_gpu_virgl_process_cmd, virtio_gpu_simple_process_cmd,
+ g, cmd);
if (!cmd->finished) {
QTAILQ_INSERT_TAIL(&g->fenceq, cmd, next);
- g->stats.inflight++;
- if (g->stats.max_inflight < g->stats.inflight) {
- g->stats.max_inflight = g->stats.inflight;
+ g->inflight++;
+ if (virtio_gpu_stats_enabled(g->conf)) {
+ if (g->stats.max_inflight < g->inflight) {
+ g->stats.max_inflight = g->inflight;
+ }
+ fprintf(stderr, "inflight: %3d (+)\r", g->inflight);
}
- fprintf(stderr, "inflight: %3d (+)\r", g->stats.inflight);
cmd = g_new(struct virtio_gpu_ctrl_command, 1);
}
}
g_free(cmd);
+
+#ifdef CONFIG_VIRGL
+ if (g->use_virgl_renderer) {
+ virtio_gpu_virgl_fence_poll(g);
+ }
+#endif
}
static void virtio_gpu_ctrl_bh(void *opaque)
@@ -804,6 +886,7 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
{
VirtIODevice *vdev = VIRTIO_DEVICE(qdev);
VirtIOGPU *g = VIRTIO_GPU(qdev);
+ bool have_virgl;
int i;
g->config_size = sizeof(struct virtio_gpu_config);
@@ -814,8 +897,25 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp)
g->req_state[0].width = 1024;
g->req_state[0].height = 768;
- g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
- g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ g->use_virgl_renderer = false;
+#if !defined(CONFIG_VIRGL) || defined(HOST_WORDS_BIGENDIAN)
+ have_virgl = false;
+#else
+ have_virgl = display_opengl;
+#endif
+ if (!have_virgl) {
+ g->conf.flags &= ~(1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED);
+ }
+
+ if (virtio_gpu_virgl_enabled(g->conf)) {
+ /* use larger control queue in 3d mode */
+ g->ctrl_vq = virtio_add_queue(vdev, 256, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ g->virtio_config.num_capsets = 1;
+ } else {
+ g->ctrl_vq = virtio_add_queue(vdev, 64, virtio_gpu_handle_ctrl_cb);
+ g->cursor_vq = virtio_add_queue(vdev, 16, virtio_gpu_handle_cursor_cb);
+ }
g->ctrl_bh = qemu_bh_new(virtio_gpu_ctrl_bh, g);
g->cursor_bh = qemu_bh_new(virtio_gpu_cursor_bh, g);
@@ -869,10 +969,23 @@ static void virtio_gpu_reset(VirtIODevice *vdev)
g->scanout[i].ds = NULL;
}
g->enabled_output_bitmask = 1;
+
+#ifdef CONFIG_VIRGL
+ if (g->use_virgl_renderer) {
+ virtio_gpu_virgl_reset(g);
+ g->use_virgl_renderer = 0;
+ }
+#endif
}
static Property virtio_gpu_properties[] = {
DEFINE_PROP_UINT32("max_outputs", VirtIOGPU, conf.max_outputs, 1),
+#ifdef CONFIG_VIRGL
+ DEFINE_PROP_BIT("virgl", VirtIOGPU, conf.flags,
+ VIRTIO_GPU_FLAG_VIRGL_ENABLED, true),
+ DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags,
+ VIRTIO_GPU_FLAG_STATS_ENABLED, false),
+#endif
DEFINE_PROP_END_OF_LIST(),
};
@@ -885,6 +998,7 @@ static void virtio_gpu_class_init(ObjectClass *klass, void *data)
vdc->get_config = virtio_gpu_get_config;
vdc->set_config = virtio_gpu_set_config;
vdc->get_features = virtio_gpu_get_features;
+ vdc->set_features = virtio_gpu_set_features;
vdc->reset = virtio_gpu_reset;
@@ -917,3 +1031,14 @@ QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_mem_entry) != 16);
QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_attach_backing) != 32);
QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_detach_backing) != 32);
QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_display_info) != 408);
+
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_transfer_host_3d) != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resource_create_3d) != 72);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_create) != 96);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_destroy) != 24);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_ctx_resource) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_cmd_submit) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset_info) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset_info) != 40);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_get_capset) != 32);
+QEMU_BUILD_BUG_ON(sizeof(struct virtio_gpu_resp_capset) != 24);
diff --git a/hw/input/Makefile.objs b/hw/input/Makefile.objs
index 624ba7ea40..7715d7230d 100644
--- a/hw/input/Makefile.objs
+++ b/hw/input/Makefile.objs
@@ -8,9 +8,9 @@ common-obj-$(CONFIG_STELLARIS_INPUT) += stellaris_input.o
common-obj-$(CONFIG_TSC2005) += tsc2005.o
common-obj-$(CONFIG_VMMOUSE) += vmmouse.o
-ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input.o
common-obj-$(CONFIG_VIRTIO) += virtio-input-hid.o
+ifeq ($(CONFIG_LINUX),y)
common-obj-$(CONFIG_VIRTIO) += virtio-input-host.o
endif
diff --git a/hw/misc/arm_integrator_debug.c b/hw/misc/arm_integrator_debug.c
index 99b720fbb9..6d9dd74e38 100644
--- a/hw/misc/arm_integrator_debug.c
+++ b/hw/misc/arm_integrator_debug.c
@@ -79,7 +79,7 @@ static void intdbg_control_init(Object *obj)
SysBusDevice *sd = SYS_BUS_DEVICE(obj);
IntegratorDebugState *s = INTEGRATOR_DEBUG(obj);
- memory_region_init_io(&s->iomem, NULL, &intdbg_control_ops,
+ memory_region_init_io(&s->iomem, obj, &intdbg_control_ops,
NULL, "dbg-leds", 0x1000000);
sysbus_init_mmio(sd, &s->iomem);
}
diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c
index f3984e3a20..5d7043e99c 100644
--- a/hw/misc/macio/cuda.c
+++ b/hw/misc/macio/cuda.c
@@ -713,7 +713,7 @@ static void cuda_initfn(Object *obj)
CUDAState *s = CUDA(obj);
int i;
- memory_region_init_io(&s->mem, NULL, &cuda_ops, s, "cuda", 0x2000);
+ memory_region_init_io(&s->mem, obj, &cuda_ops, s, "cuda", 0x2000);
sysbus_init_mmio(d, &s->mem);
sysbus_init_irq(d, &s->irq);
diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c
index e3c0242d41..c661f86c21 100644
--- a/hw/misc/macio/macio.c
+++ b/hw/misc/macio/macio.c
@@ -105,10 +105,10 @@ static void macio_escc_legacy_setup(MacIOState *macio_state)
0xF0, 0xE0,
};
- memory_region_init(escc_legacy, NULL, "escc-legacy", 256);
+ memory_region_init(escc_legacy, OBJECT(macio_state), "escc-legacy", 256);
for (i = 0; i < ARRAY_SIZE(maps); i += 2) {
MemoryRegion *port = g_new(MemoryRegion, 1);
- memory_region_init_alias(port, NULL, "escc-legacy-port",
+ memory_region_init_alias(port, OBJECT(macio_state), "escc-legacy-port",
macio_state->escc_mem, maps[i+1], 0x2);
memory_region_add_subregion(escc_legacy, maps[i], port);
}
@@ -131,6 +131,10 @@ static void macio_common_realize(PCIDevice *d, Error **errp)
MacIOState *s = MACIO(d);
SysBusDevice *sysbus_dev;
Error *err = NULL;
+ MemoryRegion *dbdma_mem;
+
+ s->dbdma = DBDMA_init(&dbdma_mem);
+ memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err);
if (err) {
@@ -328,16 +332,12 @@ static void macio_newworld_init(Object *obj)
static void macio_instance_init(Object *obj)
{
MacIOState *s = MACIO(obj);
- MemoryRegion *dbdma_mem;
- memory_region_init(&s->bar, NULL, "macio", 0x80000);
+ memory_region_init(&s->bar, obj, "macio", 0x80000);
object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA);
qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default());
object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL);
-
- s->dbdma = DBDMA_init(&dbdma_mem);
- memory_region_add_subregion(&s->bar, 0x08000, dbdma_mem);
}
static const VMStateDescription vmstate_macio_oldworld = {
diff --git a/hw/net/e1000.c b/hw/net/e1000.c
index 09c9e9d53b..910de3a7be 100644
--- a/hw/net/e1000.c
+++ b/hw/net/e1000.c
@@ -1647,7 +1647,7 @@ static const TypeInfo e1000_base_info = {
static const E1000Info e1000_devices[] = {
{
- .name = "e1000-82540em",
+ .name = "e1000",
.device_id = E1000_DEV_ID_82540EM,
.revision = 0x03,
.phy_id2 = E1000_PHY_ID2_8254xx_DEFAULT,
@@ -1666,11 +1666,6 @@ static const E1000Info e1000_devices[] = {
},
};
-static const TypeInfo e1000_default_info = {
- .name = "e1000",
- .parent = "e1000-82540em",
-};
-
static void e1000_register_types(void)
{
int i;
@@ -1688,7 +1683,6 @@ static void e1000_register_types(void)
type_register(&type_info);
}
- type_register_static(&e1000_default_info);
}
type_init(e1000_register_types)
diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c
index 04159c8222..3c5e10dd6d 100644
--- a/hw/net/vmxnet3.c
+++ b/hw/net/vmxnet3.c
@@ -729,9 +729,7 @@ static void vmxnet3_process_tx_queue(VMXNET3State *s, int qidx)
}
if (txd.eop) {
- if (!s->skip_current_tx_pkt) {
- vmxnet_tx_pkt_parse(s->tx_pkt);
-
+ if (!s->skip_current_tx_pkt && vmxnet_tx_pkt_parse(s->tx_pkt)) {
if (s->needs_vlan) {
vmxnet_tx_pkt_setup_vlan_header(s->tx_pkt, s->tci);
}
@@ -1165,9 +1163,13 @@ vmxnet3_io_bar0_write(void *opaque, hwaddr addr,
static uint64_t
vmxnet3_io_bar0_read(void *opaque, hwaddr addr, unsigned size)
{
+ VMXNET3State *s = opaque;
+
if (VMW_IS_MULTIREG_ADDR(addr, VMXNET3_REG_IMR,
VMXNET3_MAX_INTRS, VMXNET3_REG_ALIGN)) {
- g_assert_not_reached();
+ int l = VMW_MULTIREG_IDX_BY_ADDR(addr, VMXNET3_REG_IMR,
+ VMXNET3_REG_ALIGN);
+ return s->interrupt_states[l].is_masked;
}
VMW_CBPRN("BAR0 unknown read [%" PRIx64 "], size %d", addr, size);
@@ -1629,6 +1631,11 @@ static void vmxnet3_handle_command(VMXNET3State *s, uint64_t cmd)
VMW_CBPRN("Set: VMXNET3_CMD_GET_CONF_INTR - interrupt configuration");
break;
+ case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO:
+ VMW_CBPRN("Set: VMXNET3_CMD_GET_ADAPTIVE_RING_INFO - "
+ "adaptive ring info flags");
+ break;
+
default:
VMW_CBPRN("Received unknown command: %" PRIx64, cmd);
break;
@@ -1668,6 +1675,10 @@ static uint64_t vmxnet3_get_command_status(VMXNET3State *s)
ret = vmxnet3_get_interrupt_config(s);
break;
+ case VMXNET3_CMD_GET_ADAPTIVE_RING_INFO:
+ ret = VMXNET3_DISABLE_ADAPTIVE_RING;
+ break;
+
default:
VMW_WRPRN("Received request for unknown command: %x", s->last_command);
ret = -1;
diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h
index f987d71269..f7006afe96 100644
--- a/hw/net/vmxnet3.h
+++ b/hw/net/vmxnet3.h
@@ -198,9 +198,13 @@ enum {
VMXNET3_CMD_GET_DID_LO, /* 0xF00D0005 */
VMXNET3_CMD_GET_DID_HI, /* 0xF00D0006 */
VMXNET3_CMD_GET_DEV_EXTRA_INFO, /* 0xF00D0007 */
- VMXNET3_CMD_GET_CONF_INTR /* 0xF00D0008 */
+ VMXNET3_CMD_GET_CONF_INTR, /* 0xF00D0008 */
+ VMXNET3_CMD_GET_ADAPTIVE_RING_INFO /* 0xF00D0009 */
};
+/* Adaptive Ring Info Flags */
+#define VMXNET3_DISABLE_ADAPTIVE_RING 1
+
/*
* Little Endian layout of bitfields -
* Byte 0 : 7.....len.....0
diff --git a/hw/net/vmxnet_tx_pkt.c b/hw/net/vmxnet_tx_pkt.c
index f7344c4cb3..eb88ddf254 100644
--- a/hw/net/vmxnet_tx_pkt.c
+++ b/hw/net/vmxnet_tx_pkt.c
@@ -142,11 +142,24 @@ static bool vmxnet_tx_pkt_parse_headers(struct VmxnetTxPkt *pkt)
bytes_read = iov_to_buf(pkt->raw, pkt->raw_frags, 0, l2_hdr->iov_base,
ETH_MAX_L2_HDR_LEN);
- if (bytes_read < ETH_MAX_L2_HDR_LEN) {
+ if (bytes_read < sizeof(struct eth_header)) {
+ l2_hdr->iov_len = 0;
+ return false;
+ }
+
+ l2_hdr->iov_len = sizeof(struct eth_header);
+ switch (be16_to_cpu(PKT_GET_ETH_HDR(l2_hdr->iov_base)->h_proto)) {
+ case ETH_P_VLAN:
+ l2_hdr->iov_len += sizeof(struct vlan_header);
+ break;
+ case ETH_P_DVLAN:
+ l2_hdr->iov_len += 2 * sizeof(struct vlan_header);
+ break;
+ }
+
+ if (bytes_read < l2_hdr->iov_len) {
l2_hdr->iov_len = 0;
return false;
- } else {
- l2_hdr->iov_len = eth_get_l2_hdr_length(l2_hdr->iov_base);
}
l3_proto = eth_get_l3_proto(l2_hdr->iov_base, l2_hdr->iov_len);
diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c
index 6d23553094..7172b90958 100644
--- a/hw/pci-host/versatile.c
+++ b/hw/pci-host/versatile.c
@@ -500,6 +500,8 @@ static void pci_vpb_class_init(ObjectClass *klass, void *data)
dc->reset = pci_vpb_reset;
dc->vmsd = &pci_vpb_vmstate;
dc->props = pci_vpb_properties;
+ /* Reason: object_unref() hangs */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo pci_vpb_info = {
@@ -521,10 +523,19 @@ static void pci_realview_init(Object *obj)
s->mem_win_size[2] = 0x08000000;
}
+static void pci_realview_class_init(ObjectClass *class, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(class);
+
+ /* Reason: object_unref() hangs */
+ dc->cannot_destroy_with_object_finalize_yet = true;
+}
+
static const TypeInfo pci_realview_info = {
.name = "realview_pci",
.parent = TYPE_VERSATILE_PCI,
.instance_init = pci_realview_init,
+ .class_init = pci_realview_class_init,
};
static void versatile_pci_register_types(void)
diff --git a/hw/pcmcia/pxa2xx.c b/hw/pcmcia/pxa2xx.c
index a7e187743d..812716e1c8 100644
--- a/hw/pcmcia/pxa2xx.c
+++ b/hw/pcmcia/pxa2xx.c
@@ -163,7 +163,7 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
sysbus_init_mmio(sbd, &s->container_mem);
/* Socket I/O Memory Space */
- memory_region_init_io(&s->iomem, NULL, &pxa2xx_pcmcia_io_ops, s,
+ memory_region_init_io(&s->iomem, obj, &pxa2xx_pcmcia_io_ops, s,
"pxa2xx-pcmcia-io", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x00000000,
&s->iomem);
@@ -171,13 +171,13 @@ static void pxa2xx_pcmcia_initfn(Object *obj)
/* Then next 64 MB is reserved */
/* Socket Attribute Memory Space */
- memory_region_init_io(&s->attr_iomem, NULL, &pxa2xx_pcmcia_attr_ops, s,
+ memory_region_init_io(&s->attr_iomem, obj, &pxa2xx_pcmcia_attr_ops, s,
"pxa2xx-pcmcia-attribute", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x08000000,
&s->attr_iomem);
/* Socket Common Memory Space */
- memory_region_init_io(&s->common_iomem, NULL, &pxa2xx_pcmcia_common_ops, s,
+ memory_region_init_io(&s->common_iomem, obj, &pxa2xx_pcmcia_common_ops, s,
"pxa2xx-pcmcia-common", 0x04000000);
memory_region_add_subregion(&s->container_mem, 0x0c000000,
&s->common_iomem);
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 6703806f83..e5c406d1d2 100644
--- a/hw/virtio/virtio-pci.c
+++ b/hw/virtio/virtio-pci.c
@@ -2134,14 +2134,6 @@ static void virtio_tablet_initfn(Object *obj)
TYPE_VIRTIO_TABLET);
}
-static void virtio_host_initfn(Object *obj)
-{
- VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
-
- virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
- TYPE_VIRTIO_INPUT_HOST);
-}
-
static const TypeInfo virtio_input_pci_info = {
.name = TYPE_VIRTIO_INPUT_PCI,
.parent = TYPE_VIRTIO_PCI,
@@ -2180,12 +2172,22 @@ static const TypeInfo virtio_tablet_pci_info = {
.instance_init = virtio_tablet_initfn,
};
+#ifdef CONFIG_LINUX
+static void virtio_host_initfn(Object *obj)
+{
+ VirtIOInputHostPCI *dev = VIRTIO_INPUT_HOST_PCI(obj);
+
+ virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev),
+ TYPE_VIRTIO_INPUT_HOST);
+}
+
static const TypeInfo virtio_host_pci_info = {
.name = TYPE_VIRTIO_INPUT_HOST_PCI,
.parent = TYPE_VIRTIO_INPUT_PCI,
.instance_size = sizeof(VirtIOInputHostPCI),
.instance_init = virtio_host_initfn,
};
+#endif
/* virtio-pci-bus */
@@ -2233,7 +2235,9 @@ static void virtio_pci_register_types(void)
type_register_static(&virtio_keyboard_pci_info);
type_register_static(&virtio_mouse_pci_info);
type_register_static(&virtio_tablet_pci_info);
+#ifdef CONFIG_LINUX
type_register_static(&virtio_host_pci_info);
+#endif
type_register_static(&virtio_pci_bus_info);
type_register_static(&virtio_pci_info);
#ifdef CONFIG_VIRTFS
diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h
index b6c442f522..801c23aef3 100644
--- a/hw/virtio/virtio-pci.h
+++ b/hw/virtio/virtio-pci.h
@@ -267,6 +267,8 @@ struct VirtIOInputHIDPCI {
VirtIOInputHID vdev;
};
+#ifdef CONFIG_LINUX
+
#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci"
#define VIRTIO_INPUT_HOST_PCI(obj) \
OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI)
@@ -276,6 +278,8 @@ struct VirtIOInputHostPCI {
VirtIOInputHost vdev;
};
+#endif
+
/*
* virtio-gpu-pci: This extends VirtioPCIProxy.
*/
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 038b54d94b..8057aedaa6 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -114,6 +114,19 @@ typedef struct DeviceClass {
* TODO remove once we're there
*/
bool cannot_instantiate_with_device_add_yet;
+ /*
+ * Does this device model survive object_unref(object_new(TNAME))?
+ * All device models should, and this flag shouldn't exist. Some
+ * devices crash in object_new(), some crash or hang in
+ * object_unref(). Makes introspecting properties with
+ * qmp_device_list_properties() dangerous. Bad, because it's used
+ * by -device FOO,help. This flag serves to protect that code.
+ * It should never be set without a comment explaining why it is
+ * set.
+ * TODO remove once we're there
+ */
+ bool cannot_destroy_with_object_finalize_yet;
+
bool hotpluggable;
/* callbacks */
diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h
index 889676147a..9b279d7023 100644
--- a/include/hw/virtio/virtio-gpu.h
+++ b/include/hw/virtio/virtio-gpu.h
@@ -56,8 +56,19 @@ struct virtio_gpu_requested_state {
int x, y;
};
+enum virtio_gpu_conf_flags {
+ VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1,
+ VIRTIO_GPU_FLAG_STATS_ENABLED,
+};
+
+#define virtio_gpu_virgl_enabled(_cfg) \
+ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_VIRGL_ENABLED))
+#define virtio_gpu_stats_enabled(_cfg) \
+ (_cfg.flags & (1 << VIRTIO_GPU_FLAG_STATS_ENABLED))
+
struct virtio_gpu_conf {
uint32_t max_outputs;
+ uint32_t flags;
};
struct virtio_gpu_ctrl_command {
@@ -92,11 +103,13 @@ typedef struct VirtIOGPU {
int enabled_output_bitmask;
struct virtio_gpu_config virtio_config;
+ bool use_virgl_renderer;
+ bool renderer_inited;
QEMUTimer *fence_poll;
QEMUTimer *print_stats;
+ uint32_t inflight;
struct {
- uint32_t inflight;
uint32_t max_inflight;
uint32_t requests;
uint32_t req_3d;
@@ -139,4 +152,11 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab,
struct iovec **iov);
void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count);
+/* virtio-gpu-3d.c */
+void virtio_gpu_virgl_process_cmd(VirtIOGPU *g,
+ struct virtio_gpu_ctrl_command *cmd);
+void virtio_gpu_virgl_fence_poll(VirtIOGPU *g);
+void virtio_gpu_virgl_reset(VirtIOGPU *g);
+int virtio_gpu_virgl_init(VirtIOGPU *g);
+
#endif
diff --git a/include/net/filter.h b/include/net/filter.h
new file mode 100644
index 0000000000..2deda362a6
--- /dev/null
+++ b/include/net/filter.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 FUJITSU LIMITED
+ * Author: Yang Hongyang <yanghy@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_NET_FILTER_H
+#define QEMU_NET_FILTER_H
+
+#include "qom/object.h"
+#include "qemu-common.h"
+#include "qemu/typedefs.h"
+#include "net/queue.h"
+
+#define TYPE_NETFILTER "netfilter"
+#define NETFILTER(obj) \
+ OBJECT_CHECK(NetFilterState, (obj), TYPE_NETFILTER)
+#define NETFILTER_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(NetFilterClass, (obj), TYPE_NETFILTER)
+#define NETFILTER_CLASS(klass) \
+ OBJECT_CLASS_CHECK(NetFilterClass, (klass), TYPE_NETFILTER)
+
+typedef void (FilterSetup) (NetFilterState *nf, Error **errp);
+typedef void (FilterCleanup) (NetFilterState *nf);
+/*
+ * Return:
+ * 0: finished handling the packet, we should continue
+ * size: filter stolen this packet, we stop pass this packet further
+ */
+typedef ssize_t (FilterReceiveIOV)(NetFilterState *nc,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb);
+
+typedef struct NetFilterClass {
+ ObjectClass parent_class;
+
+ /* optional */
+ FilterSetup *setup;
+ FilterCleanup *cleanup;
+ /* mandatory */
+ FilterReceiveIOV *receive_iov;
+} NetFilterClass;
+
+
+struct NetFilterState {
+ /* private */
+ Object parent;
+
+ /* protected */
+ char *netdev_id;
+ NetClientState *netdev;
+ NetFilterDirection direction;
+ char info_str[256];
+ QTAILQ_ENTRY(NetFilterState) next;
+};
+
+ssize_t qemu_netfilter_receive(NetFilterState *nf,
+ NetFilterDirection direction,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb);
+
+/* pass the packet to the next filter */
+ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque);
+
+#endif /* QEMU_NET_FILTER_H */
diff --git a/include/net/net.h b/include/net/net.h
index 6a6cbef24a..7af3e15f83 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -92,6 +92,7 @@ struct NetClientState {
NetClientDestructor *destructor;
unsigned int queue_index;
unsigned rxfilter_notify_enabled:1;
+ QTAILQ_HEAD(, NetFilterState) filters;
};
typedef struct NICState {
@@ -151,11 +152,6 @@ void qemu_check_nic_model(NICInfo *nd, const char *model);
int qemu_find_nic_model(NICInfo *nd, const char * const *models,
const char *default_model);
-ssize_t qemu_deliver_packet(NetClientState *sender,
- unsigned flags,
- const uint8_t *data,
- size_t size,
- void *opaque);
ssize_t qemu_deliver_packet_iov(NetClientState *sender,
unsigned flags,
const struct iovec *iov,
diff --git a/include/net/queue.h b/include/net/queue.h
index fc02b33915..5469fdbeaa 100644
--- a/include/net/queue.h
+++ b/include/net/queue.h
@@ -34,7 +34,25 @@ typedef void (NetPacketSent) (NetClientState *sender, ssize_t ret);
#define QEMU_NET_PACKET_FLAG_NONE 0
#define QEMU_NET_PACKET_FLAG_RAW (1<<0)
-NetQueue *qemu_new_net_queue(void *opaque);
+/* Returns:
+ * >0 - success
+ * 0 - queue packet for future redelivery
+ * <0 - failure (discard packet)
+ */
+typedef ssize_t (NetQueueDeliverFunc)(NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque);
+
+NetQueue *qemu_new_net_queue(NetQueueDeliverFunc *deliver, void *opaque);
+
+void qemu_net_queue_append_iov(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb);
void qemu_del_net_queue(NetQueue *queue);
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 3a835ffb9b..ee1ce1d44d 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -45,6 +45,7 @@ typedef struct Monitor Monitor;
typedef struct MouseTransformInfo MouseTransformInfo;
typedef struct MSIMessage MSIMessage;
typedef struct NetClientState NetClientState;
+typedef struct NetFilterState NetFilterState;
typedef struct NICInfo NICInfo;
typedef struct PcGuestInfo PcGuestInfo;
typedef struct PCIBridge PCIBridge;
diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h
index b003c67059..43f1850b6b 100644
--- a/include/standard-headers/linux/input.h
+++ b/include/standard-headers/linux/input.h
@@ -887,8 +887,8 @@ struct input_keymap_entry {
#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */
#define SW_LINEIN_INSERT 0x0d /* set = inserted */
#define SW_MUTE_DEVICE 0x0e /* set = device disabled */
-#define SW_MAX 0x0f
-#define SW_CNT (SW_MAX+1)
+#define SW_MAX_ 0x0f
+#define SW_CNT (SW_MAX_+1)
/*
* Misc events
diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h
index 72ef815f51..76e5e52929 100644
--- a/include/standard-headers/linux/virtio_gpu.h
+++ b/include/standard-headers/linux/virtio_gpu.h
@@ -40,6 +40,8 @@
#include "standard-headers/linux/types.h"
+#define VIRTIO_GPU_FEATURE_VIRGL 0
+
enum virtio_gpu_ctrl_type {
VIRTIO_GPU_UNDEFINED = 0,
@@ -52,6 +54,18 @@ enum virtio_gpu_ctrl_type {
VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+ VIRTIO_GPU_CMD_GET_CAPSET_INFO,
+ VIRTIO_GPU_CMD_GET_CAPSET,
+
+ /* 3d commands */
+ VIRTIO_GPU_CMD_CTX_CREATE = 0x0200,
+ VIRTIO_GPU_CMD_CTX_DESTROY,
+ VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE,
+ VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_3D,
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D,
+ VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D,
+ VIRTIO_GPU_CMD_SUBMIT_3D,
/* cursor commands */
VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
@@ -60,6 +74,8 @@ enum virtio_gpu_ctrl_type {
/* success responses */
VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+ VIRTIO_GPU_RESP_OK_CAPSET_INFO,
+ VIRTIO_GPU_RESP_OK_CAPSET,
/* error responses */
VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
@@ -180,13 +196,107 @@ struct virtio_gpu_resp_display_info {
} pmodes[VIRTIO_GPU_MAX_SCANOUTS];
};
+/* data passed in the control vq, 3d related */
+
+struct virtio_gpu_box {
+ uint32_t x, y, z;
+ uint32_t w, h, d;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D */
+struct virtio_gpu_transfer_host_3d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_box box;
+ uint64_t offset;
+ uint32_t resource_id;
+ uint32_t level;
+ uint32_t stride;
+ uint32_t layer_stride;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_3D */
+#define VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP (1 << 0)
+struct virtio_gpu_resource_create_3d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t target;
+ uint32_t format;
+ uint32_t bind;
+ uint32_t width;
+ uint32_t height;
+ uint32_t depth;
+ uint32_t array_size;
+ uint32_t last_level;
+ uint32_t nr_samples;
+ uint32_t flags;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_CTX_CREATE */
+struct virtio_gpu_ctx_create {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t nlen;
+ uint32_t padding;
+ char debug_name[64];
+};
+
+/* VIRTIO_GPU_CMD_CTX_DESTROY */
+struct virtio_gpu_ctx_destroy {
+ struct virtio_gpu_ctrl_hdr hdr;
+};
+
+/* VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE */
+struct virtio_gpu_ctx_resource {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_SUBMIT_3D */
+struct virtio_gpu_cmd_submit {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t size;
+ uint32_t padding;
+};
+
+#define VIRTIO_GPU_CAPSET_VIRGL 1
+
+/* VIRTIO_GPU_CMD_GET_CAPSET_INFO */
+struct virtio_gpu_get_capset_info {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t capset_index;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_CAPSET_INFO */
+struct virtio_gpu_resp_capset_info {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t capset_id;
+ uint32_t capset_max_version;
+ uint32_t capset_max_size;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_GET_CAPSET */
+struct virtio_gpu_get_capset {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t capset_id;
+ uint32_t capset_version;
+};
+
+/* VIRTIO_GPU_RESP_OK_CAPSET */
+struct virtio_gpu_resp_capset {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint8_t capset_data[];
+};
+
#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
struct virtio_gpu_config {
uint32_t events_read;
uint32_t events_clear;
uint32_t num_scanouts;
- uint32_t reserved;
+ uint32_t num_capsets;
};
/* simple formats for fbcon/X use */
diff --git a/include/ui/console.h b/include/ui/console.h
index 047a2b4640..d887f911f3 100644
--- a/include/ui/console.h
+++ b/include/ui/console.h
@@ -157,6 +157,14 @@ void cursor_set_mono(QEMUCursor *c,
void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *mask);
void cursor_get_mono_mask(QEMUCursor *c, int transparent, uint8_t *mask);
+typedef void *QEMUGLContext;
+typedef struct QEMUGLParams QEMUGLParams;
+
+struct QEMUGLParams {
+ int major_ver;
+ int minor_ver;
+};
+
typedef struct DisplayChangeListenerOps {
const char *dpy_name;
@@ -183,6 +191,21 @@ typedef struct DisplayChangeListenerOps {
int x, int y, int on);
void (*dpy_cursor_define)(DisplayChangeListener *dcl,
QEMUCursor *cursor);
+
+ QEMUGLContext (*dpy_gl_ctx_create)(DisplayChangeListener *dcl,
+ QEMUGLParams *params);
+ void (*dpy_gl_ctx_destroy)(DisplayChangeListener *dcl,
+ QEMUGLContext ctx);
+ int (*dpy_gl_ctx_make_current)(DisplayChangeListener *dcl,
+ QEMUGLContext ctx);
+ QEMUGLContext (*dpy_gl_ctx_get_current)(DisplayChangeListener *dcl);
+
+ void (*dpy_gl_scanout)(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+ void (*dpy_gl_update)(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+
} DisplayChangeListenerOps;
struct DisplayChangeListener {
@@ -244,6 +267,20 @@ bool dpy_cursor_define_supported(QemuConsole *con);
bool dpy_gfx_check_format(QemuConsole *con,
pixman_format_code_t format);
+void dpy_gl_scanout(QemuConsole *con,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+void dpy_gl_update(QemuConsole *con,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+
+QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
+ QEMUGLParams *params);
+void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx);
+int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx);
+QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con);
+
+bool console_has_gl(QemuConsole *con);
+
static inline int surface_stride(DisplaySurface *s)
{
return pixman_image_get_stride(s->image);
diff --git a/include/ui/egl-context.h b/include/ui/egl-context.h
new file mode 100644
index 0000000000..f004ce11a7
--- /dev/null
+++ b/include/ui/egl-context.h
@@ -0,0 +1,14 @@
+#ifndef EGL_CONTEXT_H
+#define EGL_CONTEXT_H
+
+#include "ui/console.h"
+#include "ui/egl-helpers.h"
+
+QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params);
+void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx);
+int qemu_egl_make_context_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx);
+QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl);
+
+#endif /* EGL_CONTEXT_H */
diff --git a/include/ui/gtk.h b/include/ui/gtk.h
index 0359333fce..bf289cff4c 100644
--- a/include/ui/gtk.h
+++ b/include/ui/gtk.h
@@ -20,6 +20,7 @@
#if defined(CONFIG_OPENGL)
#include "ui/egl-helpers.h"
+#include "ui/egl-context.h"
#endif
/* Compatibility define to let us build on both Gtk2 and Gtk3 */
@@ -46,6 +47,11 @@ typedef struct VirtualGfxConsole {
EGLContext ectx;
EGLSurface esurface;
int glupdates;
+ int x, y, w, h;
+ GLuint tex_id;
+ GLuint fbo_id;
+ bool y0_top;
+ bool scanout_mode;
#endif
} VirtualGfxConsole;
@@ -90,6 +96,39 @@ void gd_egl_update(DisplayChangeListener *dcl,
void gd_egl_refresh(DisplayChangeListener *dcl);
void gd_egl_switch(DisplayChangeListener *dcl,
DisplaySurface *surface);
+QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params);
+void gd_egl_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h);
+void gd_egl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
void gtk_egl_init(void);
+int gd_egl_make_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx);
+
+/* ui/gtk-gl-area.c */
+void gd_gl_area_init(VirtualConsole *vc);
+void gd_gl_area_draw(VirtualConsole *vc);
+void gd_gl_area_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h);
+void gd_gl_area_refresh(DisplayChangeListener *dcl);
+void gd_gl_area_switch(DisplayChangeListener *dcl,
+ DisplaySurface *surface);
+QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params);
+void gd_gl_area_destroy_context(DisplayChangeListener *dcl,
+ QEMUGLContext ctx);
+void gd_gl_area_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h);
+void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h);
+void gtk_gl_area_init(void);
+QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl);
+int gd_gl_area_make_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx);
#endif /* UI_GTK_H */
diff --git a/include/ui/shader.h b/include/ui/shader.h
index 8509596ac0..f7d86188bf 100644
--- a/include/ui/shader.h
+++ b/include/ui/shader.h
@@ -3,7 +3,9 @@
#include <epoxy/gl.h>
-void qemu_gl_run_texture_blit(GLint texture_blit_prog);
+GLuint qemu_gl_init_texture_blit(GLint texture_blit_prog);
+void qemu_gl_run_texture_blit(GLint texture_blit_prog,
+ GLint texture_blit_vao);
GLuint qemu_gl_create_compile_shader(GLenum type, const GLchar *src);
GLuint qemu_gl_create_link_program(GLuint vert, GLuint frag);
diff --git a/memory.c b/memory.c
index 1b03d2251c..2eb1597518 100644
--- a/memory.c
+++ b/memory.c
@@ -1304,7 +1304,22 @@ static void memory_region_finalize(Object *obj)
{
MemoryRegion *mr = MEMORY_REGION(obj);
- assert(QTAILQ_EMPTY(&mr->subregions));
+ assert(!mr->container);
+
+ /* We know the region is not visible in any address space (it
+ * does not have a container and cannot be a root either because
+ * it has no references, so we can blindly clear mr->enabled.
+ * memory_region_set_enabled instead could trigger a transaction
+ * and cause an infinite loop.
+ */
+ mr->enabled = false;
+ memory_region_transaction_begin();
+ while (!QTAILQ_EMPTY(&mr->subregions)) {
+ MemoryRegion *subregion = QTAILQ_FIRST(&mr->subregions);
+ memory_region_del_subregion(mr, subregion);
+ }
+ memory_region_transaction_commit();
+
mr->destructor(mr);
memory_region_clear_coalescing(mr);
g_free((char *)mr->name);
diff --git a/net/Makefile.objs b/net/Makefile.objs
index ec19cb31d9..5fa2f9731d 100644
--- a/net/Makefile.objs
+++ b/net/Makefile.objs
@@ -13,3 +13,5 @@ common-obj-$(CONFIG_HAIKU) += tap-haiku.o
common-obj-$(CONFIG_SLIRP) += slirp.o
common-obj-$(CONFIG_VDE) += vde.o
common-obj-$(CONFIG_NETMAP) += netmap.o
+common-obj-y += filter.o
+common-obj-y += filter-buffer.o
diff --git a/net/filter-buffer.c b/net/filter-buffer.c
new file mode 100644
index 0000000000..57be149413
--- /dev/null
+++ b/net/filter-buffer.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2015 FUJITSU LIMITED
+ * Author: Yang Hongyang <yanghy@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 "net/filter.h"
+#include "net/queue.h"
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "qemu/iov.h"
+#include "qapi/qmp/qerror.h"
+#include "qapi-visit.h"
+#include "qom/object.h"
+
+#define TYPE_FILTER_BUFFER "filter-buffer"
+
+#define FILTER_BUFFER(obj) \
+ OBJECT_CHECK(FilterBufferState, (obj), TYPE_FILTER_BUFFER)
+
+typedef struct FilterBufferState {
+ NetFilterState parent_obj;
+
+ NetQueue *incoming_queue;
+ uint32_t interval;
+ QEMUTimer release_timer;
+} FilterBufferState;
+
+static void filter_buffer_flush(NetFilterState *nf)
+{
+ FilterBufferState *s = FILTER_BUFFER(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);
+ }
+}
+
+static void filter_buffer_release_timer(void *opaque)
+{
+ NetFilterState *nf = opaque;
+ FilterBufferState *s = FILTER_BUFFER(nf);
+
+ /*
+ * Note: filter_buffer_flush() drops packets that can't be sent
+ * TODO: We should leave them queued. But currently there's no way
+ * for the next filter or receiver to notify us that it can receive
+ * more packets.
+ */
+ filter_buffer_flush(nf);
+ /* Timer rearmed to fire again in s->interval microseconds. */
+ timer_mod(&s->release_timer,
+ qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval);
+}
+
+/* filter APIs */
+static ssize_t filter_buffer_receive_iov(NetFilterState *nf,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ FilterBufferState *s = FILTER_BUFFER(nf);
+
+ /*
+ * We return size when buffer a packet, the sender will take it as
+ * a already sent packet, so sent_cb should not be called later.
+ *
+ * FIXME: Even if the guest can't receive packets for some reasons,
+ * the filter can still accept packets until its internal queue is full.
+ * For example:
+ * For some reason, receiver could not receive more packets
+ * (.can_receive() returns zero). Without a filter, at most one packet
+ * will be queued in incoming queue and sender's poll will be disabled
+ * unit its sent_cb() was called. With a filter, it will keep receiving
+ * the packets without caring about the receiver. This is suboptimal.
+ * May need more thoughts (e.g keeping sent_cb).
+ */
+ qemu_net_queue_append_iov(s->incoming_queue, sender, flags,
+ iov, iovcnt, NULL);
+ return iov_size(iov, iovcnt);
+}
+
+static void filter_buffer_cleanup(NetFilterState *nf)
+{
+ FilterBufferState *s = FILTER_BUFFER(nf);
+
+ if (s->interval) {
+ timer_del(&s->release_timer);
+ }
+
+ /* flush packets */
+ if (s->incoming_queue) {
+ filter_buffer_flush(nf);
+ g_free(s->incoming_queue);
+ }
+}
+
+static void filter_buffer_setup(NetFilterState *nf, Error **errp)
+{
+ FilterBufferState *s = FILTER_BUFFER(nf);
+
+ /*
+ * We may want to accept zero interval when VM FT solutions like MC
+ * or COLO use this filter to release packets on demand.
+ */
+ if (!s->interval) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "interval",
+ "a non-zero interval");
+ return;
+ }
+
+ s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
+ if (s->interval) {
+ timer_init_us(&s->release_timer, QEMU_CLOCK_VIRTUAL,
+ filter_buffer_release_timer, nf);
+ /* Timer armed to fire in s->interval microseconds. */
+ timer_mod(&s->release_timer,
+ qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + s->interval);
+ }
+}
+
+static void filter_buffer_class_init(ObjectClass *oc, void *data)
+{
+ NetFilterClass *nfc = NETFILTER_CLASS(oc);
+
+ nfc->setup = filter_buffer_setup;
+ nfc->cleanup = filter_buffer_cleanup;
+ nfc->receive_iov = filter_buffer_receive_iov;
+}
+
+static void filter_buffer_get_interval(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ FilterBufferState *s = FILTER_BUFFER(obj);
+ uint32_t value = s->interval;
+
+ visit_type_uint32(v, &value, name, errp);
+}
+
+static void filter_buffer_set_interval(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ FilterBufferState *s = FILTER_BUFFER(obj);
+ Error *local_err = NULL;
+ uint32_t value;
+
+ visit_type_uint32(v, &value, name, &local_err);
+ if (local_err) {
+ goto out;
+ }
+ if (!value) {
+ error_setg(&local_err, "Property '%s.%s' requires a positive value",
+ object_get_typename(obj), name);
+ goto out;
+ }
+ s->interval = value;
+
+out:
+ error_propagate(errp, local_err);
+}
+
+static void filter_buffer_init(Object *obj)
+{
+ object_property_add(obj, "interval", "int",
+ filter_buffer_get_interval,
+ filter_buffer_set_interval, NULL, NULL, NULL);
+}
+
+static const TypeInfo filter_buffer_info = {
+ .name = TYPE_FILTER_BUFFER,
+ .parent = TYPE_NETFILTER,
+ .class_init = filter_buffer_class_init,
+ .instance_init = filter_buffer_init,
+ .instance_size = sizeof(FilterBufferState),
+};
+
+static void register_types(void)
+{
+ type_register_static(&filter_buffer_info);
+}
+
+type_init(register_types);
diff --git a/net/filter.c b/net/filter.c
new file mode 100644
index 0000000000..326f2b5ac8
--- /dev/null
+++ b/net/filter.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2015 FUJITSU LIMITED
+ * Author: Yang Hongyang <yanghy@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-common.h"
+#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
+
+#include "net/filter.h"
+#include "net/net.h"
+#include "net/vhost_net.h"
+#include "qom/object_interfaces.h"
+#include "qemu/iov.h"
+#include "qapi/string-output-visitor.h"
+
+ssize_t qemu_netfilter_receive(NetFilterState *nf,
+ NetFilterDirection direction,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
+{
+ if (nf->direction == direction ||
+ nf->direction == NET_FILTER_DIRECTION_ALL) {
+ return NETFILTER_GET_CLASS(OBJECT(nf))->receive_iov(
+ nf, sender, flags, iov, iovcnt, sent_cb);
+ }
+
+ return 0;
+}
+
+ssize_t qemu_netfilter_pass_to_next(NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ void *opaque)
+{
+ int ret = 0;
+ int direction;
+ NetFilterState *nf = opaque;
+ NetFilterState *next = QTAILQ_NEXT(nf, next);
+
+ if (!sender || !sender->peer) {
+ /* no receiver, or sender been deleted, no need to pass it further */
+ goto out;
+ }
+
+ if (nf->direction == NET_FILTER_DIRECTION_ALL) {
+ if (sender == nf->netdev) {
+ /* This packet is sent by netdev itself */
+ direction = NET_FILTER_DIRECTION_TX;
+ } else {
+ direction = NET_FILTER_DIRECTION_RX;
+ }
+ } else {
+ direction = nf->direction;
+ }
+
+ while (next) {
+ /*
+ * if qemu_netfilter_pass_to_next been called, means that
+ * the packet has been hold by filter and has already retured size
+ * to the sender, so sent_cb shouldn't be called later, just
+ * pass NULL to next.
+ */
+ ret = qemu_netfilter_receive(next, direction, sender, flags, iov,
+ iovcnt, NULL);
+ if (ret) {
+ return ret;
+ }
+ next = QTAILQ_NEXT(next, next);
+ }
+
+ /*
+ * We have gone through all filters, pass it to receiver.
+ * Do the valid check again incase sender or receiver been
+ * deleted while we go through filters.
+ */
+ if (sender && sender->peer) {
+ qemu_net_queue_send_iov(sender->peer->incoming_queue,
+ sender, flags, iov, iovcnt, NULL);
+ }
+
+out:
+ /* no receiver, or sender been deleted */
+ return iov_size(iov, iovcnt);
+}
+
+static char *netfilter_get_netdev_id(Object *obj, Error **errp)
+{
+ NetFilterState *nf = NETFILTER(obj);
+
+ return g_strdup(nf->netdev_id);
+}
+
+static void netfilter_set_netdev_id(Object *obj, const char *str, Error **errp)
+{
+ NetFilterState *nf = NETFILTER(obj);
+
+ nf->netdev_id = g_strdup(str);
+}
+
+static int netfilter_get_direction(Object *obj, Error **errp G_GNUC_UNUSED)
+{
+ NetFilterState *nf = NETFILTER(obj);
+ return nf->direction;
+}
+
+static void netfilter_set_direction(Object *obj, int direction, Error **errp)
+{
+ NetFilterState *nf = NETFILTER(obj);
+ nf->direction = direction;
+}
+
+static void netfilter_init(Object *obj)
+{
+ object_property_add_str(obj, "netdev",
+ netfilter_get_netdev_id, netfilter_set_netdev_id,
+ NULL);
+ object_property_add_enum(obj, "queue", "NetFilterDirection",
+ NetFilterDirection_lookup,
+ netfilter_get_direction, netfilter_set_direction,
+ NULL);
+}
+
+static void netfilter_complete(UserCreatable *uc, Error **errp)
+{
+ NetFilterState *nf = NETFILTER(uc);
+ NetClientState *ncs[MAX_QUEUE_NUM];
+ NetFilterClass *nfc = NETFILTER_GET_CLASS(uc);
+ int queues;
+ Error *local_err = NULL;
+ char *str, *info;
+ ObjectProperty *prop;
+ StringOutputVisitor *ov;
+
+ if (!nf->netdev_id) {
+ error_setg(errp, "Parameter 'netdev' is required");
+ return;
+ }
+
+ queues = qemu_find_net_clients_except(nf->netdev_id, ncs,
+ NET_CLIENT_OPTIONS_KIND_NIC,
+ MAX_QUEUE_NUM);
+ if (queues < 1) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "netdev",
+ "a network backend id");
+ return;
+ } else if (queues > 1) {
+ error_setg(errp, "multiqueue is not supported");
+ return;
+ }
+
+ if (get_vhost_net(ncs[0])) {
+ error_setg(errp, "Vhost is not supported");
+ return;
+ }
+
+ nf->netdev = ncs[0];
+
+ if (nfc->setup) {
+ nfc->setup(nf, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return;
+ }
+ }
+ QTAILQ_INSERT_TAIL(&nf->netdev->filters, nf, next);
+
+ /* generate info str */
+ QTAILQ_FOREACH(prop, &OBJECT(nf)->properties, node) {
+ if (!strcmp(prop->name, "type")) {
+ continue;
+ }
+ ov = string_output_visitor_new(false);
+ object_property_get(OBJECT(nf), string_output_get_visitor(ov),
+ prop->name, errp);
+ str = string_output_get_string(ov);
+ string_output_visitor_cleanup(ov);
+ info = g_strdup_printf(",%s=%s", prop->name, str);
+ g_strlcat(nf->info_str, info, sizeof(nf->info_str));
+ g_free(str);
+ g_free(info);
+ }
+}
+
+static void netfilter_finalize(Object *obj)
+{
+ NetFilterState *nf = NETFILTER(obj);
+ NetFilterClass *nfc = NETFILTER_GET_CLASS(obj);
+
+ if (nfc->cleanup) {
+ nfc->cleanup(nf);
+ }
+
+ if (nf->netdev && !QTAILQ_EMPTY(&nf->netdev->filters)) {
+ QTAILQ_REMOVE(&nf->netdev->filters, nf, next);
+ }
+}
+
+static void netfilter_class_init(ObjectClass *oc, void *data)
+{
+ UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+
+ ucc->complete = netfilter_complete;
+}
+
+static const TypeInfo netfilter_info = {
+ .name = TYPE_NETFILTER,
+ .parent = TYPE_OBJECT,
+ .abstract = true,
+ .class_size = sizeof(NetFilterClass),
+ .class_init = netfilter_class_init,
+ .instance_size = sizeof(NetFilterState),
+ .instance_init = netfilter_init,
+ .instance_finalize = netfilter_finalize,
+ .interfaces = (InterfaceInfo[]) {
+ { TYPE_USER_CREATABLE },
+ { }
+ }
+};
+
+static void register_types(void)
+{
+ type_register_static(&netfilter_info);
+}
+
+type_init(register_types);
diff --git a/net/net.c b/net/net.c
index 28a5597b8d..39af8930b4 100644
--- a/net/net.c
+++ b/net/net.c
@@ -44,6 +44,7 @@
#include "qapi/opts-visitor.h"
#include "qapi/dealloc-visitor.h"
#include "sysemu/sysemu.h"
+#include "net/filter.h"
/* Net bridge is currently not supported for W32. */
#if !defined(_WIN32)
@@ -285,8 +286,9 @@ static void qemu_net_client_setup(NetClientState *nc,
}
QTAILQ_INSERT_TAIL(&net_clients, nc, next);
- nc->incoming_queue = qemu_new_net_queue(nc);
+ nc->incoming_queue = qemu_new_net_queue(qemu_deliver_packet_iov, nc);
nc->destructor = destructor;
+ QTAILQ_INIT(&nc->filters);
}
NetClientState *qemu_new_net_client(NetClientInfo *info,
@@ -384,6 +386,7 @@ void qemu_del_net_client(NetClientState *nc)
{
NetClientState *ncs[MAX_QUEUE_NUM];
int queues, i;
+ NetFilterState *nf, *next;
assert(nc->info->type != NET_CLIENT_OPTIONS_KIND_NIC);
@@ -395,6 +398,10 @@ void qemu_del_net_client(NetClientState *nc)
MAX_QUEUE_NUM);
assert(queues != 0);
+ QTAILQ_FOREACH_SAFE(nf, &nc->filters, next, next) {
+ object_unparent(OBJECT(nf));
+ }
+
/* If there is a peer NIC, delete and cleanup client, but do not free. */
if (nc->peer && nc->peer->info->type == NET_CLIENT_OPTIONS_KIND_NIC) {
NICState *nic = qemu_get_nic(nc->peer);
@@ -554,34 +561,42 @@ int qemu_can_send_packet(NetClientState *sender)
return 1;
}
-ssize_t qemu_deliver_packet(NetClientState *sender,
- unsigned flags,
- const uint8_t *data,
- size_t size,
- void *opaque)
+static ssize_t filter_receive_iov(NetClientState *nc,
+ NetFilterDirection direction,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
{
- NetClientState *nc = opaque;
- ssize_t ret;
+ ssize_t ret = 0;
+ NetFilterState *nf = NULL;
- if (nc->link_down) {
- return size;
- }
-
- if (nc->receive_disabled) {
- return 0;
+ QTAILQ_FOREACH(nf, &nc->filters, next) {
+ ret = qemu_netfilter_receive(nf, direction, sender, flags, iov,
+ iovcnt, sent_cb);
+ if (ret) {
+ return ret;
+ }
}
- if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
- ret = nc->info->receive_raw(nc, data, size);
- } else {
- ret = nc->info->receive(nc, data, size);
- }
+ return ret;
+}
- if (ret == 0) {
- nc->receive_disabled = 1;
- }
+static ssize_t filter_receive(NetClientState *nc,
+ NetFilterDirection direction,
+ NetClientState *sender,
+ unsigned flags,
+ const uint8_t *data,
+ size_t size,
+ NetPacketSent *sent_cb)
+{
+ struct iovec iov = {
+ .iov_base = (void *)data,
+ .iov_len = size
+ };
- return ret;
+ return filter_receive_iov(nc, direction, sender, flags, &iov, 1, sent_cb);
}
void qemu_purge_queued_packets(NetClientState *nc)
@@ -625,6 +640,7 @@ static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender,
NetPacketSent *sent_cb)
{
NetQueue *queue;
+ int ret;
#ifdef DEBUG_NET
printf("qemu_send_packet_async:\n");
@@ -635,6 +651,19 @@ static ssize_t qemu_send_packet_async_with_flags(NetClientState *sender,
return size;
}
+ /* Let filters handle the packet first */
+ ret = filter_receive(sender, NET_FILTER_DIRECTION_TX,
+ sender, flags, buf, size, sent_cb);
+ if (ret) {
+ return ret;
+ }
+
+ ret = filter_receive(sender->peer, NET_FILTER_DIRECTION_RX,
+ sender, flags, buf, size, sent_cb);
+ if (ret) {
+ return ret;
+ }
+
queue = sender->peer->incoming_queue;
return qemu_net_queue_send(queue, sender, flags, buf, size, sent_cb);
@@ -660,14 +689,25 @@ ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size)
}
static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov,
- int iovcnt)
+ int iovcnt, unsigned flags)
{
- uint8_t buffer[NET_BUFSIZE];
+ uint8_t buf[NET_BUFSIZE];
+ uint8_t *buffer;
size_t offset;
- offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer));
+ if (iovcnt == 1) {
+ buffer = iov[0].iov_base;
+ offset = iov[0].iov_len;
+ } else {
+ buffer = buf;
+ offset = iov_to_buf(iov, iovcnt, 0, buffer, sizeof(buffer));
+ }
- return nc->info->receive(nc, buffer, offset);
+ if (flags & QEMU_NET_PACKET_FLAG_RAW && nc->info->receive_raw) {
+ return nc->info->receive_raw(nc, buffer, offset);
+ } else {
+ return nc->info->receive(nc, buffer, offset);
+ }
}
ssize_t qemu_deliver_packet_iov(NetClientState *sender,
@@ -690,7 +730,7 @@ ssize_t qemu_deliver_packet_iov(NetClientState *sender,
if (nc->info->receive_iov) {
ret = nc->info->receive_iov(nc, iov, iovcnt);
} else {
- ret = nc_sendv_compat(nc, iov, iovcnt);
+ ret = nc_sendv_compat(nc, iov, iovcnt, flags);
}
if (ret == 0) {
@@ -705,11 +745,25 @@ ssize_t qemu_sendv_packet_async(NetClientState *sender,
NetPacketSent *sent_cb)
{
NetQueue *queue;
+ int ret;
if (sender->link_down || !sender->peer) {
return iov_size(iov, iovcnt);
}
+ /* Let filters handle the packet first */
+ ret = filter_receive_iov(sender, NET_FILTER_DIRECTION_TX, sender,
+ QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb);
+ if (ret) {
+ return ret;
+ }
+
+ ret = filter_receive_iov(sender->peer, NET_FILTER_DIRECTION_RX, sender,
+ QEMU_NET_PACKET_FLAG_NONE, iov, iovcnt, sent_cb);
+ if (ret) {
+ return ret;
+ }
+
queue = sender->peer->incoming_queue;
return qemu_net_queue_send_iov(queue, sender,
@@ -1125,10 +1179,21 @@ void qmp_netdev_del(const char *id, Error **errp)
void print_net_client(Monitor *mon, NetClientState *nc)
{
+ NetFilterState *nf;
+
monitor_printf(mon, "%s: index=%d,type=%s,%s\n", nc->name,
nc->queue_index,
NetClientOptionsKind_lookup[nc->info->type],
nc->info_str);
+ if (!QTAILQ_EMPTY(&nc->filters)) {
+ monitor_printf(mon, "filters:\n");
+ }
+ QTAILQ_FOREACH(nf, &nc->filters, next) {
+ monitor_printf(mon, " - %s: type=%s%s\n",
+ object_get_canonical_path_component(OBJECT(nf)),
+ object_get_typename(OBJECT(nf)),
+ nf->info_str);
+ }
}
RxFilterInfoList *qmp_query_rx_filter(bool has_name, const char *name,
diff --git a/net/queue.c b/net/queue.c
index ebbe2bb93b..de8b9d31c6 100644
--- a/net/queue.c
+++ b/net/queue.c
@@ -52,13 +52,14 @@ struct NetQueue {
void *opaque;
uint32_t nq_maxlen;
uint32_t nq_count;
+ NetQueueDeliverFunc *deliver;
QTAILQ_HEAD(packets, NetPacket) packets;
unsigned delivering : 1;
};
-NetQueue *qemu_new_net_queue(void *opaque)
+NetQueue *qemu_new_net_queue(NetQueueDeliverFunc *deliver, void *opaque)
{
NetQueue *queue;
@@ -67,6 +68,7 @@ NetQueue *qemu_new_net_queue(void *opaque)
queue->opaque = opaque;
queue->nq_maxlen = 10000;
queue->nq_count = 0;
+ queue->deliver = deliver;
QTAILQ_INIT(&queue->packets);
@@ -110,12 +112,12 @@ static void qemu_net_queue_append(NetQueue *queue,
QTAILQ_INSERT_TAIL(&queue->packets, packet, entry);
}
-static void qemu_net_queue_append_iov(NetQueue *queue,
- NetClientState *sender,
- unsigned flags,
- const struct iovec *iov,
- int iovcnt,
- NetPacketSent *sent_cb)
+void qemu_net_queue_append_iov(NetQueue *queue,
+ NetClientState *sender,
+ unsigned flags,
+ const struct iovec *iov,
+ int iovcnt,
+ NetPacketSent *sent_cb)
{
NetPacket *packet;
size_t max_len = 0;
@@ -152,9 +154,13 @@ static ssize_t qemu_net_queue_deliver(NetQueue *queue,
size_t size)
{
ssize_t ret = -1;
+ struct iovec iov = {
+ .iov_base = (void *)data,
+ .iov_len = size
+ };
queue->delivering = 1;
- ret = qemu_deliver_packet(sender, flags, data, size, queue->opaque);
+ ret = queue->deliver(sender, flags, &iov, 1, queue->opaque);
queue->delivering = 0;
return ret;
@@ -169,7 +175,7 @@ static ssize_t qemu_net_queue_deliver_iov(NetQueue *queue,
ssize_t ret = -1;
queue->delivering = 1;
- ret = qemu_deliver_packet_iov(sender, flags, iov, iovcnt, queue->opaque);
+ ret = queue->deliver(sender, flags, iov, iovcnt, queue->opaque);
queue->delivering = 0;
return ret;
diff --git a/qapi-schema.json b/qapi-schema.json
index 8b0520c2d0..a386605b6a 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -2588,6 +2588,26 @@
'opts': 'NetClientOptions' } }
##
+# @NetFilterDirection
+#
+# Indicates whether a netfilter is attached to a netdev's transmit queue or
+# receive queue or both.
+#
+# @all: the filter is attached both to the receive and the transmit
+# queue of the netdev (default).
+#
+# @rx: the filter is attached to the receive queue of the netdev,
+# where it will receive packets sent to the netdev.
+#
+# @tx: the filter is attached to the transmit queue of the netdev,
+# where it will receive packets sent by the netdev.
+#
+# Since 2.5
+##
+{ 'enum': 'NetFilterDirection',
+ 'data': [ 'all', 'rx', 'tx' ] }
+
+##
# @InetSocketAddress
#
# Captures a socket address or address range in the Internet namespace.
diff --git a/qdev-monitor.c b/qdev-monitor.c
index eb7aef2c81..a35098f711 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -50,6 +50,7 @@ static const QDevAlias qdev_alias_table[] = {
{ "lsi53c895a", "lsi" },
{ "ich9-ahci", "ahci" },
{ "kvm-pci-assign", "pci-assign" },
+ { "e1000", "e1000-82540em" },
{ }
};
@@ -237,9 +238,12 @@ int qdev_device_help(QemuOpts *opts)
return 0;
}
- qdev_get_device_class(&driver, &local_err);
- if (local_err) {
- goto error;
+ if (!object_class_by_name(driver)) {
+ const char *typename = find_typename_by_alias(driver);
+
+ if (typename) {
+ driver = typename;
+ }
}
prop_list = qmp_device_list_properties(driver, &local_err);
diff --git a/qemu-options.hx b/qemu-options.hx
index 328404ca18..2485b94b16 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -3643,6 +3643,23 @@ in PEM format, in filenames @var{ca-cert.pem}, @var{ca-crl.pem} (optional),
@var{server-cert.pem} (only servers), @var{server-key.pem} (only servers),
@var{client-cert.pem} (only clients), and @var{client-key.pem} (only clients).
+@item -object filter-buffer,id=@var{id},netdev=@var{netdevid},interval=@var{t}[,queue=@var{all|rx|tx}]
+
+Interval @var{t} can't be 0, this filter batches the packet delivery: all
+packets arriving in a given interval on netdev @var{netdevid} are delayed
+until the end of the interval. Interval is in microseconds.
+
+queue @var{all|rx|tx} is an option that can be applied to any netfilter.
+
+@option{all}: the filter is attached both to the receive and the transmit
+ queue of the netdev (default).
+
+@option{rx}: the filter is attached to the receive queue of the netdev,
+ where it will receive packets sent to the netdev.
+
+@option{tx}: the filter is attached to the transmit queue of the netdev,
+ where it will receive packets sent by the netdev.
+
@end table
ETEXI
diff --git a/qmp.c b/qmp.c
index 057a7cb5e2..d9ecedef93 100644
--- a/qmp.c
+++ b/qmp.c
@@ -515,6 +515,17 @@ DevicePropertyInfoList *qmp_device_list_properties(const char *typename,
return NULL;
}
+ if (object_class_is_abstract(klass)) {
+ error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "name",
+ "non-abstract device type");
+ return NULL;
+ }
+
+ if (DEVICE_CLASS(klass)->cannot_destroy_with_object_finalize_yet) {
+ error_setg(errp, "Can't list properties of device '%s'", typename);
+ return NULL;
+ }
+
obj = object_new(typename);
QTAILQ_FOREACH(prop, &obj->properties, node) {
diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh
index 1107619121..457ef37b95 100755
--- a/scripts/update-linux-headers.sh
+++ b/scripts/update-linux-headers.sh
@@ -53,6 +53,7 @@ cp_portable() {
-e 's/__attribute__((packed))/QEMU_PACKED/' \
-e 's/__inline__/inline/' \
-e '/sys\/ioctl.h/d' \
+ -e 's/SW_MAX/SW_MAX_/' \
"$f" > "$to/$header";
}
diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c
index 421d7e5364..ff1926a5d0 100644
--- a/target-alpha/cpu.c
+++ b/target-alpha/cpu.c
@@ -298,6 +298,13 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data)
dc->vmsd = &vmstate_alpha_cpu;
#endif
cc->gdb_num_core_regs = 67;
+
+ /*
+ * Reason: alpha_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo alpha_cpu_type_info = {
diff --git a/target-arm/cpu.c b/target-arm/cpu.c
index d7b4445413..30739fc0df 100644
--- a/target-arm/cpu.c
+++ b/target-arm/cpu.c
@@ -1427,6 +1427,17 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data)
cc->debug_excp_handler = arm_debug_excp_handler;
cc->disas_set_info = arm_disas_set_info;
+
+ /*
+ * Reason: arm_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ *
+ * Once this is fixed, the devices that create ARM CPUs should be
+ * updated not to set cannot_destroy_with_object_finalize_yet,
+ * unless they still screw up something else.
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void cpu_register(const ARMCPUInfo *info)
diff --git a/target-cris/cpu.c b/target-cris/cpu.c
index d461e074c1..8eaf5a5a31 100644
--- a/target-cris/cpu.c
+++ b/target-cris/cpu.c
@@ -309,6 +309,13 @@ static void cris_cpu_class_init(ObjectClass *oc, void *data)
cc->gdb_stop_before_watchpoint = true;
cc->disas_set_info = cris_disas_set_info;
+
+ /*
+ * Reason: cris_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo cris_cpu_type_info = {
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index c793812cc2..05d7f26bf1 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -1453,6 +1453,8 @@ static void host_x86_cpu_class_init(ObjectClass *oc, void *data)
*/
dc->props = host_x86_cpu_properties;
+ /* Reason: host_x86_cpu_initfn() dies when !kvm_enabled() */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void host_x86_cpu_initfn(Object *obj)
@@ -3190,6 +3192,12 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
#endif
cc->cpu_exec_enter = x86_cpu_exec_enter;
cc->cpu_exec_exit = x86_cpu_exec_exit;
+
+ /*
+ * Reason: x86_cpu_initfn() calls cpu_exec_init(), which saves the
+ * object in cpus -> dangling pointer after final object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo x86_cpu_type_info = {
diff --git a/target-lm32/cpu.c b/target-lm32/cpu.c
index c2b77c6986..d0ab2786ae 100644
--- a/target-lm32/cpu.c
+++ b/target-lm32/cpu.c
@@ -275,6 +275,13 @@ static void lm32_cpu_class_init(ObjectClass *oc, void *data)
cc->gdb_num_core_regs = 32 + 7;
cc->gdb_stop_before_watchpoint = true;
cc->debug_excp_handler = lm32_debug_excp_handler;
+
+ /*
+ * Reason: lm32_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void lm32_register_cpu_type(const LM32CPUInfo *info)
diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c
index 4f246da748..97527ef32a 100644
--- a/target-m68k/cpu.c
+++ b/target-m68k/cpu.c
@@ -212,6 +212,13 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data)
dc->vmsd = &vmstate_m68k_cpu;
cc->gdb_num_core_regs = 18;
cc->gdb_core_xml_file = "cf-core.xml";
+
+ /*
+ * Reason: m68k_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void register_cpu_type(const M68kCPUInfo *info)
diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c
index cbd84a22f7..52959e13b4 100644
--- a/target-microblaze/cpu.c
+++ b/target-microblaze/cpu.c
@@ -264,6 +264,12 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data)
cc->gdb_num_core_regs = 32 + 5;
cc->disas_set_info = mb_disas_set_info;
+
+ /*
+ * Reason: mb_cpu_initfn() calls cpu_exec_init(), which saves the
+ * object in cpus -> dangling pointer after final object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo mb_cpu_type_info = {
diff --git a/target-mips/cpu.c b/target-mips/cpu.c
index 4027d0f417..7fe1f0407f 100644
--- a/target-mips/cpu.c
+++ b/target-mips/cpu.c
@@ -153,6 +153,13 @@ static void mips_cpu_class_init(ObjectClass *c, void *data)
cc->gdb_num_core_regs = 73;
cc->gdb_stop_before_watchpoint = true;
+
+ /*
+ * Reason: mips_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo mips_cpu_type_info = {
diff --git a/target-moxie/cpu.c b/target-moxie/cpu.c
index 6b035aaab3..3af37799b7 100644
--- a/target-moxie/cpu.c
+++ b/target-moxie/cpu.c
@@ -114,6 +114,13 @@ static void moxie_cpu_class_init(ObjectClass *oc, void *data)
cc->get_phys_page_debug = moxie_cpu_get_phys_page_debug;
cc->vmsd = &vmstate_moxie_cpu;
#endif
+
+ /*
+ * Reason: moxie_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void moxielite_initfn(Object *obj)
diff --git a/target-openrisc/cpu.c b/target-openrisc/cpu.c
index d97f3c03c2..cc5e2d1c5d 100644
--- a/target-openrisc/cpu.c
+++ b/target-openrisc/cpu.c
@@ -177,6 +177,13 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data)
dc->vmsd = &vmstate_openrisc_cpu;
#endif
cc->gdb_num_core_regs = 32 + 3;
+
+ /*
+ * Reason: openrisc_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void cpu_register(const OpenRISCCPUInfo *info)
diff --git a/target-ppc/kvm.c b/target-ppc/kvm.c
index f8ea783a6d..72762991dc 100644
--- a/target-ppc/kvm.c
+++ b/target-ppc/kvm.c
@@ -2192,6 +2192,7 @@ static void kvmppc_host_cpu_initfn(Object *obj)
static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
{
+ DeviceClass *dc = DEVICE_CLASS(oc);
PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc);
uint32_t vmx = kvmppc_get_vmx();
uint32_t dfp = kvmppc_get_dfp();
@@ -2218,6 +2219,9 @@ static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data)
if (icache_size != -1) {
pcc->l1_icache_size = icache_size;
}
+
+ /* Reason: kvmppc_host_cpu_initfn() dies when !kvm_enabled() */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
bool kvmppc_has_cap_epr(void)
diff --git a/target-s390x/cpu.c b/target-s390x/cpu.c
index c3e21b445c..ccfaa8a919 100644
--- a/target-s390x/cpu.c
+++ b/target-s390x/cpu.c
@@ -353,6 +353,13 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data)
#endif
cc->gdb_num_core_regs = S390_NUM_CORE_REGS;
cc->gdb_core_xml_file = "s390x-core64.xml";
+
+ /*
+ * Reason: s390_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo s390_cpu_type_info = {
diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c
index 5c65ab4df5..64e4467c04 100644
--- a/target-sh4/cpu.c
+++ b/target-sh4/cpu.c
@@ -290,6 +290,13 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data)
#endif
dc->vmsd = &vmstate_sh_cpu;
cc->gdb_num_core_regs = 59;
+
+ /*
+ * Reason: superh_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo superh_cpu_type_info = {
diff --git a/target-sparc/cpu.c b/target-sparc/cpu.c
index 9528e3afbb..82bb72ab79 100644
--- a/target-sparc/cpu.c
+++ b/target-sparc/cpu.c
@@ -854,6 +854,13 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data)
#else
cc->gdb_num_core_regs = 72;
#endif
+
+ /*
+ * Reason: sparc_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo sparc_cpu_type_info = {
diff --git a/target-tilegx/cpu.c b/target-tilegx/cpu.c
index 3c5481d443..c24970436d 100644
--- a/target-tilegx/cpu.c
+++ b/target-tilegx/cpu.c
@@ -159,6 +159,13 @@ static void tilegx_cpu_class_init(ObjectClass *oc, void *data)
cc->set_pc = tilegx_cpu_set_pc;
cc->handle_mmu_fault = tilegx_cpu_handle_mmu_fault;
cc->gdb_num_core_regs = 0;
+
+ /*
+ * Reason: tilegx_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo tilegx_cpu_type_info = {
diff --git a/target-tricore/cpu.c b/target-tricore/cpu.c
index 2029ef651a..ed8b030ef5 100644
--- a/target-tricore/cpu.c
+++ b/target-tricore/cpu.c
@@ -170,6 +170,12 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data)
cc->set_pc = tricore_cpu_set_pc;
cc->synchronize_from_tb = tricore_cpu_synchronize_from_tb;
+ /*
+ * Reason: tricore_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void cpu_register(const TriCoreCPUInfo *info)
diff --git a/target-unicore32/cpu.c b/target-unicore32/cpu.c
index fc451a1a35..e5252ebaf8 100644
--- a/target-unicore32/cpu.c
+++ b/target-unicore32/cpu.c
@@ -155,6 +155,13 @@ static void uc32_cpu_class_init(ObjectClass *oc, void *data)
cc->get_phys_page_debug = uc32_cpu_get_phys_page_debug;
#endif
dc->vmsd = &vmstate_uc32_cpu;
+
+ /*
+ * Reason: uc32_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static void uc32_register_cpu_type(const UniCore32CPUInfo *info)
diff --git a/target-xtensa/cpu.c b/target-xtensa/cpu.c
index da8129db50..4e49bee9b5 100644
--- a/target-xtensa/cpu.c
+++ b/target-xtensa/cpu.c
@@ -155,6 +155,13 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data)
#endif
cc->debug_excp_handler = xtensa_breakpoint_handler;
dc->vmsd = &vmstate_xtensa_cpu;
+
+ /*
+ * Reason: xtensa_cpu_initfn() calls cpu_exec_init(), which saves
+ * the object in cpus -> dangling pointer after final
+ * object_unref().
+ */
+ dc->cannot_destroy_with_object_finalize_yet = true;
}
static const TypeInfo xtensa_cpu_type_info = {
diff --git a/tests/.gitignore b/tests/.gitignore
index a607bddce4..65496aa5a6 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -49,5 +49,6 @@ test-vmstate
test-write-threshold
test-x86-cpuid
test-xbzrle
+test-netfilter
*-test
qapi-schema/*.test.*
diff --git a/tests/Makefile b/tests/Makefile
index e6474ba31b..dbd32a670a 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -86,6 +86,9 @@ check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
# All QTests for now are POSIX-only, but the dependencies are
# really in libqtest, not in the testcases themselves.
+check-qtest-generic-y = tests/device-introspect-test$(EXESUF)
+gcov-files-generic-y = qdev-monitor.c qmp.c
+
gcov-files-ipack-y += hw/ipack/ipack.c
check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF)
gcov-files-ipack-y += hw/char/ipoctal232.c
@@ -191,6 +194,7 @@ gcov-files-i386-y += hw/pci-host/q35.c
ifeq ($(CONFIG_VHOST_NET),y)
check-qtest-i386-$(CONFIG_LINUX) += tests/vhost-user-test$(EXESUF)
endif
+check-qtest-i386-y += tests/test-netfilter$(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))
@@ -218,10 +222,7 @@ gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c
check-qtest-microblazeel-y = $(check-qtest-microblaze-y)
check-qtest-xtensaeb-y = $(check-qtest-xtensa-y)
-# qom-test works for all sysemu architectures:
-$(foreach target,$(SYSEMU_TARGET_LIST), \
- $(if $(findstring tests/qom-test$(EXESUF), $(check-qtest-$(target)-y)),, \
- $(eval check-qtest-$(target)-y += tests/qom-test$(EXESUF))))
+check-qtest-generic-y += tests/qom-test$(EXESUF)
check-qapi-schema-y := $(addprefix tests/qapi-schema/, \
comments.json empty.json enum-empty.json enum-missing-data.json \
@@ -384,6 +385,7 @@ libqos-imx-obj-y = $(libqos-obj-y) tests/libqos/i2c-imx.o
libqos-usb-obj-y = $(libqos-pc-obj-y) tests/libqos/usb.o
libqos-virtio-obj-y = $(libqos-pc-obj-y) tests/libqos/virtio.o tests/libqos/virtio-pci.o tests/libqos/virtio-mmio.o tests/libqos/malloc-generic.o
+tests/device-introspect-test$(EXESUF): tests/device-introspect-test.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
tests/endianness-test$(EXESUF): tests/endianness-test.o
@@ -437,6 +439,7 @@ tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-char.o qemu-timer.o
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o $(test-util-obj-y)
tests/test-write-threshold$(EXESUF): tests/test-write-threshold.o $(test-block-obj-y)
+tests/test-netfilter$(EXESUF): tests/test-netfilter.o $(qtest-obj-y)
ifeq ($(CONFIG_POSIX),y)
LIBS += -lutil
@@ -448,8 +451,11 @@ CFLAGS += $(TEST_CFLAGS)
TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
ifeq ($(CONFIG_POSIX),y)
-QTEST_TARGETS=$(foreach TARGET,$(TARGETS), $(if $(check-qtest-$(TARGET)-y), $(TARGET),))
+QTEST_TARGETS = $(TARGETS)
check-qtest-y=$(foreach TARGET,$(TARGETS), $(check-qtest-$(TARGET)-y))
+check-qtest-y += $(check-qtest-generic-y)
+else
+QTEST_TARGETS =
endif
qtest-obj-y = tests/libqtest.o $(test-util-obj-y)
@@ -487,8 +493,8 @@ $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: $(check-qtest-y)
$(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \
QTEST_QEMU_IMG=qemu-img$(EXESUF) \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
- gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y),"GTESTER $@")
- $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y); do \
+ gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER $@")
+ $(if $(CONFIG_GCOV),@for f in $(gcov-files-$*-y) $(gcov-files-generic-y); do \
echo Gcov report for $$f:;\
$(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
done,)
@@ -499,7 +505,7 @@ $(patsubst %, check-%, $(check-unit-y)): check-%: %
$(call quiet-command, \
MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$((RANDOM % 255 + 1))} \
gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER $*")
- $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y); do \
+ $(if $(CONFIG_GCOV),@for f in $(gcov-files-$(subst tests/,,$*)-y) $(gcov-files-generic-y); do \
echo Gcov report for $$f:;\
$(GCOV) $(GCOV_OPTIONS) $$f -o `dirname $$f`; \
done,)
diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c
new file mode 100644
index 0000000000..11d5fea3e2
--- /dev/null
+++ b/tests/device-introspect-test.c
@@ -0,0 +1,124 @@
+/*
+ * Device introspection test cases
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ *
+ * Authors:
+ * Markus Armbruster <armbru@redhat.com>,
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+/*
+ * Covers QMP device-list-properties and HMP device_add help. We
+ * currently don't check that their output makes sense, only that QEMU
+ * survives. Useful since we've had an astounding number of crash
+ * bugs around here.
+ */
+
+#include <glib.h>
+#include <stdarg.h>
+#include "qemu-common.h"
+#include "qapi/qmp/qstring.h"
+#include "libqtest.h"
+
+const char common_args[] = "-nodefaults -machine none";
+
+static QList *device_type_list(bool abstract)
+{
+ QDict *resp;
+ QList *ret;
+
+ resp = qmp("{'execute': 'qom-list-types',"
+ " 'arguments': {'implements': 'device', 'abstract': %i}}",
+ abstract);
+ g_assert(qdict_haskey(resp, "return"));
+ ret = qdict_get_qlist(resp, "return");
+ QINCREF(ret);
+ QDECREF(resp);
+ return ret;
+}
+
+static void test_one_device(const char *type)
+{
+ QDict *resp;
+ char *help, *qom_tree;
+
+ resp = qmp("{'execute': 'device-list-properties',"
+ " 'arguments': {'typename': %s}}",
+ type);
+ QDECREF(resp);
+
+ help = hmp("device_add \"%s,help\"", type);
+ g_free(help);
+
+ /*
+ * Some devices leave dangling pointers in QOM behind.
+ * "info qom-tree" has a good chance at crashing then
+ */
+ qom_tree = hmp("info qom-tree");
+ g_free(qom_tree);
+}
+
+static void test_device_intro_list(void)
+{
+ QList *types;
+ char *help;
+
+ qtest_start(common_args);
+
+ types = device_type_list(true);
+ QDECREF(types);
+
+ help = hmp("device_add help");
+ g_free(help);
+
+ qtest_end();
+}
+
+static void test_device_intro_none(void)
+{
+ qtest_start(common_args);
+ test_one_device("nonexistent");
+ qtest_end();
+}
+
+static void test_device_intro_abstract(void)
+{
+ qtest_start(common_args);
+ test_one_device("device");
+ qtest_end();
+}
+
+static void test_device_intro_concrete(void)
+{
+ QList *types;
+ QListEntry *entry;
+ const char *type;
+
+ qtest_start(common_args);
+ types = device_type_list(false);
+
+ QLIST_FOREACH_ENTRY(types, entry) {
+ type = qdict_get_try_str(qobject_to_qdict(qlist_entry_obj(entry)),
+ "name");
+ g_assert(type);
+ test_one_device(type);
+ }
+
+ QDECREF(types);
+ qtest_end();
+}
+
+int main(int argc, char **argv)
+{
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_add_func("device/introspect/list", test_device_intro_list);
+ qtest_add_func("device/introspect/none", test_device_intro_none);
+ qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
+ qtest_add_func("device/introspect/concrete", test_device_intro_concrete);
+
+ return g_test_run();
+}
diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c
index 8951f6f610..33909469f1 100644
--- a/tests/drive_del-test.c
+++ b/tests/drive_del-test.c
@@ -16,28 +16,18 @@
static void drive_add(void)
{
- QDict *response;
+ char *resp = hmp("drive_add 0 if=none,id=drive0");
- response = qmp("{'execute': 'human-monitor-command',"
- " 'arguments': {"
- " 'command-line': 'drive_add 0 if=none,id=drive0'"
- "}}");
- g_assert(response);
- g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "OK\r\n");
- QDECREF(response);
+ g_assert_cmpstr(resp, ==, "OK\r\n");
+ g_free(resp);
}
static void drive_del(void)
{
- QDict *response;
+ char *resp = hmp("drive_del drive0");
- response = qmp("{'execute': 'human-monitor-command',"
- " 'arguments': {"
- " 'command-line': 'drive_del drive0'"
- "}}");
- g_assert(response);
- g_assert_cmpstr(qdict_get_try_str(response, "return"), ==, "");
- QDECREF(response);
+ g_assert_cmpstr(resp, ==, "");
+ g_free(resp);
}
static void device_del(void)
diff --git a/tests/ide-test.c b/tests/ide-test.c
index b6e9e1a232..d1014bbc46 100644
--- a/tests/ide-test.c
+++ b/tests/ide-test.c
@@ -510,9 +510,7 @@ static void test_flush(void)
tmp_path);
/* Delay the completion of the flush request until we explicitly do it */
- qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
- " 'command-line':"
- " 'qemu-io ide0-hd0 \"break flush_to_os A\"'} }");
+ g_free(hmp("qemu-io ide0-hd0 \"break flush_to_os A\""));
/* FLUSH CACHE command on device 0*/
outb(IDE_BASE + reg_device, 0);
@@ -524,9 +522,7 @@ static void test_flush(void)
assert_bit_clear(data, DF | ERR | DRQ);
/* Complete the command */
- qmp_discard_response("{'execute':'human-monitor-command', 'arguments': {"
- " 'command-line':"
- " 'qemu-io ide0-hd0 \"resume A\"'} }");
+ g_free(hmp("qemu-io ide0-hd0 \"resume A\""));
/* Check registers */
data = inb(IDE_BASE + reg_device);
diff --git a/tests/libqtest.c b/tests/libqtest.c
index e5188e0327..2a396ba08d 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -46,7 +46,6 @@ struct QTestState
bool irq_level[MAX_IRQ];
GString *rx;
pid_t qemu_pid; /* our child QEMU process */
- struct sigaction sigact_old; /* restored on exit */
};
static GList *qtest_instances;
@@ -484,6 +483,33 @@ void qtest_qmp_eventwait(QTestState *s, const char *event)
}
}
+char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap)
+{
+ char *cmd;
+ QDict *resp;
+ char *ret;
+
+ cmd = g_strdup_vprintf(fmt, ap);
+ resp = qtest_qmp(s, "{'execute': 'human-monitor-command',"
+ " 'arguments': {'command-line': %s}}",
+ cmd);
+ ret = g_strdup(qdict_get_try_str(resp, "return"));
+ g_assert(ret);
+ QDECREF(resp);
+ g_free(cmd);
+ return ret;
+}
+
+char *qtest_hmp(QTestState *s, const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = qtest_hmpv(s, fmt, ap);
+ va_end(ap);
+ return ret;
+}
const char *qtest_get_arch(void)
{
@@ -775,6 +801,16 @@ void qmp_discard_response(const char *fmt, ...)
qtest_qmpv_discard_response(global_qtest, fmt, ap);
va_end(ap);
}
+char *hmp(const char *fmt, ...)
+{
+ va_list ap;
+ char *ret;
+
+ va_start(ap, fmt);
+ ret = qtest_hmpv(global_qtest, fmt, ap);
+ va_end(ap);
+ return ret;
+}
bool qtest_big_endian(void)
{
diff --git a/tests/libqtest.h b/tests/libqtest.h
index ec42031523..55bccbf0e6 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -120,6 +120,29 @@ QDict *qtest_qmp_receive(QTestState *s);
void qtest_qmp_eventwait(QTestState *s, const char *event);
/**
+ * qtest_hmpv:
+ * @s: #QTestState instance to operate on.
+ * @fmt...: HMP command to send to QEMU
+ *
+ * Send HMP command to QEMU via QMP's human-monitor-command.
+ *
+ * Returns: the command's output. The caller should g_free() it.
+ */
+char *qtest_hmp(QTestState *s, const char *fmt, ...);
+
+/**
+ * qtest_hmpv:
+ * @s: #QTestState instance to operate on.
+ * @fmt: HMP command to send to QEMU
+ * @ap: HMP command arguments
+ *
+ * Send HMP command to QEMU via QMP's human-monitor-command.
+ *
+ * Returns: the command's output. The caller should g_free() it.
+ */
+char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap);
+
+/**
* qtest_get_irq:
* @s: #QTestState instance to operate on.
* @num: Interrupt to observe.
@@ -499,6 +522,16 @@ static inline void qmp_eventwait(const char *event)
}
/**
+ * hmp:
+ * @fmt...: HMP command to send to QEMU
+ *
+ * Send HMP command to QEMU via QMP's human-monitor-command.
+ *
+ * Returns: the command's output. The caller should g_free() it.
+ */
+char *hmp(const char *fmt, ...);
+
+/**
* get_irq:
* @num: Interrupt to observe.
*
diff --git a/tests/test-netfilter.c b/tests/test-netfilter.c
new file mode 100644
index 0000000000..303deb7e09
--- /dev/null
+++ b/tests/test-netfilter.c
@@ -0,0 +1,200 @@
+/*
+ * QTest testcase for netfilter
+ *
+ * Copyright (c) 2015 FUJITSU LIMITED
+ * Author: Yang Hongyang <yanghy@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 <glib.h>
+#include "libqtest.h"
+
+/* add a netfilter to a netdev and then remove it */
+static void add_one_netfilter(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'object-add',"
+ " 'arguments': {"
+ " 'qom-type': 'filter-buffer',"
+ " 'id': 'qtest-f0',"
+ " 'props': {"
+ " 'netdev': 'qtest-bn0',"
+ " 'queue': 'rx',"
+ " 'interval': 1000"
+ "}}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'object-del',"
+ " 'arguments': {"
+ " 'id': 'qtest-f0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+}
+
+/* add a netfilter to a netdev and then remove the netdev */
+static void remove_netdev_with_one_netfilter(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'object-add',"
+ " 'arguments': {"
+ " 'qom-type': 'filter-buffer',"
+ " 'id': 'qtest-f0',"
+ " 'props': {"
+ " 'netdev': 'qtest-bn0',"
+ " 'queue': 'rx',"
+ " 'interval': 1000"
+ "}}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'netdev_del',"
+ " 'arguments': {"
+ " 'id': 'qtest-bn0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ /* add back the netdev */
+ response = qmp("{'execute': 'netdev_add',"
+ " 'arguments': {"
+ " 'type': 'user',"
+ " 'id': 'qtest-bn0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+}
+
+/* add multi(2) netfilters to a netdev and then remove them */
+static void add_multi_netfilter(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'object-add',"
+ " 'arguments': {"
+ " 'qom-type': 'filter-buffer',"
+ " 'id': 'qtest-f0',"
+ " 'props': {"
+ " 'netdev': 'qtest-bn0',"
+ " 'queue': 'rx',"
+ " 'interval': 1000"
+ "}}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'object-add',"
+ " 'arguments': {"
+ " 'qom-type': 'filter-buffer',"
+ " 'id': 'qtest-f1',"
+ " 'props': {"
+ " 'netdev': 'qtest-bn0',"
+ " 'queue': 'rx',"
+ " 'interval': 1000"
+ "}}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'object-del',"
+ " 'arguments': {"
+ " 'id': 'qtest-f0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'object-del',"
+ " 'arguments': {"
+ " 'id': 'qtest-f1'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+}
+
+/* add multi(2) netfilters to a netdev and then remove the netdev */
+static void remove_netdev_with_multi_netfilter(void)
+{
+ QDict *response;
+
+ response = qmp("{'execute': 'object-add',"
+ " 'arguments': {"
+ " 'qom-type': 'filter-buffer',"
+ " 'id': 'qtest-f0',"
+ " 'props': {"
+ " 'netdev': 'qtest-bn0',"
+ " 'queue': 'rx',"
+ " 'interval': 1000"
+ "}}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'object-add',"
+ " 'arguments': {"
+ " 'qom-type': 'filter-buffer',"
+ " 'id': 'qtest-f1',"
+ " 'props': {"
+ " 'netdev': 'qtest-bn0',"
+ " 'queue': 'rx',"
+ " 'interval': 1000"
+ "}}}");
+
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ response = qmp("{'execute': 'netdev_del',"
+ " 'arguments': {"
+ " 'id': 'qtest-bn0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+
+ /* add back the netdev */
+ response = qmp("{'execute': 'netdev_add',"
+ " 'arguments': {"
+ " 'type': 'user',"
+ " 'id': 'qtest-bn0'"
+ "}}");
+ g_assert(response);
+ g_assert(!qdict_haskey(response, "error"));
+ QDECREF(response);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/netfilter/addremove_one", add_one_netfilter);
+ qtest_add_func("/netfilter/remove_netdev_one",
+ remove_netdev_with_one_netfilter);
+ qtest_add_func("/netfilter/addremove_multi", add_multi_netfilter);
+ qtest_add_func("/netfilter/remove_netdev_multi",
+ remove_netdev_with_multi_netfilter);
+
+ qtest_start("-netdev user,id=qtest-bn0 -device e1000,netdev=qtest-bn0");
+ ret = g_test_run();
+
+ qtest_end();
+
+ return ret;
+}
diff --git a/trace-events b/trace-events
index b813ae4c77..a0ddc6b14d 100644
--- a/trace-events
+++ b/trace-events
@@ -1178,6 +1178,7 @@ vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x"
vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp"
# hw/display/virtio-gpu.c
+virtio_gpu_features(bool virgl) "virgl %d"
virtio_gpu_cmd_get_display_info(void) ""
virtio_gpu_cmd_get_caps(void) ""
virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d"
@@ -1187,7 +1188,15 @@ virtio_gpu_cmd_res_unref(uint32_t res) "res 0x%x"
virtio_gpu_cmd_res_back_attach(uint32_t res) "res 0x%x"
virtio_gpu_cmd_res_back_detach(uint32_t res) "res 0x%x"
virtio_gpu_cmd_res_xfer_toh_2d(uint32_t res) "res 0x%x"
+virtio_gpu_cmd_res_xfer_toh_3d(uint32_t res) "res 0x%x"
+virtio_gpu_cmd_res_xfer_fromh_3d(uint32_t res) "res 0x%x"
virtio_gpu_cmd_res_flush(uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "res 0x%x, w %d, h %d, x %d, y %d"
+virtio_gpu_cmd_ctx_create(uint32_t ctx, const char *name) "ctx 0x%x, name %s"
+virtio_gpu_cmd_ctx_destroy(uint32_t ctx) "ctx 0x%x"
+virtio_gpu_cmd_ctx_res_attach(uint32_t ctx, uint32_t res) "ctx 0x%x, res 0x%x"
+virtio_gpu_cmd_ctx_res_detach(uint32_t ctx, uint32_t res) "ctx 0x%x, res 0x%x"
+virtio_gpu_cmd_ctx_submit(uint32_t ctx, uint32_t size) "ctx 0x%x, size %d"
+virtio_gpu_update_cursor(uint32_t scanout, uint32_t x, uint32_t y, const char *type, uint32_t res) "scanout %d, x %d, y %d, %s, res 0x%x"
virtio_gpu_fence_ctrl(uint64_t fence, uint32_t type) "fence 0x%" PRIx64 ", type 0x%x"
virtio_gpu_fence_resp(uint64_t fence) "fence 0x%" PRIx64
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 0034fbb49f..728393c5ea 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -31,11 +31,17 @@ ifeq ($(CONFIG_OPENGL),y)
common-obj-y += shader.o
common-obj-y += console-gl.o
common-obj-y += egl-helpers.o
+common-obj-y += egl-context.o
+ifeq ($(CONFIG_GTK_GL),y)
+common-obj-$(CONFIG_GTK) += gtk-gl-area.o
+else
common-obj-$(CONFIG_GTK) += gtk-egl.o
endif
+endif
gtk.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS)
gtk-egl.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS)
+gtk-gl-area.o-cflags := $(GTK_CFLAGS) $(VTE_CFLAGS) $(OPENGL_CFLAGS)
shader.o-cflags += $(OPENGL_CFLAGS)
console-gl.o-cflags += $(OPENGL_CFLAGS)
egl-helpers.o-cflags += $(OPENGL_CFLAGS)
diff --git a/ui/console-gl.c b/ui/console-gl.c
index cb45cf8a29..baf397b300 100644
--- a/ui/console-gl.c
+++ b/ui/console-gl.c
@@ -33,6 +33,7 @@
struct ConsoleGLState {
GLint texture_blit_prog;
+ GLint texture_blit_vao;
};
/* ---------------------------------------------------------------------- */
@@ -47,6 +48,9 @@ ConsoleGLState *console_gl_init_context(void)
exit(1);
}
+ gls->texture_blit_vao =
+ qemu_gl_init_texture_blit(gls->texture_blit_prog);
+
return gls;
}
@@ -131,7 +135,8 @@ void surface_gl_render_texture(ConsoleGLState *gls,
glClearColor(0.1f, 0.1f, 0.1f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
- qemu_gl_run_texture_blit(gls->texture_blit_prog);
+ qemu_gl_run_texture_blit(gls->texture_blit_prog,
+ gls->texture_blit_vao);
}
void surface_gl_destroy_texture(ConsoleGLState *gls,
diff --git a/ui/console.c b/ui/console.c
index 75fc492f73..31f0d35987 100644
--- a/ui/console.c
+++ b/ui/console.c
@@ -121,6 +121,7 @@ struct QemuConsole {
DisplayState *ds;
DisplaySurface *surface;
int dcls;
+ DisplayChangeListener *gl;
/* Graphic console state. */
Object *device;
@@ -1332,6 +1333,11 @@ void qemu_free_displaysurface(DisplaySurface *surface)
g_free(surface);
}
+bool console_has_gl(QemuConsole *con)
+{
+ return con->gl != NULL;
+}
+
void register_displaychangelistener(DisplayChangeListener *dcl)
{
static const char nodev[] =
@@ -1339,6 +1345,17 @@ void register_displaychangelistener(DisplayChangeListener *dcl)
static DisplaySurface *dummy;
QemuConsole *con;
+ if (dcl->ops->dpy_gl_ctx_create) {
+ /* display has opengl support */
+ assert(dcl->con);
+ if (dcl->con->gl) {
+ fprintf(stderr, "can't register two opengl displays (%s, %s)\n",
+ dcl->ops->dpy_name, dcl->con->gl->ops->dpy_name);
+ exit(1);
+ }
+ dcl->con->gl = dcl;
+ }
+
trace_displaychangelistener_register(dcl, dcl->ops->dpy_name);
dcl->ds = get_alloc_displaystate();
QLIST_INSERT_HEAD(&dcl->ds->listeners, dcl, next);
@@ -1417,9 +1434,13 @@ void dpy_gfx_update(QemuConsole *con, int x, int y, int w, int h)
{
DisplayState *s = con->ds;
DisplayChangeListener *dcl;
- int width = surface_width(con->surface);
- int height = surface_height(con->surface);
+ int width = w;
+ int height = h;
+ if (con->surface) {
+ width = surface_width(con->surface);
+ height = surface_height(con->surface);
+ }
x = MAX(x, 0);
y = MAX(y, 0);
x = MIN(x, width);
@@ -1619,6 +1640,48 @@ bool dpy_cursor_define_supported(QemuConsole *con)
return false;
}
+QEMUGLContext dpy_gl_ctx_create(QemuConsole *con,
+ struct QEMUGLParams *qparams)
+{
+ assert(con->gl);
+ return con->gl->ops->dpy_gl_ctx_create(con->gl, qparams);
+}
+
+void dpy_gl_ctx_destroy(QemuConsole *con, QEMUGLContext ctx)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_ctx_destroy(con->gl, ctx);
+}
+
+int dpy_gl_ctx_make_current(QemuConsole *con, QEMUGLContext ctx)
+{
+ assert(con->gl);
+ return con->gl->ops->dpy_gl_ctx_make_current(con->gl, ctx);
+}
+
+QEMUGLContext dpy_gl_ctx_get_current(QemuConsole *con)
+{
+ assert(con->gl);
+ return con->gl->ops->dpy_gl_ctx_get_current(con->gl);
+}
+
+void dpy_gl_scanout(QemuConsole *con,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y, uint32_t width, uint32_t height)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_scanout(con->gl, backing_id,
+ backing_y_0_top,
+ x, y, width, height);
+}
+
+void dpy_gl_update(QemuConsole *con,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ assert(con->gl);
+ con->gl->ops->dpy_gl_update(con->gl, x, y, w, h);
+}
+
/***********************************************************/
/* register display */
diff --git a/ui/egl-context.c b/ui/egl-context.c
new file mode 100644
index 0000000000..40102e392e
--- /dev/null
+++ b/ui/egl-context.c
@@ -0,0 +1,34 @@
+#include "qemu-common.h"
+#include "ui/egl-context.h"
+
+QEMUGLContext qemu_egl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ EGLContext ctx;
+ EGLint ctx_att[] = {
+ EGL_CONTEXT_CLIENT_VERSION, params->major_ver,
+ EGL_CONTEXT_MINOR_VERSION_KHR, params->minor_ver,
+ EGL_NONE
+ };
+
+ ctx = eglCreateContext(qemu_egl_display, qemu_egl_config,
+ eglGetCurrentContext(), ctx_att);
+ return ctx;
+}
+
+void qemu_egl_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+{
+ eglDestroyContext(qemu_egl_display, ctx);
+}
+
+int qemu_egl_make_context_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ return eglMakeCurrent(qemu_egl_display,
+ EGL_NO_SURFACE, EGL_NO_SURFACE, ctx);
+}
+
+QEMUGLContext qemu_egl_get_current_context(DisplayChangeListener *dcl)
+{
+ return eglGetCurrentContext();
+}
diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c
index 15b41f2bab..500c42c9fa 100644
--- a/ui/gtk-egl.c
+++ b/ui/gtk-egl.c
@@ -21,6 +21,29 @@
#include "sysemu/sysemu.h"
+static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout)
+{
+ if (vc->gfx.scanout_mode == scanout) {
+ return;
+ }
+
+ vc->gfx.scanout_mode = scanout;
+ if (!vc->gfx.scanout_mode) {
+ if (vc->gfx.fbo_id) {
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, 0, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+ glDeleteFramebuffers(1, &vc->gfx.fbo_id);
+ vc->gfx.fbo_id = 0;
+ }
+ if (vc->gfx.surface) {
+ surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+ surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+ }
+}
+
/** DisplayState Callbacks (opengl version) **/
void gd_egl_init(VirtualConsole *vc)
@@ -50,19 +73,26 @@ void gd_egl_draw(VirtualConsole *vc)
GdkWindow *window;
int ww, wh;
- if (!vc->gfx.gls || !vc->gfx.ds) {
+ if (!vc->gfx.gls) {
return;
}
- eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
- vc->gfx.esurface, vc->gfx.ectx);
+ if (vc->gfx.scanout_mode) {
+ gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h);
+ } else {
+ if (!vc->gfx.ds) {
+ return;
+ }
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
- window = gtk_widget_get_window(vc->gfx.drawing_area);
- gdk_drawable_get_size(window, &ww, &wh);
- surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
- surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
+ window = gtk_widget_get_window(vc->gfx.drawing_area);
+ gdk_drawable_get_size(window, &ww, &wh);
+ surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
+ surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
- eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
+ eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
+ }
}
void gd_egl_update(DisplayChangeListener *dcl,
@@ -99,6 +129,7 @@ void gd_egl_refresh(DisplayChangeListener *dcl)
if (vc->gfx.glupdates) {
vc->gfx.glupdates = 0;
+ gtk_egl_set_scanout_mode(vc, false);
gd_egl_draw(vc);
}
}
@@ -128,6 +159,81 @@ void gd_egl_switch(DisplayChangeListener *dcl,
}
}
+QEMUGLContext gd_egl_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+ return qemu_egl_create_context(dcl, params);
+}
+
+void gd_egl_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ vc->gfx.x = x;
+ vc->gfx.y = y;
+ vc->gfx.w = w;
+ vc->gfx.h = h;
+ vc->gfx.tex_id = backing_id;
+ vc->gfx.y0_top = backing_y_0_top;
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+
+ if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
+ gtk_egl_set_scanout_mode(vc, false);
+ return;
+ }
+
+ gtk_egl_set_scanout_mode(vc, true);
+ if (!vc->gfx.fbo_id) {
+ glGenFramebuffers(1, &vc->gfx.fbo_id);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, vc->gfx.tex_id, 0);
+}
+
+void gd_egl_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ GdkWindow *window;
+ int ww, wh, y1, y2;
+
+ if (!vc->gfx.scanout_mode) {
+ return;
+ }
+ if (!vc->gfx.fbo_id) {
+ return;
+ }
+
+ eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, vc->gfx.ectx);
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+ window = gtk_widget_get_window(vc->gfx.drawing_area);
+ gdk_drawable_get_size(window, &ww, &wh);
+ glViewport(0, 0, ww, wh);
+ y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
+ y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
+ glBlitFramebuffer(0, y1, vc->gfx.w, y2,
+ 0, 0, ww, wh,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
+
+ eglSwapBuffers(qemu_egl_display, vc->gfx.esurface);
+}
+
void gtk_egl_init(void)
{
GdkDisplay *gdk_display = gdk_display_get_default();
@@ -139,3 +245,12 @@ void gtk_egl_init(void)
display_opengl = 1;
}
+
+int gd_egl_make_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ return eglMakeCurrent(qemu_egl_display, vc->gfx.esurface,
+ vc->gfx.esurface, ctx);
+}
diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c
new file mode 100644
index 0000000000..dec3edb296
--- /dev/null
+++ b/ui/gtk-gl-area.c
@@ -0,0 +1,223 @@
+/*
+ * GTK UI -- glarea opengl code.
+ *
+ * Requires 3.16+ (GtkGLArea widget).
+ *
+ * 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-common.h"
+
+#include "trace.h"
+
+#include "ui/console.h"
+#include "ui/gtk.h"
+#include "ui/egl-helpers.h"
+
+#include "sysemu/sysemu.h"
+
+static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout)
+{
+ if (vc->gfx.scanout_mode == scanout) {
+ return;
+ }
+
+ vc->gfx.scanout_mode = scanout;
+ if (!vc->gfx.scanout_mode) {
+ if (vc->gfx.fbo_id) {
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
+ GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, 0, 0);
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0);
+ glDeleteFramebuffers(1, &vc->gfx.fbo_id);
+ vc->gfx.fbo_id = 0;
+ }
+ if (vc->gfx.surface) {
+ surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+ surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+ }
+}
+
+/** DisplayState Callbacks (opengl version) **/
+
+void gd_gl_area_draw(VirtualConsole *vc)
+{
+ int ww, wh, y1, y2;
+
+ if (!vc->gfx.gls) {
+ return;
+ }
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area);
+ wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area);
+
+ if (vc->gfx.scanout_mode) {
+ if (!vc->gfx.fbo_id) {
+ return;
+ }
+
+ glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.fbo_id);
+ /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */
+
+ glViewport(0, 0, ww, wh);
+ y1 = vc->gfx.y0_top ? 0 : vc->gfx.h;
+ y2 = vc->gfx.y0_top ? vc->gfx.h : 0;
+ glBlitFramebuffer(0, y1, vc->gfx.w, y2,
+ 0, 0, ww, wh,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ } else {
+ if (!vc->gfx.ds) {
+ return;
+ }
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+
+ surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh);
+ surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+}
+
+void gd_gl_area_update(DisplayChangeListener *dcl,
+ int x, int y, int w, int h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ if (!vc->gfx.gls || !vc->gfx.ds) {
+ return;
+ }
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h);
+ vc->gfx.glupdates++;
+}
+
+void gd_gl_area_refresh(DisplayChangeListener *dcl)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ if (!vc->gfx.gls) {
+ if (!gtk_widget_get_realized(vc->gfx.drawing_area)) {
+ return;
+ }
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ vc->gfx.gls = console_gl_init_context();
+ if (vc->gfx.ds) {
+ surface_gl_create_texture(vc->gfx.gls, vc->gfx.ds);
+ }
+ }
+
+ graphic_hw_update(dcl->con);
+
+ if (vc->gfx.glupdates) {
+ vc->gfx.glupdates = 0;
+ gtk_gl_area_set_scanout_mode(vc, false);
+ gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
+ }
+}
+
+void gd_gl_area_switch(DisplayChangeListener *dcl,
+ DisplaySurface *surface)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ bool resized = true;
+
+ trace_gd_switch(vc->label, surface_width(surface), surface_height(surface));
+
+ if (vc->gfx.ds &&
+ surface_width(vc->gfx.ds) == surface_width(surface) &&
+ surface_height(vc->gfx.ds) == surface_height(surface)) {
+ resized = false;
+ }
+
+ if (vc->gfx.gls) {
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds);
+ surface_gl_create_texture(vc->gfx.gls, surface);
+ }
+ vc->gfx.ds = surface;
+
+ if (resized) {
+ gd_update_windowsize(vc);
+ }
+}
+
+QEMUGLContext gd_gl_area_create_context(DisplayChangeListener *dcl,
+ QEMUGLParams *params)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+ GdkWindow *window;
+ GdkGLContext *ctx;
+ GError *err = NULL;
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+ window = gtk_widget_get_window(vc->gfx.drawing_area);
+ ctx = gdk_window_create_gl_context(window, &err);
+ gdk_gl_context_set_required_version(ctx,
+ params->major_ver,
+ params->minor_ver);
+ gdk_gl_context_realize(ctx, &err);
+ return ctx;
+}
+
+void gd_gl_area_destroy_context(DisplayChangeListener *dcl, QEMUGLContext ctx)
+{
+ /* FIXME */
+}
+
+void gd_gl_area_scanout(DisplayChangeListener *dcl,
+ uint32_t backing_id, bool backing_y_0_top,
+ uint32_t x, uint32_t y,
+ uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ vc->gfx.x = x;
+ vc->gfx.y = y;
+ vc->gfx.w = w;
+ vc->gfx.h = h;
+ vc->gfx.tex_id = backing_id;
+ vc->gfx.y0_top = backing_y_0_top;
+
+ gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area));
+
+ if (vc->gfx.tex_id == 0 || vc->gfx.w == 0 || vc->gfx.h == 0) {
+ gtk_gl_area_set_scanout_mode(vc, false);
+ return;
+ }
+
+ gtk_gl_area_set_scanout_mode(vc, true);
+ if (!vc->gfx.fbo_id) {
+ glGenFramebuffers(1, &vc->gfx.fbo_id);
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER_EXT, vc->gfx.fbo_id);
+ glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, vc->gfx.tex_id, 0);
+}
+
+void gd_gl_area_scanout_flush(DisplayChangeListener *dcl,
+ uint32_t x, uint32_t y, uint32_t w, uint32_t h)
+{
+ VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl);
+
+ gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
+}
+
+void gtk_gl_area_init(void)
+{
+ display_opengl = 1;
+}
+
+QEMUGLContext gd_gl_area_get_current_context(DisplayChangeListener *dcl)
+{
+ return gdk_gl_context_get_current();
+}
+
+int gd_gl_area_make_current(DisplayChangeListener *dcl,
+ QEMUGLContext ctx)
+{
+ gdk_gl_context_make_current(ctx);
+ return 0;
+}
diff --git a/ui/gtk.c b/ui/gtk.c
index 3057cdc3f7..294783885f 100644
--- a/ui/gtk.c
+++ b/ui/gtk.c
@@ -367,6 +367,12 @@ static void gd_update_full_redraw(VirtualConsole *vc)
GtkWidget *area = vc->gfx.drawing_area;
int ww, wh;
gdk_drawable_get_size(gtk_widget_get_window(area), &ww, &wh);
+#if defined(CONFIG_GTK_GL)
+ if (vc->gfx.gls) {
+ gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area));
+ return;
+ }
+#endif
gtk_widget_queue_draw_area(area, 0, 0, ww, wh);
}
@@ -607,6 +613,27 @@ static const DisplayChangeListenerOps dcl_ops = {
/** DisplayState Callbacks (opengl version) **/
+#if defined(CONFIG_GTK_GL)
+
+static const DisplayChangeListenerOps dcl_gl_area_ops = {
+ .dpy_name = "gtk-egl",
+ .dpy_gfx_update = gd_gl_area_update,
+ .dpy_gfx_switch = gd_gl_area_switch,
+ .dpy_gfx_check_format = console_gl_check_format,
+ .dpy_refresh = gd_gl_area_refresh,
+ .dpy_mouse_set = gd_mouse_set,
+ .dpy_cursor_define = gd_cursor_define,
+
+ .dpy_gl_ctx_create = gd_gl_area_create_context,
+ .dpy_gl_ctx_destroy = gd_gl_area_destroy_context,
+ .dpy_gl_ctx_make_current = gd_gl_area_make_current,
+ .dpy_gl_ctx_get_current = gd_gl_area_get_current_context,
+ .dpy_gl_scanout = gd_gl_area_scanout,
+ .dpy_gl_update = gd_gl_area_scanout_flush,
+};
+
+#else
+
static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_name = "gtk-egl",
.dpy_gfx_update = gd_egl_update,
@@ -615,9 +642,17 @@ static const DisplayChangeListenerOps dcl_egl_ops = {
.dpy_refresh = gd_egl_refresh,
.dpy_mouse_set = gd_mouse_set,
.dpy_cursor_define = gd_cursor_define,
+
+ .dpy_gl_ctx_create = gd_egl_create_context,
+ .dpy_gl_ctx_destroy = qemu_egl_destroy_context,
+ .dpy_gl_ctx_make_current = gd_egl_make_current,
+ .dpy_gl_ctx_get_current = qemu_egl_get_current_context,
+ .dpy_gl_scanout = gd_egl_scanout,
+ .dpy_gl_update = gd_egl_scanout_flush,
};
-#endif
+#endif /* CONFIG_GTK_GL */
+#endif /* CONFIG_OPENGL */
/** QEMU Events **/
@@ -667,6 +702,39 @@ static gboolean gd_window_close(GtkWidget *widget, GdkEvent *event,
return TRUE;
}
+static void gd_set_ui_info(VirtualConsole *vc, gint width, gint height)
+{
+ QemuUIInfo info;
+
+ memset(&info, 0, sizeof(info));
+ info.width = width;
+ info.height = height;
+ dpy_set_ui_info(vc->gfx.dcl.con, &info);
+}
+
+#if defined(CONFIG_GTK_GL)
+
+static gboolean gd_render_event(GtkGLArea *area, GdkGLContext *context,
+ void *opaque)
+{
+ VirtualConsole *vc = opaque;
+
+ if (vc->gfx.gls) {
+ gd_gl_area_draw(vc);
+ }
+ return TRUE;
+}
+
+static void gd_resize_event(GtkGLArea *area,
+ gint width, gint height, gpointer *opaque)
+{
+ VirtualConsole *vc = (void *)opaque;
+
+ gd_set_ui_info(vc, width, height);
+}
+
+#endif
+
static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
{
VirtualConsole *vc = opaque;
@@ -677,8 +745,13 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque)
#if defined(CONFIG_OPENGL)
if (vc->gfx.gls) {
+#if defined(CONFIG_GTK_GL)
+ /* invoke render callback please */
+ return FALSE;
+#else
gd_egl_draw(vc);
return TRUE;
+#endif
}
#endif
@@ -1466,12 +1539,8 @@ static gboolean gd_configure(GtkWidget *widget,
GdkEventConfigure *cfg, gpointer opaque)
{
VirtualConsole *vc = opaque;
- QemuUIInfo info;
- memset(&info, 0, sizeof(info));
- info.width = cfg->width;
- info.height = cfg->height;
- dpy_set_ui_info(vc->gfx.dcl.con, &info);
+ gd_set_ui_info(vc, cfg->width, cfg->height);
return FALSE;
}
@@ -1628,6 +1697,15 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *vc)
#if GTK_CHECK_VERSION(3, 0, 0)
g_signal_connect(vc->gfx.drawing_area, "draw",
G_CALLBACK(gd_draw_event), vc);
+#if defined(CONFIG_GTK_GL)
+ if (display_opengl) {
+ /* wire up GtkGlArea events */
+ g_signal_connect(vc->gfx.drawing_area, "render",
+ G_CALLBACK(gd_render_event), vc);
+ g_signal_connect(vc->gfx.drawing_area, "resize",
+ G_CALLBACK(gd_resize_event), vc);
+ }
+#endif
#else
g_signal_connect(vc->gfx.drawing_area, "expose-event",
G_CALLBACK(gd_expose_event), vc);
@@ -1736,26 +1814,13 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
vc->gfx.scale_x = 1.0;
vc->gfx.scale_y = 1.0;
- vc->gfx.drawing_area = gtk_drawing_area_new();
- gtk_widget_add_events(vc->gfx.drawing_area,
- GDK_POINTER_MOTION_MASK |
- GDK_BUTTON_PRESS_MASK |
- GDK_BUTTON_RELEASE_MASK |
- GDK_BUTTON_MOTION_MASK |
- GDK_ENTER_NOTIFY_MASK |
- GDK_LEAVE_NOTIFY_MASK |
- GDK_SCROLL_MASK |
- GDK_KEY_PRESS_MASK);
- gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
-
- vc->type = GD_VC_GFX;
- vc->tab_item = vc->gfx.drawing_area;
- vc->focus = vc->gfx.drawing_area;
- gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
- vc->tab_item, gtk_label_new(vc->label));
-
#if defined(CONFIG_OPENGL)
if (display_opengl) {
+#if defined(CONFIG_GTK_GL)
+ vc->gfx.drawing_area = gtk_gl_area_new();
+ vc->gfx.dcl.ops = &dcl_gl_area_ops;
+#else
+ vc->gfx.drawing_area = gtk_drawing_area_new();
/*
* gtk_widget_set_double_buffered() was deprecated in 3.14.
* It is required for opengl rendering on X11 though. A
@@ -1771,12 +1836,32 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc,
#pragma GCC diagnostic pop
#endif
vc->gfx.dcl.ops = &dcl_egl_ops;
+#endif /* CONFIG_GTK_GL */
} else
#endif
{
+ vc->gfx.drawing_area = gtk_drawing_area_new();
vc->gfx.dcl.ops = &dcl_ops;
}
+
+ gtk_widget_add_events(vc->gfx.drawing_area,
+ GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_ENTER_NOTIFY_MASK |
+ GDK_LEAVE_NOTIFY_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK);
+ gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE);
+
+ vc->type = GD_VC_GFX;
+ vc->tab_item = vc->gfx.drawing_area;
+ vc->focus = vc->gfx.drawing_area;
+ gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook),
+ vc->tab_item, gtk_label_new(vc->label));
+
vc->gfx.dcl.con = con;
register_displaychangelistener(&vc->gfx.dcl);
@@ -2059,8 +2144,12 @@ void early_gtk_display_init(int opengl)
break;
case 1: /* on */
#if defined(CONFIG_OPENGL)
+#if defined(CONFIG_GTK_GL)
+ gtk_gl_area_init();
+#else
gtk_egl_init();
#endif
+#endif
break;
default:
g_assert_not_reached();
diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c
index d0b340f956..191ee3b52d 100644
--- a/ui/sdl2-2d.c
+++ b/ui/sdl2-2d.c
@@ -45,10 +45,23 @@ void sdl2_2d_update(DisplayChangeListener *dcl,
return;
}
+ /*
+ * SDL2 seems to do some double-buffering, and trying to only
+ * update the changed areas results in only one of the two buffers
+ * being updated. Which flickers alot. So lets not try to be
+ * clever do a full update every time ...
+ */
+#if 0
rect.x = x;
rect.y = y;
rect.w = w;
rect.h = h;
+#else
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = surface_width(surf);
+ rect.h = surface_height(surf);
+#endif
SDL_UpdateTexture(scon->texture, NULL, surface_data(surf),
surface_stride(surf));
diff --git a/ui/shader.c b/ui/shader.c
index 52a4632930..0588655cfe 100644
--- a/ui/shader.c
+++ b/ui/shader.c
@@ -29,21 +29,42 @@
/* ---------------------------------------------------------------------- */
-void qemu_gl_run_texture_blit(GLint texture_blit_prog)
+GLuint qemu_gl_init_texture_blit(GLint texture_blit_prog)
{
- GLfloat in_position[] = {
+ static const GLfloat in_position[] = {
-1, -1,
1, -1,
-1, 1,
1, 1,
};
GLint l_position;
+ GLuint vao, buffer;
+
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ /* this is the VBO that holds the vertex data */
+ glGenBuffers(1, &buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, buffer);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(in_position), in_position,
+ GL_STATIC_DRAW);
- glUseProgram(texture_blit_prog);
l_position = glGetAttribLocation(texture_blit_prog, "in_position");
- glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, in_position);
+ glVertexAttribPointer(l_position, 2, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(l_position);
- glDrawArrays(GL_TRIANGLE_STRIP, l_position, 4);
+
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+
+ return vao;
+}
+
+void qemu_gl_run_texture_blit(GLint texture_blit_prog,
+ GLint texture_blit_vao)
+{
+ glUseProgram(texture_blit_prog);
+ glBindVertexArray(texture_blit_vao);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
/* ---------------------------------------------------------------------- */
diff --git a/vl.c b/vl.c
index ea9e0e670c..7c806a2428 100644
--- a/vl.c
+++ b/vl.c
@@ -2747,13 +2747,18 @@ static bool object_create_initial(const char *type)
if (g_str_equal(type, "rng-egd")) {
return false;
}
+
+ if (g_str_equal(type, "filter-buffer")) {
+ return false;
+ }
+
return true;
}
/*
* The remainder of object creation happens after the
- * creation of chardev, fsdev and device data types.
+ * creation of chardev, fsdev, net clients and device data types.
*/
static bool object_create_delayed(const char *type)
{
@@ -4259,12 +4264,6 @@ int main(int argc, char **argv, char **envp)
exit(0);
}
- if (qemu_opts_foreach(qemu_find_opts("object"),
- object_create,
- object_create_delayed, NULL)) {
- exit(1);
- }
-
machine_opts = qemu_get_machine_opts();
if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine,
NULL)) {
@@ -4370,6 +4369,12 @@ int main(int argc, char **argv, char **envp)
exit(1);
}
+ if (qemu_opts_foreach(qemu_find_opts("object"),
+ object_create,
+ object_create_delayed, NULL)) {
+ exit(1);
+ }
+
#ifdef CONFIG_TPM
if (tpm_init() < 0) {
exit(1);