From 6838f0389088b07843a1899adc818ca99db8d855 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 22 Feb 2018 12:34:57 +0100 Subject: [PATCH] bump version to 2.11.1-1 Signed-off-by: Wolfgang Bumiller --- Makefile | 19 +- debian/changelog | 6 + ...arget-i386-disable-LINT0-after-reset.patch | 2 +- ...t-don-t-align-wait-time-with-slices.patch} | 6 +- ...io-serial-fix-segfault-on-disconnect.patch | 74 - ...ys-store-SCSIRequest-into-MegasasCmd.patch | 125 - ...k-len-against-dhcp-options-array-end.patch | 35 - ...-IDE-Do-not-flush-empty-CDROM-drives.patch | 44 - ...map-add-bitmap_copy_and_clear_atomic.patch | 51 - ...rt-getting-and-using-a-dirty-bitmap-.patch | 241 -- ...-add-vga_scanline_invalidated-helper.patch | 63 - ...vga-make-display-updates-thread-safe.patch | 114 - ...ix-display-update-region-calculation.patch | 37 - ...update-region-calculation-split-scre.patch | 46 - ...-pointers-to-vga_draw_line-functions.patch | 497 ---- ...date-multiboot-header-address-values.patch | 61 - ...descriptor-counting-in-virtqueue_pop.patch | 57 - ...017-15119-Reject-options-larger-than.patch | 30 - ...ation-Update-memory-map-in-post_load.patch | 32 - .../0017-vga-drop-line_offset-variable.patch | 52 - ...a-handle-cirrus-vbe-mode-wraparounds.patch | 103 - .../extra/0019-vga-add-ram_addr_t-cast.patch | 30 - ...fix-region-checks-in-wraparound-case.patch | 32 - ...tput-buffer-size-from-websocket-GSou.patch | 54 - ..._malloc0-to-allocate-space-for-xattr.patch | 43 - ...-access-in-mode4and5-write-functions.patch | 58 - ...-check-VirtQueue-Vring-object-is-set.patch | 68 - ...-block-gluster-glfs_lseek-workaround.patch | 107 - ...add-support-for-PREALLOC_MODE_FALLOC.patch | 180 -- ...host_vendor_fms-in-max_x86_cpu_initf.patch | 39 - ...-i386-Define-CPUID_MODEL_ID_SZ-macro.patch | 40 - ...t-use-x86_cpu_load_def-on-max-CPU-mo.patch | 92 - ...CPUDefinition-model_id-to-const-char.patch | 85 - ...1-i386-Add-support-for-SPEC_CTRL-MSR.patch | 135 - .../0032-i386-Add-spec-ctrl-CPUID-bit.patch | 41 - ...EAT_8000_0008_EBX-CPUID-feature-word.patch | 83 - ...ew-IBRS-versions-of-Intel-CPU-models.patch | 518 ---- ...ke-it-thread-safe-fix-qcow2-over-nbd.patch | 121 - .../extra/0037-nbd-strict-nbd_wr_syncv.patch | 52 - ...sync-and-friends-return-0-on-success.patch | 606 ----- .../extra/0039-nbd-make-nbd_drop-public.patch | 151 -- ...id-of-nbd_negotiate_read-and-friends.patch | 292 -- ...regression-when-server-sends-garbage.patch | 153 -- ...uild-failure-in-nbd_read_reply_entry.patch | 55 - ...-spurious-qio_channel_yield-re-entry.patch | 184 -- ...d-read_reply_co-entry-if-send-failed.patch | 160 -- ...rove-nbd-fault-injector.py-startup-p.patch | 60 - ...t-NBD-over-UNIX-domain-sockets-in-08.patch | 454 ---- ...-nbd_co_send_request-fix-return-code.patch | 45 - ...rget-i386-cpu-Add-new-EPYC-CPU-model.patch | 86 - .../0049-i386-Add-EPYC-IBPB-CPU-model.patch | 78 - ...k-file-change-locking-default-to-off.patch | 39 + .../pve/0001-fr-ca-keymap-corrections.patch | 48 - ...djust-network-script-path-to-etc-kvm.patch | 4 +- ...rn-success-on-info-without-snapshots.patch | 8 +- .../patches/pve/0004-use-kvm-by-default.patch | 22 +- .../pve/0005-virtio-balloon-fix-query.patch | 24 +- ...del-to-kvm64-32-instead-of-qemu64-32.patch | 26 +- .../pve/0007-qapi-modify-query-machines.patch | 10 +- .../pve/0008-qapi-modify-spice-query.patch | 18 +- ...-to-pve-certs-unless-otherwise-speci.patch | 4 +- .../pve/0010-internal-snapshot-async.patch | 317 +-- ...0011-convert-savevm-async-to-threads.patch | 33 +- .../pve/0012-qmp-add-get_link_status.patch | 32 +- .../pve/0013-smm_available-false.patch | 4 +- ...space-between-VERSION-and-PKGVERSION.patch | 4 +- .../pve/0015-vnc-altgr-emulation.patch | 8 +- .../0016-vnc-make-x509-imply-tls-again.patch | 4 +- .../pve/0017-vnc-PVE-VNC-authentication.patch | 125 +- ...-rbd_cache_writethrough_until_flush.patch} | 4 +- ...ix-possible-unitialised-return-value.patch | 25 - ...t-qmp_snapshot_drive-add-aiocontext.patch} | 2 +- ...p_delete_drive_snapshot-add-aiocont.patch} | 2 +- ...fs-no-default-logfile-if-daemonized.patch} | 10 +- ... 0022-glusterfs-allow-partial-reads.patch} | 14 +- ...dd-the-zeroinit-block-driver-filter.patch} | 58 +- ...osize-and-read-from-to-stdin-stdout.patch} | 185 +- ...patch => 0025-backup-modify-job-api.patch} | 24 +- ...backup-introduce-vma-archive-format.patch} | 276 +- ....patch => 0027-adding-old-vma-files.patch} | 363 +-- ...ckup-fix-race-in-backup-stop-command.patch | 253 -- ...ng-options-to-drive-mapping-fifo-pro.patch | 165 -- debian/patches/series | 74 +- keycodemapdb/LICENSE.BSD | 27 + keycodemapdb/LICENSE.GPL2 | 339 +++ keycodemapdb/README | 114 + keycodemapdb/data/README | 89 + keycodemapdb/data/keymaps.csv | 539 ++++ keycodemapdb/tests/.gitignore | 11 + keycodemapdb/tests/Makefile | 118 + keycodemapdb/tests/javascript | 53 + keycodemapdb/tests/python2 | 3 + keycodemapdb/tests/python3 | 3 + keycodemapdb/tests/stdc++.cc | 40 + keycodemapdb/tests/stdc.c | 64 + keycodemapdb/tests/test.py | 30 + keycodemapdb/thirdparty/LICENSE-argparse.txt | 20 + keycodemapdb/thirdparty/__init__.py | 0 keycodemapdb/thirdparty/argparse.py | 2392 +++++++++++++++++ keycodemapdb/tools/keymap-gen | 972 +++++++ qemu | 2 +- 101 files changed, 5625 insertions(+), 7173 deletions(-) rename debian/patches/extra/{0035-ratelimit-don-t-align-wait-time-with-slices.patch => 0002-ratelimit-don-t-align-wait-time-with-slices.patch} (91%) delete mode 100644 debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch delete mode 100644 debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch delete mode 100644 debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch delete mode 100644 debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch delete mode 100644 debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch delete mode 100644 debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch delete mode 100644 debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch delete mode 100644 debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch delete mode 100644 debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch delete mode 100644 debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch delete mode 100644 debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch delete mode 100644 debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch delete mode 100644 debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch delete mode 100644 debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch delete mode 100644 debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch delete mode 100644 debian/patches/extra/0017-vga-drop-line_offset-variable.patch delete mode 100644 debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch delete mode 100644 debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch delete mode 100644 debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch delete mode 100644 debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch delete mode 100644 debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch delete mode 100644 debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch delete mode 100644 debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch delete mode 100644 debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch delete mode 100644 debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch delete mode 100644 debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch delete mode 100644 debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch delete mode 100644 debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch delete mode 100644 debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch delete mode 100644 debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch delete mode 100644 debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch delete mode 100644 debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch delete mode 100644 debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch delete mode 100644 debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch delete mode 100644 debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch delete mode 100644 debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch delete mode 100644 debian/patches/extra/0039-nbd-make-nbd_drop-public.patch delete mode 100644 debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch delete mode 100644 debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch delete mode 100644 debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch delete mode 100644 debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch delete mode 100644 debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch delete mode 100644 debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch delete mode 100644 debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch delete mode 100644 debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch delete mode 100644 debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch delete mode 100644 debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch create mode 100644 debian/patches/pve/0001-block-file-change-locking-default-to-off.patch delete mode 100644 debian/patches/pve/0001-fr-ca-keymap-corrections.patch rename debian/patches/pve/{0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch => 0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch} (88%) delete mode 100644 debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch rename debian/patches/pve/{0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch => 0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch} (98%) rename debian/patches/pve/{0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch => 0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch} (97%) rename debian/patches/pve/{0022-glusterfs-no-default-logfile-if-daemonized.patch => 0021-glusterfs-no-default-logfile-if-daemonized.patch} (86%) rename debian/patches/pve/{0023-glusterfs-allow-partial-reads.patch => 0022-glusterfs-allow-partial-reads.patch} (86%) rename debian/patches/pve/{0024-block-add-the-zeroinit-block-driver-filter.patch => 0023-block-add-the-zeroinit-block-driver-filter.patch} (76%) rename debian/patches/pve/{0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch => 0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch} (79%) rename debian/patches/pve/{0026-backup-modify-job-api.patch => 0025-backup-modify-job-api.patch} (85%) rename debian/patches/pve/{0027-backup-introduce-vma-archive-format.patch => 0026-backup-introduce-vma-archive-format.patch} (85%) rename debian/patches/pve/{0028-adding-old-vma-files.patch => 0027-adding-old-vma-files.patch} (91%) delete mode 100644 debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch delete mode 100644 debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch create mode 100644 keycodemapdb/LICENSE.BSD create mode 100644 keycodemapdb/LICENSE.GPL2 create mode 100644 keycodemapdb/README create mode 100644 keycodemapdb/data/README create mode 100644 keycodemapdb/data/keymaps.csv create mode 100644 keycodemapdb/tests/.gitignore create mode 100644 keycodemapdb/tests/Makefile create mode 100755 keycodemapdb/tests/javascript create mode 100755 keycodemapdb/tests/python2 create mode 100755 keycodemapdb/tests/python3 create mode 100644 keycodemapdb/tests/stdc++.cc create mode 100644 keycodemapdb/tests/stdc.c create mode 100644 keycodemapdb/tests/test.py create mode 100644 keycodemapdb/thirdparty/LICENSE-argparse.txt create mode 100644 keycodemapdb/thirdparty/__init__.py create mode 100644 keycodemapdb/thirdparty/argparse.py create mode 100755 keycodemapdb/tools/keymap-gen diff --git a/Makefile b/Makefile index c760a7f..db62cae 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # also update debian/changelog -KVMVER=2.9.1 -KVMPKGREL=9 +KVMVER=2.11.1 +KVMPKGREL=1 KVMPACKAGE = pve-qemu-kvm KVMSRC = qemu @@ -25,18 +25,31 @@ submodule: .PHONY: deb kvm deb kvm: $(DEBS) $(DEB_DBG): $(DEB) -$(DEB): | submodule +$(DEB): keycodemapdb | submodule rm -f *.deb rm -rf $(BUILDSRC) mkdir $(BUILDSRC) cp -a $(KVMSRC)/* $(BUILDSRC)/ cp -a debian $(BUILDSRC)/debian + rm -rf $(BUILDSRC)/ui/keycodemapdb + cp -a keycodemapdb $(BUILDSRC)/ui/ echo "git clone git://git.proxmox.com/git/pve-qemu-kvm.git\\ngit checkout $(GITVERSION)" > $(BUILDSRC)/debian/SOURCE # set package version sed -i 's/^pkgversion="".*/pkgversion="${KVMPACKAGE}_${KVMVER}-${KVMPKGREL}"/' $(BUILDSRC)/configure cd $(BUILDSRC); dpkg-buildpackage -b -rfakeroot -us -uc lintian $(DEBS) || true +.PHONY: update +update: + cd $(KVMSRC) && git submodule deinit ui/keycodemapdb || true + rm -rf $(KVMSRC)/ui/keycodemapdb + mkdir $(KVMSRC)/ui/keycodemapdb + cd $(KVMSRC) && git submodule update --init ui/keycodemapdb + rm -rf keycodemapdb + mkdir keycodemapdb + cp -R $(KVMSRC)/ui/keycodemapdb/* keycodemapdb/ + git add keycodemapdb + .PHONY: upload upload: $(DEBS) tar cf - ${DEBS} | ssh repoman@repo.proxmox.com upload --product pve --dist stretch diff --git a/debian/changelog b/debian/changelog index 9a5bfa5..8989aa4 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +pve-qemu-kvm (2.11.1-1) stable; urgency=medium + + * update to 2.11.1 + + -- Proxmox Support Team Thu, 22 Feb 2018 12:34:43 +0100 + pve-qemu-kvm (2.9.1-9) stable; urgency=medium * add EPYC and EPYC-IPBP cpu models diff --git a/debian/patches/extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch b/debian/patches/extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch index 09714dd..1b43e71 100644 --- a/debian/patches/extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch +++ b/debian/patches/extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch @@ -9,7 +9,7 @@ This reverts commit b8eb5512fd8a115f164edbbe897cdf8884920ccb. 1 file changed, 9 insertions(+) diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c -index 1ef56f8d10..31fb73e9fb 100644 +index 78903ea909..cdfbec5e47 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -257,6 +257,15 @@ static void apic_reset_common(DeviceState *dev) diff --git a/debian/patches/extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch b/debian/patches/extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch similarity index 91% rename from debian/patches/extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch rename to debian/patches/extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch index ef8c525..9502552 100644 --- a/debian/patches/extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch +++ b/debian/patches/extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch @@ -13,10 +13,10 @@ Signed-off-by: Wolfgang Bumiller 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/qemu/ratelimit.h b/include/qemu/ratelimit.h -index 8da1232574..324ff04fe2 100644 +index 8dece483f5..1b38291823 100644 --- a/include/qemu/ratelimit.h +++ b/include/qemu/ratelimit.h -@@ -35,7 +35,7 @@ typedef struct { +@@ -36,7 +36,7 @@ typedef struct { static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -25,7 +25,7 @@ index 8da1232574..324ff04fe2 100644 assert(limit->slice_quota && limit->slice_ns); -@@ -54,12 +54,11 @@ static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) +@@ -55,12 +55,11 @@ static inline int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n) return 0; } diff --git a/debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch b/debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch deleted file mode 100644 index 05ed7e8..0000000 --- a/debian/patches/extra/0002-virtio-serial-fix-segfault-on-disconnect.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Stefan Hajnoczi -Date: Fri, 2 Jun 2017 10:54:24 +0100 -Subject: [PATCH] virtio-serial: fix segfault on disconnect - -Since commit d4c19cdeeb2f1e474bc426a6da261f1d7346eb5b ("virtio-serial: -add missing virtio_detach_element() call") the following commands may -cause QEMU to segfault: - - $ qemu -M accel=kvm -cpu host -m 1G \ - -drive if=virtio,file=test.img,format=raw \ - -device virtio-serial-pci,id=virtio-serial0 \ - -chardev socket,id=channel1,path=/tmp/chardev.sock,server,nowait \ - -device virtserialport,chardev=channel1,bus=virtio-serial0.0,id=port1 - $ nc -U /tmp/chardev.sock - ^C - - (guest)$ cat /dev/zero >/dev/vport0p1 - -The segfault is non-deterministic: if the event loop notices the socket -has been closed then there is no crash. The disconnect has to happen -right before QEMU attempts to write data to the socket. - -The backtrace is as follows: - - Thread 1 "qemu-system-x86" received signal SIGSEGV, Segmentation fault. - 0x00005555557e0698 in do_flush_queued_data (port=0x5555582cedf0, vq=0x7fffcc854290, vdev=0x55555807b1d0) at hw/char/virtio-serial-bus.c:180 - 180 for (i = port->iov_idx; i < port->elem->out_num; i++) { - #1 0x000055555580d363 in virtio_queue_notify_vq (vq=0x7fffcc854290) at hw/virtio/virtio.c:1524 - #2 0x000055555580d363 in virtio_queue_host_notifier_read (n=0x7fffcc8542f8) at hw/virtio/virtio.c:2430 - #3 0x0000555555b3482c in aio_dispatch_handlers (ctx=ctx@entry=0x5555566b8c80) at util/aio-posix.c:399 - #4 0x0000555555b350d8 in aio_dispatch (ctx=0x5555566b8c80) at util/aio-posix.c:430 - #5 0x0000555555b3212e in aio_ctx_dispatch (source=, callback=, user_data=) at util/async.c:261 - #6 0x00007fffde71de52 in g_main_context_dispatch () at /lib64/libglib-2.0.so.0 - #7 0x0000555555b34353 in glib_pollfds_poll () at util/main-loop.c:213 - #8 0x0000555555b34353 in os_host_main_loop_wait (timeout=) at util/main-loop.c:261 - #9 0x0000555555b34353 in main_loop_wait (nonblocking=) at util/main-loop.c:517 - #10 0x0000555555773207 in main_loop () at vl.c:1917 - #11 0x0000555555773207 in main (argc=, argv=, envp=) at vl.c:4751 - -The do_flush_queued_data() function does not anticipate chardev close -events during vsc->have_data(). It expects port->elem to remain -non-NULL for the duration its for loop. - -The fix is simply to return from do_flush_queued_data() if the port -closes because the close event already frees port->elem and drains the -virtqueue - there is nothing left for do_flush_queued_data() to do. - -Reported-by: Sitong Liu -Reported-by: Min Deng -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Michael S. Tsirkin -Signed-off-by: Michael S. Tsirkin ---- - hw/char/virtio-serial-bus.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c -index aa9c11ae92..f5bc173844 100644 ---- a/hw/char/virtio-serial-bus.c -+++ b/hw/char/virtio-serial-bus.c -@@ -186,6 +186,9 @@ static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, - port->elem->out_sg[i].iov_base - + port->iov_offset, - buf_size); -+ if (!port->elem) { /* bail if we got disconnected */ -+ return; -+ } - if (port->throttled) { - port->iov_idx = i; - if (ret > 0) { --- -2.11.0 - diff --git a/debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch b/debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch deleted file mode 100644 index bd2755a..0000000 --- a/debian/patches/extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch +++ /dev/null @@ -1,125 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Thu, 1 Jun 2017 17:26:14 +0200 -Subject: [PATCH] megasas: always store SCSIRequest* into MegasasCmd - -This ensures that the request is unref'ed properly, and avoids a -segmentation fault in the new qtest testcase that is added. -This is CVE-2017-9503. - -Reported-by: Zhangyanyu -Signed-off-by: Paolo Bonzini ---- - hw/scsi/megasas.c | 31 ++++++++++++++++--------------- - 1 file changed, 16 insertions(+), 15 deletions(-) - -diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c -index 135662df31..734fdaef90 100644 ---- a/hw/scsi/megasas.c -+++ b/hw/scsi/megasas.c -@@ -609,6 +609,9 @@ static void megasas_reset_frames(MegasasState *s) - static void megasas_abort_command(MegasasCmd *cmd) - { - /* Never abort internal commands. */ -+ if (cmd->dcmd_opcode != -1) { -+ return; -+ } - if (cmd->req != NULL) { - scsi_req_cancel(cmd->req); - } -@@ -1017,7 +1020,6 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, - uint64_t pd_size; - uint16_t pd_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); - uint8_t cmdbuf[6]; -- SCSIRequest *req; - size_t len, resid; - - if (!cmd->iov_buf) { -@@ -1026,8 +1028,8 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, - info->inquiry_data[0] = 0x7f; /* Force PQual 0x3, PType 0x1f */ - info->vpd_page83[0] = 0x7f; - megasas_setup_inquiry(cmdbuf, 0, sizeof(info->inquiry_data)); -- req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); -- if (!req) { -+ cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); -+ if (!cmd->req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "PD get info std inquiry"); - g_free(cmd->iov_buf); -@@ -1036,26 +1038,26 @@ static int megasas_pd_get_info_submit(SCSIDevice *sdev, int lun, - } - trace_megasas_dcmd_internal_submit(cmd->index, - "PD get info std inquiry", lun); -- len = scsi_req_enqueue(req); -+ len = scsi_req_enqueue(cmd->req); - if (len > 0) { - cmd->iov_size = len; -- scsi_req_continue(req); -+ scsi_req_continue(cmd->req); - } - return MFI_STAT_INVALID_STATUS; - } else if (info->inquiry_data[0] != 0x7f && info->vpd_page83[0] == 0x7f) { - megasas_setup_inquiry(cmdbuf, 0x83, sizeof(info->vpd_page83)); -- req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); -- if (!req) { -+ cmd->req = scsi_req_new(sdev, cmd->index, lun, cmdbuf, cmd); -+ if (!cmd->req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "PD get info vpd inquiry"); - return MFI_STAT_FLASH_ALLOC_FAIL; - } - trace_megasas_dcmd_internal_submit(cmd->index, - "PD get info vpd inquiry", lun); -- len = scsi_req_enqueue(req); -+ len = scsi_req_enqueue(cmd->req); - if (len > 0) { - cmd->iov_size = len; -- scsi_req_continue(req); -+ scsi_req_continue(cmd->req); - } - return MFI_STAT_INVALID_STATUS; - } -@@ -1217,7 +1219,6 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, - struct mfi_ld_info *info = cmd->iov_buf; - size_t dcmd_size = sizeof(struct mfi_ld_info); - uint8_t cdb[6]; -- SCSIRequest *req; - ssize_t len, resid; - uint16_t sdev_id = ((sdev->id & 0xFF) << 8) | (lun & 0xFF); - uint64_t ld_size; -@@ -1226,8 +1227,8 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, - cmd->iov_buf = g_malloc0(dcmd_size); - info = cmd->iov_buf; - megasas_setup_inquiry(cdb, 0x83, sizeof(info->vpd_page83)); -- req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); -- if (!req) { -+ cmd->req = scsi_req_new(sdev, cmd->index, lun, cdb, cmd); -+ if (!cmd->req) { - trace_megasas_dcmd_req_alloc_failed(cmd->index, - "LD get info vpd inquiry"); - g_free(cmd->iov_buf); -@@ -1236,10 +1237,10 @@ static int megasas_ld_get_info_submit(SCSIDevice *sdev, int lun, - } - trace_megasas_dcmd_internal_submit(cmd->index, - "LD get info vpd inquiry", lun); -- len = scsi_req_enqueue(req); -+ len = scsi_req_enqueue(cmd->req); - if (len > 0) { - cmd->iov_size = len; -- scsi_req_continue(req); -+ scsi_req_continue(cmd->req); - } - return MFI_STAT_INVALID_STATUS; - } -@@ -1851,7 +1852,7 @@ static void megasas_command_complete(SCSIRequest *req, uint32_t status, - return; - } - -- if (cmd->req == NULL) { -+ if (cmd->dcmd_opcode != -1) { - /* - * Internal command complete - */ --- -2.11.0 - diff --git a/debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch b/debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch deleted file mode 100644 index 2832d63..0000000 --- a/debian/patches/extra/0004-slirp-check-len-against-dhcp-options-array-end.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Mon, 17 Jul 2017 17:33:26 +0530 -Subject: [PATCH] slirp: check len against dhcp options array end - -While parsing dhcp options string in 'dhcp_decode', if an options' -length 'len' appeared towards the end of 'bp_vend' array, ensuing -read could lead to an OOB memory access issue. Add check to avoid it. - -This is CVE-2017-11434. - -Reported-by: Reno Robert -Signed-off-by: Prasad J Pandit -Signed-off-by: Samuel Thibault ---- - slirp/bootp.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/slirp/bootp.c b/slirp/bootp.c -index 5a4646c182..5dd1a415b5 100644 ---- a/slirp/bootp.c -+++ b/slirp/bootp.c -@@ -123,6 +123,9 @@ static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type, - if (p >= p_end) - break; - len = *p++; -+ if (p + len > p_end) { -+ break; -+ } - DPRINTF("dhcp: tag=%d len=%d\n", tag, len); - - switch(tag) { --- -2.11.0 - diff --git a/debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch b/debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch deleted file mode 100644 index 86f970c..0000000 --- a/debian/patches/extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Stefan Hajnoczi -Date: Wed, 9 Aug 2017 17:02:11 +0100 -Subject: [PATCH] IDE: Do not flush empty CDROM drives - -The block backend changed in a way that flushing empty CDROM drives now -crashes. Amend IDE to avoid doing so until the root problem can be -addressed for 2.11. - -Original patch by John Snow . - -Reported-by: Kieron Shorrock -Signed-off-by: Stefan Hajnoczi -Reviewed-by: Eric Blake -Message-id: 20170809160212.29976-2-stefanha@redhat.com -Signed-off-by: Stefan Hajnoczi ---- - hw/ide/core.c | 10 +++++++++- - 1 file changed, 9 insertions(+), 1 deletion(-) - -diff --git a/hw/ide/core.c b/hw/ide/core.c -index 0b48b64d3a..bea39536b0 100644 ---- a/hw/ide/core.c -+++ b/hw/ide/core.c -@@ -1063,7 +1063,15 @@ static void ide_flush_cache(IDEState *s) - s->status |= BUSY_STAT; - ide_set_retry(s); - block_acct_start(blk_get_stats(s->blk), &s->acct, 0, BLOCK_ACCT_FLUSH); -- s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); -+ -+ if (blk_bs(s->blk)) { -+ s->pio_aiocb = blk_aio_flush(s->blk, ide_flush_cb, s); -+ } else { -+ /* XXX blk_aio_flush() crashes when blk_bs(blk) is NULL, remove this -+ * temporary workaround when blk_aio_*() functions handle NULL blk_bs. -+ */ -+ ide_flush_cb(s, 0); -+ } - } - - static void ide_cfata_metadata_inquiry(IDEState *s) --- -2.11.0 - diff --git a/debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch b/debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch deleted file mode 100644 index cd584a2..0000000 --- a/debian/patches/extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 21 Apr 2017 11:16:24 +0200 -Subject: [PATCH] bitmap: add bitmap_copy_and_clear_atomic - -Signed-off-by: Gerd Hoffmann -Message-id: 20170421091632.30900-2-kraxel@redhat.com -Signed-off-by: Gerd Hoffmann ---- - include/qemu/bitmap.h | 2 ++ - util/bitmap.c | 11 +++++++++++ - 2 files changed, 13 insertions(+) - -diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h -index 63ea2d0b1e..c318da12d7 100644 ---- a/include/qemu/bitmap.h -+++ b/include/qemu/bitmap.h -@@ -220,6 +220,8 @@ void bitmap_set(unsigned long *map, long i, long len); - void bitmap_set_atomic(unsigned long *map, long i, long len); - void bitmap_clear(unsigned long *map, long start, long nr); - bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr); -+void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, -+ long nr); - unsigned long bitmap_find_next_zero_area(unsigned long *map, - unsigned long size, - unsigned long start, -diff --git a/util/bitmap.c b/util/bitmap.c -index c1a84ca5e3..efced9a7d8 100644 ---- a/util/bitmap.c -+++ b/util/bitmap.c -@@ -287,6 +287,17 @@ bool bitmap_test_and_clear_atomic(unsigned long *map, long start, long nr) - return dirty != 0; - } - -+void bitmap_copy_and_clear_atomic(unsigned long *dst, unsigned long *src, -+ long nr) -+{ -+ while (nr > 0) { -+ *dst = atomic_xchg(src, 0); -+ dst++; -+ src++; -+ nr -= BITS_PER_LONG; -+ } -+} -+ - #define ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) - - /** --- -2.11.0 - diff --git a/debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch b/debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch deleted file mode 100644 index 8b202fb..0000000 --- a/debian/patches/extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch +++ /dev/null @@ -1,241 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 21 Apr 2017 11:16:25 +0200 -Subject: [PATCH] memory: add support getting and using a dirty bitmap copy. - -This patch adds support for getting and using a local copy of the dirty -bitmap. - -memory_region_snapshot_and_clear_dirty() will create a snapshot of the -dirty bitmap for the specified range, clear the dirty bitmap and return -the copy. The returned bitmap can be a bit larger than requested, the -range is expanded so the code can copy unsigned longs from the bitmap -and avoid atomic bit update operations. - -memory_region_snapshot_get_dirty() will return the dirty status of -pages, pretty much like memory_region_get_dirty(), but using the copy -returned by memory_region_copy_and_clear_dirty(). - -Signed-off-by: Gerd Hoffmann -Message-id: 20170421091632.30900-3-kraxel@redhat.com -Signed-off-by: Gerd Hoffmann ---- - exec.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ - include/exec/memory.h | 47 +++++++++++++++++++++++++++++++ - include/exec/ram_addr.h | 7 +++++ - include/qemu/typedefs.h | 1 + - memory.c | 17 +++++++++++ - 5 files changed, 147 insertions(+) - -diff --git a/exec.c b/exec.c -index fcb5b16131..07c2c8ea88 100644 ---- a/exec.c -+++ b/exec.c -@@ -223,6 +223,12 @@ struct CPUAddressSpace { - MemoryListener tcg_as_listener; - }; - -+struct DirtyBitmapSnapshot { -+ ram_addr_t start; -+ ram_addr_t end; -+ unsigned long dirty[]; -+}; -+ - #endif - - #if !defined(CONFIG_USER_ONLY) -@@ -1061,6 +1067,75 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, - return dirty; - } - -+DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty -+ (ram_addr_t start, ram_addr_t length, unsigned client) -+{ -+ DirtyMemoryBlocks *blocks; -+ unsigned long align = 1UL << (TARGET_PAGE_BITS + BITS_PER_LEVEL); -+ ram_addr_t first = QEMU_ALIGN_DOWN(start, align); -+ ram_addr_t last = QEMU_ALIGN_UP(start + length, align); -+ DirtyBitmapSnapshot *snap; -+ unsigned long page, end, dest; -+ -+ snap = g_malloc0(sizeof(*snap) + -+ ((last - first) >> (TARGET_PAGE_BITS + 3))); -+ snap->start = first; -+ snap->end = last; -+ -+ page = first >> TARGET_PAGE_BITS; -+ end = last >> TARGET_PAGE_BITS; -+ dest = 0; -+ -+ rcu_read_lock(); -+ -+ blocks = atomic_rcu_read(&ram_list.dirty_memory[client]); -+ -+ while (page < end) { -+ unsigned long idx = page / DIRTY_MEMORY_BLOCK_SIZE; -+ unsigned long offset = page % DIRTY_MEMORY_BLOCK_SIZE; -+ unsigned long num = MIN(end - page, DIRTY_MEMORY_BLOCK_SIZE - offset); -+ -+ assert(QEMU_IS_ALIGNED(offset, (1 << BITS_PER_LEVEL))); -+ assert(QEMU_IS_ALIGNED(num, (1 << BITS_PER_LEVEL))); -+ offset >>= BITS_PER_LEVEL; -+ -+ bitmap_copy_and_clear_atomic(snap->dirty + dest, -+ blocks->blocks[idx] + offset, -+ num); -+ page += num; -+ dest += num >> BITS_PER_LEVEL; -+ } -+ -+ rcu_read_unlock(); -+ -+ if (tcg_enabled()) { -+ tlb_reset_dirty_range_all(start, length); -+ } -+ -+ return snap; -+} -+ -+bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, -+ ram_addr_t start, -+ ram_addr_t length) -+{ -+ unsigned long page, end; -+ -+ assert(start >= snap->start); -+ assert(start + length <= snap->end); -+ -+ end = TARGET_PAGE_ALIGN(start + length - snap->start) >> TARGET_PAGE_BITS; -+ page = (start - snap->start) >> TARGET_PAGE_BITS; -+ -+ while (page < end) { -+ if (test_bit(page, snap->dirty)) { -+ return true; -+ } -+ page++; -+ } -+ return false; -+} -+ - /* Called from RCU critical section */ - hwaddr memory_region_section_get_iotlb(CPUState *cpu, - MemoryRegionSection *section, -diff --git a/include/exec/memory.h b/include/exec/memory.h -index f20b191793..1e15e79d00 100644 ---- a/include/exec/memory.h -+++ b/include/exec/memory.h -@@ -871,6 +871,53 @@ void memory_region_set_dirty(MemoryRegion *mr, hwaddr addr, - */ - bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, - hwaddr size, unsigned client); -+ -+/** -+ * memory_region_snapshot_and_clear_dirty: Get a snapshot of the dirty -+ * bitmap and clear it. -+ * -+ * Creates a snapshot of the dirty bitmap, clears the dirty bitmap and -+ * returns the snapshot. The snapshot can then be used to query dirty -+ * status, using memory_region_snapshot_get_dirty. Unlike -+ * memory_region_test_and_clear_dirty this allows to query the same -+ * page multiple times, which is especially useful for display updates -+ * where the scanlines often are not page aligned. -+ * -+ * The dirty bitmap region which gets copyed into the snapshot (and -+ * cleared afterwards) can be larger than requested. The boundaries -+ * are rounded up/down so complete bitmap longs (covering 64 pages on -+ * 64bit hosts) can be copied over into the bitmap snapshot. Which -+ * isn't a problem for display updates as the extra pages are outside -+ * the visible area, and in case the visible area changes a full -+ * display redraw is due anyway. Should other use cases for this -+ * function emerge we might have to revisit this implementation -+ * detail. -+ * -+ * Use g_free to release DirtyBitmapSnapshot. -+ * -+ * @mr: the memory region being queried. -+ * @addr: the address (relative to the start of the region) being queried. -+ * @size: the size of the range being queried. -+ * @client: the user of the logging information; typically %DIRTY_MEMORY_VGA. -+ */ -+DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, -+ hwaddr addr, -+ hwaddr size, -+ unsigned client); -+ -+/** -+ * memory_region_snapshot_get_dirty: Check whether a range of bytes is dirty -+ * in the specified dirty bitmap snapshot. -+ * -+ * @mr: the memory region being queried. -+ * @snap: the dirty bitmap snapshot -+ * @addr: the address (relative to the start of the region) being queried. -+ * @size: the size of the range being queried. -+ */ -+bool memory_region_snapshot_get_dirty(MemoryRegion *mr, -+ DirtyBitmapSnapshot *snap, -+ hwaddr addr, hwaddr size); -+ - /** - * memory_region_sync_dirty_bitmap: Synchronize a region's dirty bitmap with - * any external TLBs (e.g. kvm) -diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h -index b05dc84ab9..2b63d7f59e 100644 ---- a/include/exec/ram_addr.h -+++ b/include/exec/ram_addr.h -@@ -343,6 +343,13 @@ bool cpu_physical_memory_test_and_clear_dirty(ram_addr_t start, - ram_addr_t length, - unsigned client); - -+DirtyBitmapSnapshot *cpu_physical_memory_snapshot_and_clear_dirty -+ (ram_addr_t start, ram_addr_t length, unsigned client); -+ -+bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, -+ ram_addr_t start, -+ ram_addr_t length); -+ - static inline void cpu_physical_memory_clear_dirty_range(ram_addr_t start, - ram_addr_t length) - { -diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h -index e95f28cfec..f08d327aec 100644 ---- a/include/qemu/typedefs.h -+++ b/include/qemu/typedefs.h -@@ -23,6 +23,7 @@ typedef struct CPUAddressSpace CPUAddressSpace; - typedef struct CPUState CPUState; - typedef struct DeviceListener DeviceListener; - typedef struct DeviceState DeviceState; -+typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; - typedef struct DisplayChangeListener DisplayChangeListener; - typedef struct DisplayState DisplayState; - typedef struct DisplaySurface DisplaySurface; -diff --git a/memory.c b/memory.c -index 4c95aaf39c..8a0648551f 100644 ---- a/memory.c -+++ b/memory.c -@@ -1716,6 +1716,23 @@ bool memory_region_test_and_clear_dirty(MemoryRegion *mr, hwaddr addr, - memory_region_get_ram_addr(mr) + addr, size, client); - } - -+DirtyBitmapSnapshot *memory_region_snapshot_and_clear_dirty(MemoryRegion *mr, -+ hwaddr addr, -+ hwaddr size, -+ unsigned client) -+{ -+ assert(mr->ram_block); -+ return cpu_physical_memory_snapshot_and_clear_dirty( -+ memory_region_get_ram_addr(mr) + addr, size, client); -+} -+ -+bool memory_region_snapshot_get_dirty(MemoryRegion *mr, DirtyBitmapSnapshot *snap, -+ hwaddr addr, hwaddr size) -+{ -+ assert(mr->ram_block); -+ return cpu_physical_memory_snapshot_get_dirty(snap, -+ memory_region_get_ram_addr(mr) + addr, size); -+} - - void memory_region_sync_dirty_bitmap(MemoryRegion *mr) - { --- -2.11.0 - diff --git a/debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch b/debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch deleted file mode 100644 index 78227ee..0000000 --- a/debian/patches/extra/0008-vga-add-vga_scanline_invalidated-helper.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 21 Apr 2017 11:16:26 +0200 -Subject: [PATCH] vga: add vga_scanline_invalidated helper - -Add vga_scanline_invalidated helper to check whenever a scanline was -invalidated. Add a sanity check to fix OOB read access for display -heights larger than 2048. - -Only cirrus uses this, for hardware cursor rendering, so having this -work properly for the first 2048 scanlines only shouldn't be a problem -as the cirrus can't handle large resolutions anyway. Also changing the -invalidated_y_table size would break live migration. - -Signed-off-by: Gerd Hoffmann -Message-id: 20170421091632.30900-4-kraxel@redhat.com -Signed-off-by: Gerd Hoffmann ---- - hw/display/vga.c | 14 +++++++++++--- - 1 file changed, 11 insertions(+), 3 deletions(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 69c3e1d674..3991b88aac 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1434,6 +1434,14 @@ void vga_invalidate_scanlines(VGACommonState *s, int y1, int y2) - } - } - -+static bool vga_scanline_invalidated(VGACommonState *s, int y) -+{ -+ if (y >= VGA_MAX_HEIGHT) { -+ return false; -+ } -+ return s->invalidated_y_table[y >> 5] & (1 << (y & 0x1f)); -+} -+ - void vga_sync_dirty_bitmap(VGACommonState *s) - { - memory_region_sync_dirty_bitmap(&s->vram); -@@ -1638,8 +1646,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - page1 = addr + bwidth - 1; - update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, - DIRTY_MEMORY_VGA); -- /* explicit invalidation for the hardware cursor */ -- update |= (s->invalidated_y_table[y >> 5] >> (y & 0x1f)) & 1; -+ /* explicit invalidation for the hardware cursor (cirrus only) */ -+ update |= vga_scanline_invalidated(s, y); - if (update) { - if (y_start < 0) - y_start = y; -@@ -1686,7 +1694,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - page_max - page_min, - DIRTY_MEMORY_VGA); - } -- memset(s->invalidated_y_table, 0, ((height + 31) >> 5) * 4); -+ memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table)); - } - - static void vga_draw_blank(VGACommonState *s, int full_update) --- -2.11.0 - diff --git a/debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch b/debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch deleted file mode 100644 index 6c1edf8..0000000 --- a/debian/patches/extra/0009-vga-make-display-updates-thread-safe.patch +++ /dev/null @@ -1,114 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 21 Apr 2017 11:16:27 +0200 -Subject: [PATCH] vga: make display updates thread safe. - -The vga code clears the dirty bits *after* reading the framebuffer -memory. So if the guest framebuffer updates hits the race window -between vga reading the framebuffer and vga clearing the dirty bits -vga will miss that update - -Fix it by using the new memory_region_copy_and_clear_dirty() -memory_region_copy_get_dirty() functions. That way we clear the -dirty bitmap before reading the framebuffer. Any guest display -updates happening in parallel will be properly tracked in the -dirty bitmap then and the next display refresh will pick them up. - -Problem triggers with mttcg only. Before mttcg was merged tcg -never ran in parallel to vga emulation. Using kvm will hide the -problem too, due to qemu operating on a userspace copy of the -kernel's dirty bitmap. - -Signed-off-by: Gerd Hoffmann -Message-id: 20170421091632.30900-5-kraxel@redhat.com -Signed-off-by: Gerd Hoffmann ---- - hw/display/vga.c | 36 +++++++++++++++++------------------- - 1 file changed, 17 insertions(+), 19 deletions(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 3991b88aac..b2516c8d21 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1465,7 +1465,8 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - DisplaySurface *surface = qemu_console_surface(s->con); - int y1, y, update, linesize, y_start, double_scan, mask, depth; - int width, height, shift_control, line_offset, bwidth, bits; -- ram_addr_t page0, page1, page_min, page_max; -+ ram_addr_t page0, page1; -+ DirtyBitmapSnapshot *snap = NULL; - int disp_width, multi_scan, multi_run; - uint8_t *d; - uint32_t v, addr1, addr; -@@ -1480,9 +1481,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - - full_update |= update_basic_params(s); - -- if (!full_update) -- vga_sync_dirty_bitmap(s); -- - s->get_resolution(s, &width, &height); - disp_width = width; - -@@ -1625,11 +1623,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - addr1 = (s->start_addr * 4); - bwidth = (width * bits + 7) / 8; - y_start = -1; -- page_min = -1; -- page_max = 0; - d = surface_data(surface); - linesize = surface_stride(surface); - y1 = 0; -+ -+ if (!full_update) { -+ vga_sync_dirty_bitmap(s); -+ snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1, -+ bwidth * height, -+ DIRTY_MEMORY_VGA); -+ } -+ - for(y = 0; y < height; y++) { - addr = addr1; - if (!(s->cr[VGA_CRTC_MODE] & 1)) { -@@ -1644,17 +1648,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - update = full_update; - page0 = addr; - page1 = addr + bwidth - 1; -- update |= memory_region_get_dirty(&s->vram, page0, page1 - page0, -- DIRTY_MEMORY_VGA); -+ if (full_update) { -+ update = 1; -+ } else { -+ update = memory_region_snapshot_get_dirty(&s->vram, snap, -+ page0, page1 - page0); -+ } - /* explicit invalidation for the hardware cursor (cirrus only) */ - update |= vga_scanline_invalidated(s, y); - if (update) { - if (y_start < 0) - y_start = y; -- if (page0 < page_min) -- page_min = page0; -- if (page1 > page_max) -- page_max = page1; - if (!(is_buffer_shared(surface))) { - vga_draw_line(s, d, s->vram_ptr + addr, width); - if (s->cursor_draw_line) -@@ -1687,13 +1691,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - dpy_gfx_update(s->con, 0, y_start, - disp_width, y - y_start); - } -- /* reset modified pages */ -- if (page_max >= page_min) { -- memory_region_reset_dirty(&s->vram, -- page_min, -- page_max - page_min, -- DIRTY_MEMORY_VGA); -- } -+ g_free(snap); - memset(s->invalidated_y_table, 0, sizeof(s->invalidated_y_table)); - } - --- -2.11.0 - diff --git a/debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch b/debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch deleted file mode 100644 index 96f35ee..0000000 --- a/debian/patches/extra/0010-vga-fix-display-update-region-calculation.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Tue, 9 May 2017 12:48:39 +0200 -Subject: [PATCH] vga: fix display update region calculation -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -vga display update mis-calculated the region for the dirty bitmap -snapshot in case the scanlines are padded. This can triggere an -assert in cpu_physical_memory_snapshot_get_dirty(). - -Fixes: fec5e8c92becad223df9d972770522f64aafdb72 -Reported-by: Kevin Wolf -Reported-by: 李强 -Signed-off-by: Gerd Hoffmann -Message-id: 20170509104839.19415-1-kraxel@redhat.com ---- - hw/display/vga.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index b2516c8d21..dcc95f88e2 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1630,7 +1630,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - if (!full_update) { - vga_sync_dirty_bitmap(s); - snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1, -- bwidth * height, -+ line_offset * height, - DIRTY_MEMORY_VGA); - } - --- -2.11.0 - diff --git a/debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch b/debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch deleted file mode 100644 index 9ad4652..0000000 --- a/debian/patches/extra/0011-vga-fix-display-update-region-calculation-split-scre.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 1 Sep 2017 14:57:38 +0200 -Subject: [PATCH] vga: fix display update region calculation (split screen) - -vga display update mis-calculated the region for the dirty bitmap -snapshot in case split screen mode is used. This can trigger an -assert in cpu_physical_memory_snapshot_get_dirty(). - -Impact: DoS for privileged guest users. - -Fixes: CVE-2017-13673 -Fixes: fec5e8c92becad223df9d972770522f64aafdb72 -Cc: P J P -Reported-by: David Buchanan -Signed-off-by: Gerd Hoffmann -Message-id: 20170828123307.15392-1-kraxel@redhat.com ---- - hw/display/vga.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index dcc95f88e2..533d8d7895 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1628,9 +1628,15 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - y1 = 0; - - if (!full_update) { -+ ram_addr_t region_start = addr1; -+ ram_addr_t region_end = addr1 + line_offset * height; - vga_sync_dirty_bitmap(s); -- snap = memory_region_snapshot_and_clear_dirty(&s->vram, addr1, -- line_offset * height, -+ if (s->line_compare < height) { -+ /* split screen mode */ -+ region_start = 0; -+ } -+ snap = memory_region_snapshot_and_clear_dirty(&s->vram, region_start, -+ region_end - region_start, - DIRTY_MEMORY_VGA); - } - --- -2.11.0 - diff --git a/debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch b/debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch deleted file mode 100644 index 12395c4..0000000 --- a/debian/patches/extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch +++ /dev/null @@ -1,497 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Fri, 1 Sep 2017 14:57:39 +0200 -Subject: [PATCH] vga: stop passing pointers to vga_draw_line* functions - -Instead pass around the address (aka offset into vga memory). -Add vga_read_* helper functions which apply vbe_size_mask to -the address, to make sure the address stays within the valid -range, similar to the cirrus blitter fixes (commits ffaf857778 -and 026aeffcb4). - -Impact: DoS for privileged guest users. qemu crashes with -a segfault, when hitting the guard page after vga memory -allocation, while reading vga memory for display updates. - -Fixes: CVE-2017-13672 -Cc: P J P -Reported-by: David Buchanan -Signed-off-by: Gerd Hoffmann -Message-id: 20170828122906.18993-1-kraxel@redhat.com ---- - hw/display/vga-helpers.h | 202 ++++++++++++++++++++++++++--------------------- - hw/display/vga.c | 5 +- - hw/display/vga_int.h | 1 + - 3 files changed, 114 insertions(+), 94 deletions(-) - -diff --git a/hw/display/vga-helpers.h b/hw/display/vga-helpers.h -index 94f6de2046..5a752b3f9e 100644 ---- a/hw/display/vga-helpers.h -+++ b/hw/display/vga-helpers.h -@@ -95,20 +95,46 @@ static void vga_draw_glyph9(uint8_t *d, int linesize, - } while (--h); - } - -+static inline uint8_t vga_read_byte(VGACommonState *vga, uint32_t addr) -+{ -+ return vga->vram_ptr[addr & vga->vbe_size_mask]; -+} -+ -+static inline uint16_t vga_read_word_le(VGACommonState *vga, uint32_t addr) -+{ -+ uint32_t offset = addr & vga->vbe_size_mask & ~1; -+ uint16_t *ptr = (uint16_t *)(vga->vram_ptr + offset); -+ return lduw_le_p(ptr); -+} -+ -+static inline uint16_t vga_read_word_be(VGACommonState *vga, uint32_t addr) -+{ -+ uint32_t offset = addr & vga->vbe_size_mask & ~1; -+ uint16_t *ptr = (uint16_t *)(vga->vram_ptr + offset); -+ return lduw_be_p(ptr); -+} -+ -+static inline uint32_t vga_read_dword_le(VGACommonState *vga, uint32_t addr) -+{ -+ uint32_t offset = addr & vga->vbe_size_mask & ~3; -+ uint32_t *ptr = (uint32_t *)(vga->vram_ptr + offset); -+ return ldl_le_p(ptr); -+} -+ - /* - * 4 color mode - */ --static void vga_draw_line2(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line2(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - uint32_t plane_mask, *palette, data, v; - int x; - -- palette = s1->last_palette; -- plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; -+ palette = vga->last_palette; -+ plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { -- data = ((uint32_t *)s)[0]; -+ data = vga_read_dword_le(vga, addr); - data &= plane_mask; - v = expand2[GET_PLANE(data, 0)]; - v |= expand2[GET_PLANE(data, 2)] << 2; -@@ -124,7 +150,7 @@ static void vga_draw_line2(VGACommonState *s1, uint8_t *d, - ((uint32_t *)d)[6] = palette[(v >> 4) & 0xf]; - ((uint32_t *)d)[7] = palette[(v >> 0) & 0xf]; - d += 32; -- s += 4; -+ addr += 4; - } - } - -@@ -134,17 +160,17 @@ static void vga_draw_line2(VGACommonState *s1, uint8_t *d, - /* - * 4 color mode, dup2 horizontal - */ --static void vga_draw_line2d2(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line2d2(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - uint32_t plane_mask, *palette, data, v; - int x; - -- palette = s1->last_palette; -- plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; -+ palette = vga->last_palette; -+ plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { -- data = ((uint32_t *)s)[0]; -+ data = vga_read_dword_le(vga, addr); - data &= plane_mask; - v = expand2[GET_PLANE(data, 0)]; - v |= expand2[GET_PLANE(data, 2)] << 2; -@@ -160,24 +186,24 @@ static void vga_draw_line2d2(VGACommonState *s1, uint8_t *d, - PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); - PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); - d += 64; -- s += 4; -+ addr += 4; - } - } - - /* - * 16 color mode - */ --static void vga_draw_line4(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line4(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - uint32_t plane_mask, data, v, *palette; - int x; - -- palette = s1->last_palette; -- plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; -+ palette = vga->last_palette; -+ plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { -- data = ((uint32_t *)s)[0]; -+ data = vga_read_dword_le(vga, addr); - data &= plane_mask; - v = expand4[GET_PLANE(data, 0)]; - v |= expand4[GET_PLANE(data, 1)] << 1; -@@ -192,24 +218,24 @@ static void vga_draw_line4(VGACommonState *s1, uint8_t *d, - ((uint32_t *)d)[6] = palette[(v >> 4) & 0xf]; - ((uint32_t *)d)[7] = palette[(v >> 0) & 0xf]; - d += 32; -- s += 4; -+ addr += 4; - } - } - - /* - * 16 color mode, dup2 horizontal - */ --static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line4d2(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - uint32_t plane_mask, data, v, *palette; - int x; - -- palette = s1->last_palette; -- plane_mask = mask16[s1->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; -+ palette = vga->last_palette; -+ plane_mask = mask16[vga->ar[VGA_ATC_PLANE_ENABLE] & 0xf]; - width >>= 3; - for(x = 0; x < width; x++) { -- data = ((uint32_t *)s)[0]; -+ data = vga_read_dword_le(vga, addr); - data &= plane_mask; - v = expand4[GET_PLANE(data, 0)]; - v |= expand4[GET_PLANE(data, 1)] << 1; -@@ -224,7 +250,7 @@ static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d, - PUT_PIXEL2(d, 6, palette[(v >> 4) & 0xf]); - PUT_PIXEL2(d, 7, palette[(v >> 0) & 0xf]); - d += 64; -- s += 4; -+ addr += 4; - } - } - -@@ -233,21 +259,21 @@ static void vga_draw_line4d2(VGACommonState *s1, uint8_t *d, - * - * XXX: add plane_mask support (never used in standard VGA modes) - */ --static void vga_draw_line8d2(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line8d2(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - uint32_t *palette; - int x; - -- palette = s1->last_palette; -+ palette = vga->last_palette; - width >>= 3; - for(x = 0; x < width; x++) { -- PUT_PIXEL2(d, 0, palette[s[0]]); -- PUT_PIXEL2(d, 1, palette[s[1]]); -- PUT_PIXEL2(d, 2, palette[s[2]]); -- PUT_PIXEL2(d, 3, palette[s[3]]); -+ PUT_PIXEL2(d, 0, palette[vga_read_byte(vga, addr + 0)]); -+ PUT_PIXEL2(d, 1, palette[vga_read_byte(vga, addr + 1)]); -+ PUT_PIXEL2(d, 2, palette[vga_read_byte(vga, addr + 2)]); -+ PUT_PIXEL2(d, 3, palette[vga_read_byte(vga, addr + 3)]); - d += 32; -- s += 4; -+ addr += 4; - } - } - -@@ -256,63 +282,63 @@ static void vga_draw_line8d2(VGACommonState *s1, uint8_t *d, - * - * XXX: add plane_mask support (never used in standard VGA modes) - */ --static void vga_draw_line8(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line8(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - uint32_t *palette; - int x; - -- palette = s1->last_palette; -+ palette = vga->last_palette; - width >>= 3; - for(x = 0; x < width; x++) { -- ((uint32_t *)d)[0] = palette[s[0]]; -- ((uint32_t *)d)[1] = palette[s[1]]; -- ((uint32_t *)d)[2] = palette[s[2]]; -- ((uint32_t *)d)[3] = palette[s[3]]; -- ((uint32_t *)d)[4] = palette[s[4]]; -- ((uint32_t *)d)[5] = palette[s[5]]; -- ((uint32_t *)d)[6] = palette[s[6]]; -- ((uint32_t *)d)[7] = palette[s[7]]; -+ ((uint32_t *)d)[0] = palette[vga_read_byte(vga, addr + 0)]; -+ ((uint32_t *)d)[1] = palette[vga_read_byte(vga, addr + 1)]; -+ ((uint32_t *)d)[2] = palette[vga_read_byte(vga, addr + 2)]; -+ ((uint32_t *)d)[3] = palette[vga_read_byte(vga, addr + 3)]; -+ ((uint32_t *)d)[4] = palette[vga_read_byte(vga, addr + 4)]; -+ ((uint32_t *)d)[5] = palette[vga_read_byte(vga, addr + 5)]; -+ ((uint32_t *)d)[6] = palette[vga_read_byte(vga, addr + 6)]; -+ ((uint32_t *)d)[7] = palette[vga_read_byte(vga, addr + 7)]; - d += 32; -- s += 8; -+ addr += 8; - } - } - - /* - * 15 bit color - */ --static void vga_draw_line15_le(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line15_le(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - int w; - uint32_t v, r, g, b; - - w = width; - do { -- v = lduw_le_p((void *)s); -+ v = vga_read_word_le(vga, addr); - r = (v >> 7) & 0xf8; - g = (v >> 2) & 0xf8; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 2; -+ addr += 2; - d += 4; - } while (--w != 0); - } - --static void vga_draw_line15_be(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line15_be(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - int w; - uint32_t v, r, g, b; - - w = width; - do { -- v = lduw_be_p((void *)s); -+ v = vga_read_word_be(vga, addr); - r = (v >> 7) & 0xf8; - g = (v >> 2) & 0xf8; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 2; -+ addr += 2; - d += 4; - } while (--w != 0); - } -@@ -320,38 +346,38 @@ static void vga_draw_line15_be(VGACommonState *s1, uint8_t *d, - /* - * 16 bit color - */ --static void vga_draw_line16_le(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line16_le(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - int w; - uint32_t v, r, g, b; - - w = width; - do { -- v = lduw_le_p((void *)s); -+ v = vga_read_word_le(vga, addr); - r = (v >> 8) & 0xf8; - g = (v >> 3) & 0xfc; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 2; -+ addr += 2; - d += 4; - } while (--w != 0); - } - --static void vga_draw_line16_be(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line16_be(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - int w; - uint32_t v, r, g, b; - - w = width; - do { -- v = lduw_be_p((void *)s); -+ v = vga_read_word_be(vga, addr); - r = (v >> 8) & 0xf8; - g = (v >> 3) & 0xfc; - b = (v << 3) & 0xf8; - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 2; -+ addr += 2; - d += 4; - } while (--w != 0); - } -@@ -359,36 +385,36 @@ static void vga_draw_line16_be(VGACommonState *s1, uint8_t *d, - /* - * 24 bit color - */ --static void vga_draw_line24_le(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line24_le(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - int w; - uint32_t r, g, b; - - w = width; - do { -- b = s[0]; -- g = s[1]; -- r = s[2]; -+ b = vga_read_byte(vga, addr + 0); -+ g = vga_read_byte(vga, addr + 1); -+ r = vga_read_byte(vga, addr + 2); - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 3; -+ addr += 3; - d += 4; - } while (--w != 0); - } - --static void vga_draw_line24_be(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line24_be(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { - int w; - uint32_t r, g, b; - - w = width; - do { -- r = s[0]; -- g = s[1]; -- b = s[2]; -+ r = vga_read_byte(vga, addr + 0); -+ g = vga_read_byte(vga, addr + 1); -+ b = vga_read_byte(vga, addr + 2); - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 3; -+ addr += 3; - d += 4; - } while (--w != 0); - } -@@ -396,44 +422,36 @@ static void vga_draw_line24_be(VGACommonState *s1, uint8_t *d, - /* - * 32 bit color - */ --static void vga_draw_line32_le(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line32_le(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { --#ifndef HOST_WORDS_BIGENDIAN -- memcpy(d, s, width * 4); --#else - int w; - uint32_t r, g, b; - - w = width; - do { -- b = s[0]; -- g = s[1]; -- r = s[2]; -+ b = vga_read_byte(vga, addr + 0); -+ g = vga_read_byte(vga, addr + 1); -+ r = vga_read_byte(vga, addr + 2); - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 4; -+ addr += 4; - d += 4; - } while (--w != 0); --#endif - } - --static void vga_draw_line32_be(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width) -+static void vga_draw_line32_be(VGACommonState *vga, uint8_t *d, -+ uint32_t addr, int width) - { --#ifdef HOST_WORDS_BIGENDIAN -- memcpy(d, s, width * 4); --#else - int w; - uint32_t r, g, b; - - w = width; - do { -- r = s[1]; -- g = s[2]; -- b = s[3]; -+ r = vga_read_byte(vga, addr + 1); -+ g = vga_read_byte(vga, addr + 2); -+ b = vga_read_byte(vga, addr + 3); - ((uint32_t *)d)[0] = rgb_to_pixel32(r, g, b); -- s += 4; -+ addr += 4; - d += 4; - } while (--w != 0); --#endif - } -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 533d8d7895..13e4a5d55d 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1005,7 +1005,7 @@ void vga_mem_writeb(VGACommonState *s, hwaddr addr, uint32_t val) - } - - typedef void vga_draw_line_func(VGACommonState *s1, uint8_t *d, -- const uint8_t *s, int width); -+ uint32_t srcaddr, int width); - - #include "vga-helpers.h" - -@@ -1666,7 +1666,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - if (y_start < 0) - y_start = y; - if (!(is_buffer_shared(surface))) { -- vga_draw_line(s, d, s->vram_ptr + addr, width); -+ vga_draw_line(s, d, addr, width); - if (s->cursor_draw_line) - s->cursor_draw_line(s, d, y); - } -@@ -2170,6 +2170,7 @@ void vga_common_init(VGACommonState *s, Object *obj, bool global_vmstate) - if (!s->vbe_size) { - s->vbe_size = s->vram_size; - } -+ s->vbe_size_mask = s->vbe_size - 1; - - s->is_vbe_vmstate = 1; - memory_region_init_ram(&s->vram, obj, "vga.vram", s->vram_size, -diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h -index dd6c958da3..ad34a1f048 100644 ---- a/hw/display/vga_int.h -+++ b/hw/display/vga_int.h -@@ -94,6 +94,7 @@ typedef struct VGACommonState { - uint32_t vram_size; - uint32_t vram_size_mb; /* property */ - uint32_t vbe_size; -+ uint32_t vbe_size_mask; - uint32_t latch; - bool has_chain4_alias; - MemoryRegion chain4_alias; --- -2.11.0 - diff --git a/debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch b/debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch deleted file mode 100644 index 37d12af..0000000 --- a/debian/patches/extra/0013-multiboot-validate-multiboot-header-address-values.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Thu, 7 Sep 2017 12:02:56 +0530 -Subject: [PATCH] multiboot: validate multiboot header address values - -While loading kernel via multiboot-v1 image, (flags & 0x00010000) -indicates that multiboot header contains valid addresses to load -the kernel image. These addresses are used to compute kernel -size and kernel text offset in the OS image. Validate these -address values to avoid an OOB access issue. - -This is CVE-2017-14167. - -Reported-by: Thomas Garnier -Signed-off-by: Prasad J Pandit ---- - hw/i386/multiboot.c | 19 +++++++++++++++++++ - 1 file changed, 19 insertions(+) - -diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c -index f13e23139b..22688d376d 100644 ---- a/hw/i386/multiboot.c -+++ b/hw/i386/multiboot.c -@@ -221,15 +221,34 @@ int load_multiboot(FWCfgState *fw_cfg, - uint32_t mh_header_addr = ldl_p(header+i+12); - uint32_t mh_load_end_addr = ldl_p(header+i+20); - uint32_t mh_bss_end_addr = ldl_p(header+i+24); -+ - mh_load_addr = ldl_p(header+i+16); -+ if (mh_header_addr < mh_load_addr) { -+ fprintf(stderr, "invalid mh_load_addr address\n"); -+ exit(1); -+ } -+ - uint32_t mb_kernel_text_offset = i - (mh_header_addr - mh_load_addr); - uint32_t mb_load_size = 0; - mh_entry_addr = ldl_p(header+i+28); - - if (mh_load_end_addr) { -+ if (mh_bss_end_addr < mh_load_addr) { -+ fprintf(stderr, "invalid mh_bss_end_addr address\n"); -+ exit(1); -+ } - mb_kernel_size = mh_bss_end_addr - mh_load_addr; -+ -+ if (mh_load_end_addr < mh_load_addr) { -+ fprintf(stderr, "invalid mh_load_end_addr address\n"); -+ exit(1); -+ } - mb_load_size = mh_load_end_addr - mh_load_addr; - } else { -+ if (kernel_file_size < mb_kernel_text_offset) { -+ fprintf(stderr, "invalid kernel_file_size\n"); -+ exit(1); -+ } - mb_kernel_size = kernel_file_size - mb_kernel_text_offset; - mb_load_size = mb_kernel_size; - } --- -2.11.0 - diff --git a/debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch b/debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch deleted file mode 100644 index 526e67f..0000000 --- a/debian/patches/extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch +++ /dev/null @@ -1,57 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 20 Sep 2017 08:09:33 +0200 -Subject: [PATCH] virtio: fix descriptor counting in virtqueue_pop - -While changing the s/g list allocation, commit 3b3b0628 -also changed the descriptor counting to count iovec entries -as split by cpu_physical_memory_map(). Previously only the -actual descriptor entries were counted and the split into -the iovec happened afterwards in virtqueue_map(). -Count the entries again instead to avoid erroneous -"Looped descriptor" errors. - -Reported-by: Hans Middelhoek -Link: https://forum.proxmox.com/threads/vm-crash-with-memory-hotplug.35904/ -Fixes: 3b3b0628217e ("virtio: slim down allocation of VirtQueueElements") -Signed-off-by: Wolfgang Bumiller -Reviewed-by: Michael S. Tsirkin -Signed-off-by: Michael S. Tsirkin ---- - hw/virtio/virtio.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c -index 890b4d7eb7..33bb770177 100644 ---- a/hw/virtio/virtio.c -+++ b/hw/virtio/virtio.c -@@ -834,7 +834,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) - int64_t len; - VirtIODevice *vdev = vq->vdev; - VirtQueueElement *elem = NULL; -- unsigned out_num, in_num; -+ unsigned out_num, in_num, elem_entries; - hwaddr addr[VIRTQUEUE_MAX_SIZE]; - struct iovec iov[VIRTQUEUE_MAX_SIZE]; - VRingDesc desc; -@@ -852,7 +852,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) - smp_rmb(); - - /* When we start there are none of either input nor output. */ -- out_num = in_num = 0; -+ out_num = in_num = elem_entries = 0; - - max = vq->vring.num; - -@@ -922,7 +922,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) - } - - /* If we've got too many, that implies a descriptor loop. */ -- if ((in_num + out_num) > max) { -+ if (++elem_entries > max) { - virtio_error(vdev, "Looped descriptor"); - goto err_undo_map; - } --- -2.11.0 - diff --git a/debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch b/debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch deleted file mode 100644 index 4f966dc..0000000 --- a/debian/patches/extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 29 Nov 2017 09:39:55 +0100 -Subject: [PATCH] nbd/server: CVE-2017-15119 Reject options larger than 32M - -Backported-from: fdad35ef6c58 ---- - nbd/server.c | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/nbd/server.c b/nbd/server.c -index a98bb21a0a..4d6da8ac06 100644 ---- a/nbd/server.c -+++ b/nbd/server.c -@@ -489,6 +489,12 @@ static int nbd_negotiate_options(NBDClient *client) - } - length = be32_to_cpu(length); - -+ if (length > NBD_MAX_BUFFER_SIZE) { -+ LOG("len (%" PRIu32" ) is larger than max len (%u)", -+ length, NBD_MAX_BUFFER_SIZE); -+ return -EINVAL; -+ } -+ - TRACE("Checking option 0x%" PRIx32, clientflags); - if (client->tlscreds && - client->ioc == (QIOChannel *)client->sioc) { --- -2.11.0 - diff --git a/debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch b/debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch deleted file mode 100644 index 5941926..0000000 --- a/debian/patches/extra/0016-vga-migration-Update-memory-map-in-post_load.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: "Dr. David Alan Gilbert" -Date: Fri, 4 Aug 2017 12:33:29 +0100 -Subject: [PATCH] vga/migration: Update memory map in post_load - -After migration the chain4 alias mapping added by 80763888 (in 2011) -might be missing, since there's no call to vga_update_memory_access -in the post_load after the registers are updated. Add it back. - -Signed-off-by: Dr. David Alan Gilbert -Reviewed-by: Juan Quintela -Message-id: 20170804113329.13609-1-dgilbert@redhat.com -Signed-off-by: Gerd Hoffmann ---- - hw/display/vga.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 13e4a5d55d..a99d831e04 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -2050,6 +2050,7 @@ static int vga_common_post_load(void *opaque, int version_id) - /* force refresh */ - s->graphic_mode = -1; - vbe_update_vgaregs(s); -+ vga_update_memory_access(s); - return 0; - } - --- -2.11.0 - diff --git a/debian/patches/extra/0017-vga-drop-line_offset-variable.patch b/debian/patches/extra/0017-vga-drop-line_offset-variable.patch deleted file mode 100644 index d441d02..0000000 --- a/debian/patches/extra/0017-vga-drop-line_offset-variable.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Tue, 10 Oct 2017 16:13:21 +0200 -Subject: [PATCH] vga: drop line_offset variable - -Signed-off-by: Gerd Hoffmann ---- - hw/display/vga.c | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index a99d831e04..77af807a51 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1464,7 +1464,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - { - DisplaySurface *surface = qemu_console_surface(s->con); - int y1, y, update, linesize, y_start, double_scan, mask, depth; -- int width, height, shift_control, line_offset, bwidth, bits; -+ int width, height, shift_control, bwidth, bits; - ram_addr_t page0, page1; - DirtyBitmapSnapshot *snap = NULL; - int disp_width, multi_scan, multi_run; -@@ -1614,7 +1614,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - s->cursor_invalidate(s); - } - -- line_offset = s->line_offset; - #if 0 - printf("w=%d h=%d v=%d line_offset=%d cr[0x09]=0x%02x cr[0x17]=0x%02x linecmp=%d sr[0x01]=0x%02x\n", - width, height, v, line_offset, s->cr[9], s->cr[VGA_CRTC_MODE], -@@ -1629,7 +1628,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - - if (!full_update) { - ram_addr_t region_start = addr1; -- ram_addr_t region_end = addr1 + line_offset * height; -+ ram_addr_t region_end = addr1 + s->line_offset * height; - vga_sync_dirty_bitmap(s); - if (s->line_compare < height) { - /* split screen mode */ -@@ -1681,7 +1680,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - if (!multi_run) { - mask = (s->cr[VGA_CRTC_MODE] & 3) ^ 3; - if ((y1 & mask) == mask) -- addr1 += line_offset; -+ addr1 += s->line_offset; - y1++; - multi_run = multi_scan; - } else { --- -2.11.0 - diff --git a/debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch b/debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch deleted file mode 100644 index 9fe31bb..0000000 --- a/debian/patches/extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch +++ /dev/null @@ -1,103 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Tue, 10 Oct 2017 16:13:22 +0200 -Subject: [PATCH] vga: handle cirrus vbe mode wraparounds. - -Commit "3d90c62548 vga: stop passing pointers to vga_draw_line* -functions" is incomplete. It doesn't handle the case that the vga -rendering code tries to create a shared surface, i.e. a pixman image -backed by vga video memory. That can not work in case the guest display -wraps from end of video memory to the start. So force shadowing in that -case. Also adjust the snapshot region calculation. - -Can trigger with cirrus only, when programming vbe modes using the bochs -api (stdvga, also qxl and virtio-vga in vga compat mode) wrap arounds -can't happen. - -Fixes: CVE-2017-13672 -Fixes: 3d90c6254863693a6b13d918d2b8682e08bbc681 -Cc: P J P -Reported-by: David Buchanan -Signed-off-by: Gerd Hoffmann -Message-id: 20171010141323.14049-3-kraxel@redhat.com ---- - hw/display/vga.c | 28 +++++++++++++++++++++------- - 1 file changed, 21 insertions(+), 7 deletions(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 77af807a51..7bdbf7441e 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1465,13 +1465,13 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - DisplaySurface *surface = qemu_console_surface(s->con); - int y1, y, update, linesize, y_start, double_scan, mask, depth; - int width, height, shift_control, bwidth, bits; -- ram_addr_t page0, page1; -+ ram_addr_t page0, page1, region_start, region_end; - DirtyBitmapSnapshot *snap = NULL; - int disp_width, multi_scan, multi_run; - uint8_t *d; - uint32_t v, addr1, addr; - vga_draw_line_func *vga_draw_line = NULL; -- bool share_surface; -+ bool share_surface, force_shadow = false; - pixman_format_code_t format; - #ifdef HOST_WORDS_BIGENDIAN - bool byteswap = !s->big_endian_fb; -@@ -1484,6 +1484,15 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - s->get_resolution(s, &width, &height); - disp_width = width; - -+ region_start = (s->start_addr * 4); -+ region_end = region_start + s->line_offset * height; -+ if (region_end > s->vbe_size) { -+ /* wraps around (can happen with cirrus vbe modes) */ -+ region_start = 0; -+ region_end = s->vbe_size; -+ force_shadow = true; -+ } -+ - shift_control = (s->gr[VGA_GFX_MODE] >> 5) & 3; - double_scan = (s->cr[VGA_CRTC_MAX_SCAN] >> 7); - if (shift_control != 1) { -@@ -1523,7 +1532,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - format = qemu_default_pixman_format(depth, !byteswap); - if (format) { - share_surface = dpy_gfx_check_format(s->con, format) -- && !s->force_shadow; -+ && !s->force_shadow && !force_shadow; - } else { - share_surface = false; - } -@@ -1627,8 +1636,6 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - y1 = 0; - - if (!full_update) { -- ram_addr_t region_start = addr1; -- ram_addr_t region_end = addr1 + s->line_offset * height; - vga_sync_dirty_bitmap(s); - if (s->line_compare < height) { - /* split screen mode */ -@@ -1651,10 +1658,17 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - addr = (addr & ~0x8000) | ((y1 & 2) << 14); - } - update = full_update; -- page0 = addr; -- page1 = addr + bwidth - 1; -+ page0 = addr & s->vbe_size_mask; -+ page1 = (addr + bwidth - 1) & s->vbe_size_mask; - if (full_update) { - update = 1; -+ } else if (page1 < page0) { -+ /* scanline wraps from end of video memory to the start */ -+ assert(force_shadow); -+ update = memory_region_snapshot_get_dirty(&s->vram, snap, -+ page0, 0); -+ update |= memory_region_snapshot_get_dirty(&s->vram, snap, -+ page1, 0); - } else { - update = memory_region_snapshot_get_dirty(&s->vram, snap, - page0, page1 - page0); --- -2.11.0 - diff --git a/debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch b/debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch deleted file mode 100644 index bc89a7e..0000000 --- a/debian/patches/extra/0019-vga-add-ram_addr_t-cast.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Tue, 10 Oct 2017 16:13:23 +0200 -Subject: [PATCH] vga: add ram_addr_t cast - -Reported by Coverity. - -Fixes: CID 1381409 -Signed-off-by: Gerd Hoffmann -Message-id: 20171010141323.14049-4-kraxel@redhat.com ---- - hw/display/vga.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 7bdbf7441e..63ba404ef2 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1485,7 +1485,7 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - disp_width = width; - - region_start = (s->start_addr * 4); -- region_end = region_start + s->line_offset * height; -+ region_end = region_start + (ram_addr_t)s->line_offset * height; - if (region_end > s->vbe_size) { - /* wraps around (can happen with cirrus vbe modes) */ - region_start = 0; --- -2.11.0 - diff --git a/debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch b/debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch deleted file mode 100644 index 371403d..0000000 --- a/debian/patches/extra/0020-vga-fix-region-checks-in-wraparound-case.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Mon, 30 Oct 2017 11:28:30 +0100 -Subject: [PATCH] vga: fix region checks in wraparound case - -Cc: "Dr. David Alan Gilbert" -Signed-off-by: Gerd Hoffmann -Reviewed-by: Dr. David Alan Gilbert -Message-id: 20171030102830.4469-1-kraxel@redhat.com ---- - hw/display/vga.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/hw/display/vga.c b/hw/display/vga.c -index 63ba404ef2..a58d8bcd67 100644 ---- a/hw/display/vga.c -+++ b/hw/display/vga.c -@@ -1666,9 +1666,9 @@ static void vga_draw_graphic(VGACommonState *s, int full_update) - /* scanline wraps from end of video memory to the start */ - assert(force_shadow); - update = memory_region_snapshot_get_dirty(&s->vram, snap, -- page0, 0); -+ page0, s->vbe_size - page0); - update |= memory_region_snapshot_get_dirty(&s->vram, snap, -- page1, 0); -+ 0, page1); - } else { - update = memory_region_snapshot_get_dirty(&s->vram, snap, - page0, page1 - page0); --- -2.11.0 - diff --git a/debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch b/debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch deleted file mode 100644 index 3a9e498..0000000 --- a/debian/patches/extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: "Daniel P. Berrange" -Date: Mon, 9 Oct 2017 14:43:42 +0100 -Subject: [PATCH] io: monitor encoutput buffer size from websocket GSource - -The websocket GSource is monitoring the size of the rawoutput -buffer to determine if the channel can accepts more writes. -The rawoutput buffer, however, is merely a temporary staging -buffer before data is copied into the encoutput buffer. Thus -its size will always be zero when the GSource runs. - -This flaw causes the encoutput buffer to grow without bound -if the other end of the underlying data channel doesn't -read data being sent. This can be seen with VNC if a client -is on a slow WAN link and the guest OS is sending many screen -updates. A malicious VNC client can act like it is on a slow -link by playing a video in the guest and then reading data -very slowly, causing QEMU host memory to expand arbitrarily. - -This issue is assigned CVE-2017-15268, publically reported in - - https://bugs.launchpad.net/qemu/+bug/1718964 - -Reviewed-by: Eric Blake -Signed-off-by: Daniel P. Berrange ---- - io/channel-websock.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/io/channel-websock.c b/io/channel-websock.c -index 8fabadea2f..882bbb4cbc 100644 ---- a/io/channel-websock.c -+++ b/io/channel-websock.c -@@ -26,7 +26,7 @@ - #include "trace.h" - - --/* Max amount to allow in rawinput/rawoutput buffers */ -+/* Max amount to allow in rawinput/encoutput buffers */ - #define QIO_CHANNEL_WEBSOCK_MAX_BUFFER 8192 - - #define QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN 24 -@@ -1006,7 +1006,7 @@ qio_channel_websock_source_prepare(GSource *source, - if (wsource->wioc->rawinput.offset) { - cond |= G_IO_IN; - } -- if (wsource->wioc->rawoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { -+ if (wsource->wioc->encoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { - cond |= G_IO_OUT; - } - --- -2.11.0 - diff --git a/debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch b/debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch deleted file mode 100644 index 649a77a..0000000 --- a/debian/patches/extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Mon, 16 Oct 2017 14:21:59 +0200 -Subject: [PATCH] 9pfs: use g_malloc0 to allocate space for xattr - -9p back-end first queries the size of an extended attribute, -allocates space for it via g_malloc() and then retrieves its -value into allocated buffer. Race between querying attribute -size and retrieving its could lead to memory bytes disclosure. -Use g_malloc0() to avoid it. - -Reported-by: Tuomas Tynkkynen -Signed-off-by: Prasad J Pandit -Signed-off-by: Greg Kurz ---- - hw/9pfs/9p.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c -index c80ba67389..aaf9935ef4 100644 ---- a/hw/9pfs/9p.c -+++ b/hw/9pfs/9p.c -@@ -3220,7 +3220,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.xattrwalk_fid = true; - if (size) { -- xattr_fidp->fs.xattr.value = g_malloc(size); -+ xattr_fidp->fs.xattr.value = g_malloc0(size); - err = v9fs_co_llistxattr(pdu, &xattr_fidp->path, - xattr_fidp->fs.xattr.value, - xattr_fidp->fs.xattr.len); -@@ -3253,7 +3253,7 @@ static void coroutine_fn v9fs_xattrwalk(void *opaque) - xattr_fidp->fid_type = P9_FID_XATTR; - xattr_fidp->fs.xattr.xattrwalk_fid = true; - if (size) { -- xattr_fidp->fs.xattr.value = g_malloc(size); -+ xattr_fidp->fs.xattr.value = g_malloc0(size); - err = v9fs_co_lgetxattr(pdu, &xattr_fidp->path, - &name, xattr_fidp->fs.xattr.value, - xattr_fidp->fs.xattr.len); --- -2.11.0 - diff --git a/debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch b/debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch deleted file mode 100644 index 789998c..0000000 --- a/debian/patches/extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Gerd Hoffmann -Date: Wed, 11 Oct 2017 10:43:14 +0200 -Subject: [PATCH] cirrus: fix oob access in mode4and5 write functions - -Move dst calculation into the loop, so we apply the mask on each -interation and will not overflow vga memory. - -Cc: Prasad J Pandit -Reported-by: Niu Guoxiang -Signed-off-by: Gerd Hoffmann -Message-id: 20171011084314.21752-1-kraxel@redhat.com ---- - hw/display/cirrus_vga.c | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c -index afc290ab91..077a8cb74f 100644 ---- a/hw/display/cirrus_vga.c -+++ b/hw/display/cirrus_vga.c -@@ -2038,15 +2038,14 @@ static void cirrus_mem_writeb_mode4and5_8bpp(CirrusVGAState * s, - unsigned val = mem_value; - uint8_t *dst; - -- dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask); - for (x = 0; x < 8; x++) { -+ dst = s->vga.vram_ptr + ((offset + x) & s->cirrus_addr_mask); - if (val & 0x80) { - *dst = s->cirrus_shadow_gr1; - } else if (mode == 5) { - *dst = s->cirrus_shadow_gr0; - } - val <<= 1; -- dst++; - } - memory_region_set_dirty(&s->vga.vram, offset, 8); - } -@@ -2060,8 +2059,8 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, - unsigned val = mem_value; - uint8_t *dst; - -- dst = s->vga.vram_ptr + (offset &= s->cirrus_addr_mask); - for (x = 0; x < 8; x++) { -+ dst = s->vga.vram_ptr + ((offset + 2 * x) & s->cirrus_addr_mask & ~1); - if (val & 0x80) { - *dst = s->cirrus_shadow_gr1; - *(dst + 1) = s->vga.gr[0x11]; -@@ -2070,7 +2069,6 @@ static void cirrus_mem_writeb_mode4and5_16bpp(CirrusVGAState * s, - *(dst + 1) = s->vga.gr[0x10]; - } - val <<= 1; -- dst += 2; - } - memory_region_set_dirty(&s->vga.vram, offset, 16); - } --- -2.11.0 - diff --git a/debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch b/debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch deleted file mode 100644 index 84c046a..0000000 --- a/debian/patches/extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Prasad J Pandit -Date: Wed, 29 Nov 2017 23:14:27 +0530 -Subject: [PATCH] virtio: check VirtQueue Vring object is set - -A guest could attempt to use an uninitialised VirtQueue object -or unset Vring.align leading to a arithmetic exception. Add check -to avoid it. - -Reported-by: Zhangboxian -Signed-off-by: Prasad J Pandit -Reviewed-by: Michael S. Tsirkin -Signed-off-by: Michael S. Tsirkin -Reviewed-by: Stefan Hajnoczi -Reviewed-by: Cornelia Huck ---- - hw/virtio/virtio.c | 14 +++++++++++--- - 1 file changed, 11 insertions(+), 3 deletions(-) - -diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c -index 33bb770177..76b9a9907c 100644 ---- a/hw/virtio/virtio.c -+++ b/hw/virtio/virtio.c -@@ -183,7 +183,7 @@ void virtio_queue_update_rings(VirtIODevice *vdev, int n) - { - VRing *vring = &vdev->vq[n].vring; - -- if (!vring->desc) { -+ if (!vring->num || !vring->desc || !vring->align) { - /* not yet setup -> nothing to do */ - return; - } -@@ -1416,6 +1416,9 @@ void virtio_config_modern_writel(VirtIODevice *vdev, - - void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr) - { -+ if (!vdev->vq[n].vring.num) { -+ return; -+ } - vdev->vq[n].vring.desc = addr; - virtio_queue_update_rings(vdev, n); - } -@@ -1428,6 +1431,9 @@ hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n) - void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, - hwaddr avail, hwaddr used) - { -+ if (!vdev->vq[n].vring.num) { -+ return; -+ } - vdev->vq[n].vring.desc = desc; - vdev->vq[n].vring.avail = avail; - vdev->vq[n].vring.used = used; -@@ -1496,8 +1502,10 @@ void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) - */ - assert(k->has_variable_vring_alignment); - -- vdev->vq[n].vring.align = align; -- virtio_queue_update_rings(vdev, n); -+ if (align) { -+ vdev->vq[n].vring.align = align; -+ virtio_queue_update_rings(vdev, n); -+ } - } - - static bool virtio_queue_notify_aio_vq(VirtQueue *vq) --- -2.11.0 - diff --git a/debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch b/debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch deleted file mode 100644 index e42c1fd..0000000 --- a/debian/patches/extra/0025-block-gluster-glfs_lseek-workaround.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Jeff Cody -Date: Tue, 23 May 2017 13:27:50 -0400 -Subject: [PATCH] block/gluster: glfs_lseek() workaround - -On current released versions of glusterfs, glfs_lseek() will sometimes -return invalid values for SEEK_DATA or SEEK_HOLE. For SEEK_DATA and -SEEK_HOLE, the returned value should be >= the passed offset, or < 0 in -the case of error: - -LSEEK(2): - - off_t lseek(int fd, off_t offset, int whence); - - [...] - - SEEK_HOLE - Adjust the file offset to the next hole in the file greater - than or equal to offset. If offset points into the middle of - a hole, then the file offset is set to offset. If there is no - hole past offset, then the file offset is adjusted to the end - of the file (i.e., there is an implicit hole at the end of - any file). - - [...] - - RETURN VALUE - Upon successful completion, lseek() returns the resulting - offset location as measured in bytes from the beginning of the - file. On error, the value (off_t) -1 is returned and errno is - set to indicate the error - -However, occasionally glfs_lseek() for SEEK_HOLE/DATA will return a -value less than the passed offset, yet greater than zero. - -For instance, here are example values observed from this call: - - offs = glfs_lseek(s->fd, start, SEEK_HOLE); - if (offs < 0) { - return -errno; /* D1 and (H3 or H4) */ - } - -start == 7608336384 -offs == 7607877632 - -This causes QEMU to abort on the assert test. When this value is -returned, errno is also 0. - -This is a reported and known bug to glusterfs: -https://bugzilla.redhat.com/show_bug.cgi?id=1425293 - -Although this is being fixed in gluster, we still should work around it -in QEMU, given that multiple released versions of gluster behave this -way. - -This patch treats the return case of (offs < start) the same as if an -error value other than ENXIO is returned; we will assume we learned -nothing, and there are no holes in the file. - -Signed-off-by: Jeff Cody -Reviewed-by: Eric Blake -Reviewed-by: Niels de Vos -Message-id: 87c0140e9407c08f6e74b04131b610f2e27c014c.1495560397.git.jcody@redhat.com -Signed-off-by: Jeff Cody ---- - block/gluster.c | 18 ++++++++++++++++-- - 1 file changed, 16 insertions(+), 2 deletions(-) - -diff --git a/block/gluster.c b/block/gluster.c -index 4fdf68f1fc..06421ef79d 100644 ---- a/block/gluster.c -+++ b/block/gluster.c -@@ -1287,7 +1287,14 @@ static int find_allocation(BlockDriverState *bs, off_t start, - if (offs < 0) { - return -errno; /* D3 or D4 */ - } -- assert(offs >= start); -+ -+ if (offs < start) { -+ /* This is not a valid return by lseek(). We are safe to just return -+ * -EIO in this case, and we'll treat it like D4. Unfortunately some -+ * versions of gluster server will return offs < start, so an assert -+ * here will unnecessarily abort QEMU. */ -+ return -EIO; -+ } - - if (offs > start) { - /* D2: in hole, next data at offs */ -@@ -1319,7 +1326,14 @@ static int find_allocation(BlockDriverState *bs, off_t start, - if (offs < 0) { - return -errno; /* D1 and (H3 or H4) */ - } -- assert(offs >= start); -+ -+ if (offs < start) { -+ /* This is not a valid return by lseek(). We are safe to just return -+ * -EIO in this case, and we'll treat it like H4. Unfortunately some -+ * versions of gluster server will return offs < start, so an assert -+ * here will unnecessarily abort QEMU. */ -+ return -EIO; -+ } - - if (offs > start) { - /* --- -2.11.0 - diff --git a/debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch b/debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch deleted file mode 100644 index f794745..0000000 --- a/debian/patches/extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch +++ /dev/null @@ -1,180 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Niels de Vos -Date: Sun, 28 May 2017 12:01:14 +0530 -Subject: [PATCH] gluster: add support for PREALLOC_MODE_FALLOC - -Add missing support for "preallocation=falloc" to the Gluster block -driver. This change bases its logic on that of block/file-posix.c and -removed the gluster_supports_zerofill() and qemu_gluster_zerofill() -functions in favour of #ifdef checks in an easy to read -switch-statement. - -Both glfs_zerofill() and glfs_fallocate() have been introduced with -GlusterFS 3.5.0 (pkg-config glusterfs-api = 6). A #define for the -availability of glfs_fallocate() has been added to ./configure. - -Reported-by: Satheesaran Sundaramoorthi -Signed-off-by: Niels de Vos -Message-id: 20170528063114.28691-1-ndevos@redhat.com -URL: https://bugzilla.redhat.com/1450759 -Signed-off-by: Niels de Vos -Signed-off-by: Jeff Cody ---- - block/gluster.c | 76 ++++++++++++++++++++++++++++++--------------------------- - configure | 6 +++++ - 2 files changed, 46 insertions(+), 36 deletions(-) - -diff --git a/block/gluster.c b/block/gluster.c -index 06421ef79d..8108c89c7f 100644 ---- a/block/gluster.c -+++ b/block/gluster.c -@@ -975,29 +975,6 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, - qemu_coroutine_yield(); - return acb.ret; - } -- --static inline bool gluster_supports_zerofill(void) --{ -- return 1; --} -- --static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset, -- int64_t size) --{ -- return glfs_zerofill(fd, offset, size); --} -- --#else --static inline bool gluster_supports_zerofill(void) --{ -- return 0; --} -- --static inline int qemu_gluster_zerofill(struct glfs_fd *fd, int64_t offset, -- int64_t size) --{ -- return 0; --} - #endif - - static int qemu_gluster_create(const char *filename, -@@ -1007,9 +984,10 @@ static int qemu_gluster_create(const char *filename, - struct glfs *glfs; - struct glfs_fd *fd; - int ret = 0; -- int prealloc = 0; -+ PreallocMode prealloc; - int64_t total_size = 0; - char *tmp = NULL; -+ Error *local_err = NULL; - - gconf = g_new0(BlockdevOptionsGluster, 1); - gconf->debug = qemu_opt_get_number_del(opts, GLUSTER_OPT_DEBUG, -@@ -1037,13 +1015,12 @@ static int qemu_gluster_create(const char *filename, - BDRV_SECTOR_SIZE); - - tmp = qemu_opt_get_del(opts, BLOCK_OPT_PREALLOC); -- if (!tmp || !strcmp(tmp, "off")) { -- prealloc = 0; -- } else if (!strcmp(tmp, "full") && gluster_supports_zerofill()) { -- prealloc = 1; -- } else { -- error_setg(errp, "Invalid preallocation mode: '%s'" -- " or GlusterFS doesn't support zerofill API", tmp); -+ prealloc = qapi_enum_parse(PreallocMode_lookup, tmp, -+ PREALLOC_MODE__MAX, PREALLOC_MODE_OFF, -+ &local_err); -+ g_free(tmp); -+ if (local_err) { -+ error_propagate(errp, local_err); - ret = -EINVAL; - goto out; - } -@@ -1052,21 +1029,48 @@ static int qemu_gluster_create(const char *filename, - O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR); - if (!fd) { - ret = -errno; -- } else { -+ goto out; -+ } -+ -+ switch (prealloc) { -+#ifdef CONFIG_GLUSTERFS_FALLOCATE -+ case PREALLOC_MODE_FALLOC: -+ if (glfs_fallocate(fd, 0, 0, total_size)) { -+ error_setg(errp, "Could not preallocate data for the new file"); -+ ret = -errno; -+ } -+ break; -+#endif /* CONFIG_GLUSTERFS_FALLOCATE */ -+#ifdef CONFIG_GLUSTERFS_ZEROFILL -+ case PREALLOC_MODE_FULL: - if (!glfs_ftruncate(fd, total_size)) { -- if (prealloc && qemu_gluster_zerofill(fd, 0, total_size)) { -+ if (glfs_zerofill(fd, 0, total_size)) { -+ error_setg(errp, "Could not zerofill the new file"); - ret = -errno; - } - } else { -+ error_setg(errp, "Could not resize file"); - ret = -errno; - } -- -- if (glfs_close(fd) != 0) { -+ break; -+#endif /* CONFIG_GLUSTERFS_ZEROFILL */ -+ case PREALLOC_MODE_OFF: -+ if (glfs_ftruncate(fd, total_size) != 0) { - ret = -errno; -+ error_setg(errp, "Could not resize file"); - } -+ break; -+ default: -+ ret = -EINVAL; -+ error_setg(errp, "Unsupported preallocation mode: %s", -+ PreallocMode_lookup[prealloc]); -+ break; -+ } -+ -+ if (glfs_close(fd) != 0) { -+ ret = -errno; - } - out: -- g_free(tmp); - qapi_free_BlockdevOptionsGluster(gconf); - glfs_clear_preopened(glfs); - return ret; -diff --git a/configure b/configure -index 841f7a8fae..3667da6f07 100755 ---- a/configure -+++ b/configure -@@ -300,6 +300,7 @@ seccomp="" - glusterfs="" - glusterfs_xlator_opt="no" - glusterfs_discard="no" -+glusterfs_fallocate="no" - glusterfs_zerofill="no" - gtk="" - gtkabi="" -@@ -3537,6 +3538,7 @@ if test "$glusterfs" != "no" ; then - glusterfs_discard="yes" - fi - if $pkg_config --atleast-version=6 glusterfs-api; then -+ glusterfs_fallocate="yes" - glusterfs_zerofill="yes" - fi - else -@@ -5717,6 +5719,10 @@ if test "$glusterfs_discard" = "yes" ; then - echo "CONFIG_GLUSTERFS_DISCARD=y" >> $config_host_mak - fi - -+if test "$glusterfs_fallocate" = "yes" ; then -+ echo "CONFIG_GLUSTERFS_FALLOCATE=y" >> $config_host_mak -+fi -+ - if test "$glusterfs_zerofill" = "yes" ; then - echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak - fi --- -2.11.0 - diff --git a/debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch b/debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch deleted file mode 100644 index 3cc2f0a..0000000 --- a/debian/patches/extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Wed, 12 Jul 2017 13:20:56 -0300 -Subject: [PATCH] target/i386: Use host_vendor_fms() in max_x86_cpu_initfn() - -The existing code duplicated the logic in host_vendor_fms(), so -reuse the helper function instead. - -Signed-off-by: Eduardo Habkost -Message-Id: <20170712162058.10538-3-ehabkost@redhat.com> -Reviewed-by: Igor Mammedov -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 9 ++------- - 1 file changed, 2 insertions(+), 7 deletions(-) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 4b3bfb3802..1affd3bb5b 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -1592,13 +1592,8 @@ static void max_x86_cpu_initfn(Object *obj) - X86CPUDefinition host_cpudef = { }; - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; - -- host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); -- x86_cpu_vendor_words2str(host_cpudef.vendor, ebx, edx, ecx); -- -- host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); -- host_cpudef.family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); -- host_cpudef.model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); -- host_cpudef.stepping = eax & 0x0F; -+ host_vendor_fms(host_cpudef.vendor, &host_cpudef.family, -+ &host_cpudef.model, &host_cpudef.stepping); - - cpu_x86_fill_model_id(host_cpudef.model_id); - --- -2.11.0 - diff --git a/debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch b/debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch deleted file mode 100644 index f46cb61..0000000 --- a/debian/patches/extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Wed, 12 Jul 2017 13:20:57 -0300 -Subject: [PATCH] target/i386: Define CPUID_MODEL_ID_SZ macro - -Document cpu_x86_fill_model_id() and define CPUID_MODEL_ID_SZ to -help callers use the right buffer size. - -Signed-off-by: Eduardo Habkost -Message-Id: <20170712162058.10538-4-ehabkost@redhat.com> -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 1affd3bb5b..54832dd591 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -1541,6 +1541,17 @@ static bool lmce_supported(void) - return !!(mce_cap & MCG_LMCE_P); - } - -+#define CPUID_MODEL_ID_SZ 48 -+ -+/** -+ * cpu_x86_fill_model_id: -+ * Get CPUID model ID string from host CPU. -+ * -+ * @str should have at least CPUID_MODEL_ID_SZ bytes -+ * -+ * The function does NOT add a null terminator to the string -+ * automatically. -+ */ - static int cpu_x86_fill_model_id(char *str) - { - uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; --- -2.11.0 - diff --git a/debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch b/debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch deleted file mode 100644 index d8f9bfa..0000000 --- a/debian/patches/extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Wed, 12 Jul 2017 13:20:58 -0300 -Subject: [PATCH] target/i386: Don't use x86_cpu_load_def() on "max" CPU model - -When commit 0bacd8b3046f ('i386: Don't set CPUClass::cpu_def on -"max" model') removed the CPUClass::cpu_def field, we kept using -the x86_cpu_load_def() helper directly in max_x86_cpu_initfn(), -emulating the previous behavior when CPUClass::cpu_def was set. - -However, x86_cpu_load_def() is intended to help initialization of -CPU models from the builtin_x86_defs table, and does lots of -other steps that are not necessary for "max". - -One of the things x86_cpu_load_def() do is to set the properties -listed at tcg_default_props/kvm_default_props. We must not do -that on the "max" CPU model, otherwise under KVM we will -incorrectly report all KVM features as always available, and the -"svm" feature as always unavailable. The latter caused the bug -reported at: - - https://bugzilla.redhat.com/show_bug.cgi?id=1467599 - ("Unable to start domain: the CPU is incompatible with host CPU: - Host CPU does not provide required features: svm") - -Replace x86_cpu_load_def() with simple object_property_set*() -calls. In addition to fixing the above bug, this makes the KVM -branch in max_x86_cpu_initfn() very similar to the existing TCG -branch. - -For reference, the full list of steps performed by -x86_cpu_load_def() is: - -* Setting min-level and min-xlevel. Already done by - max_x86_cpu_initfn(). -* Setting family/model/stepping/model-id. Done by the code added - to max_x86_cpu_initfn() in this patch. -* Copying def->features. Wrong because "-cpu max" features need to - be calculated at realize time. This was not a problem in the - current code because host_cpudef.features was all zeroes. -* x86_cpu_apply_props() calls. This causes the bug above, and - shouldn't be done. -* Setting CPUID_EXT_HYPERVISOR. Not needed because it is already - reported by x86_cpu_get_supported_feature_word(), and because - "-cpu max" features need to be calculated at realize time. -* Setting CPU vendor to host CPU vendor if on KVM mode. - Redundant, because max_x86_cpu_initfn() already sets it to the - host CPU vendor. - -Signed-off-by: Eduardo Habkost -Message-Id: <20170712162058.10538-5-ehabkost@redhat.com> -Reviewed-by: Igor Mammedov -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 18 ++++++++++++------ - 1 file changed, 12 insertions(+), 6 deletions(-) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 54832dd591..3d53cb4c86 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -1600,15 +1600,21 @@ static void max_x86_cpu_initfn(Object *obj) - cpu->max_features = true; - - if (kvm_enabled()) { -- X86CPUDefinition host_cpudef = { }; -- uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; -+ char vendor[CPUID_VENDOR_SZ + 1] = { 0 }; -+ char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 }; -+ int family, model, stepping; - -- host_vendor_fms(host_cpudef.vendor, &host_cpudef.family, -- &host_cpudef.model, &host_cpudef.stepping); -+ host_vendor_fms(vendor, &family, &model, &stepping); - -- cpu_x86_fill_model_id(host_cpudef.model_id); -+ cpu_x86_fill_model_id(model_id); - -- x86_cpu_load_def(cpu, &host_cpudef, &error_abort); -+ object_property_set_str(OBJECT(cpu), vendor, "vendor", &error_abort); -+ object_property_set_int(OBJECT(cpu), family, "family", &error_abort); -+ object_property_set_int(OBJECT(cpu), model, "model", &error_abort); -+ object_property_set_int(OBJECT(cpu), stepping, "stepping", -+ &error_abort); -+ object_property_set_str(OBJECT(cpu), model_id, "model-id", -+ &error_abort); - - env->cpuid_min_level = - kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); --- -2.11.0 - diff --git a/debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch b/debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch deleted file mode 100644 index 0db5d5d..0000000 --- a/debian/patches/extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch +++ /dev/null @@ -1,85 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Tue, 9 Jan 2018 13:45:13 -0200 -Subject: [PATCH] i386: Change X86CPUDefinition::model_id to const char* - -It is valid to have a 48-character model ID on CPUID, however the -definition of X86CPUDefinition::model_id is char[48], which can -make the compiler drop the null terminator from the string. - -If a CPU model happens to have 48 bytes on model_id, "-cpu help" -will print garbage and the object_property_set_str() call at -x86_cpu_load_def() will read data outside the model_id array. - -We could increase the array size to 49, but this would mean the -compiler would not issue a warning if a 49-char string is used by -mistake for model_id. - -To make things simpler, simply change model_id to be const char*, -and validate the string length using an assert() on -x86_cpu_cpudef_class_init. - -Reported-by: "Dr. David Alan Gilbert" -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 9 ++++++++- - 1 file changed, 8 insertions(+), 1 deletion(-) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index 3d53cb4c86..c673521016 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -753,7 +753,7 @@ struct X86CPUDefinition { - int model; - int stepping; - FeatureWordArray features; -- char model_id[48]; -+ const char *model_id; - }; - - static X86CPUDefinition builtin_x86_defs[] = { -@@ -922,6 +922,7 @@ static X86CPUDefinition builtin_x86_defs[] = { - .features[FEAT_1_EDX] = - I486_FEATURES, - .xlevel = 0, -+ .model_id = "", - }, - { - .name = "pentium", -@@ -933,6 +934,7 @@ static X86CPUDefinition builtin_x86_defs[] = { - .features[FEAT_1_EDX] = - PENTIUM_FEATURES, - .xlevel = 0, -+ .model_id = "", - }, - { - .name = "pentium2", -@@ -944,6 +946,7 @@ static X86CPUDefinition builtin_x86_defs[] = { - .features[FEAT_1_EDX] = - PENTIUM2_FEATURES, - .xlevel = 0, -+ .model_id = "", - }, - { - .name = "pentium3", -@@ -955,6 +958,7 @@ static X86CPUDefinition builtin_x86_defs[] = { - .features[FEAT_1_EDX] = - PENTIUM3_FEATURES, - .xlevel = 0, -+ .model_id = "", - }, - { - .name = "athlon", -@@ -2617,6 +2621,9 @@ static void x86_register_cpudef_type(X86CPUDefinition *def) - * they shouldn't be set on the CPU model table. - */ - assert(!(def->features[FEAT_8000_0001_EDX] & CPUID_EXT2_AMD_ALIASES)); -+ /* catch mistakes instead of silently truncating model_id when too long */ -+ assert(def->model_id && strlen(def->model_id) <= 48); -+ - - type_register(&ti); - g_free(typename); --- -2.11.0 - diff --git a/debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch b/debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch deleted file mode 100644 index 2a6f6d6..0000000 --- a/debian/patches/extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch +++ /dev/null @@ -1,135 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Tue, 9 Jan 2018 13:45:14 -0200 -Subject: [PATCH] i386: Add support for SPEC_CTRL MSR - -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.h | 3 +++ - target/i386/kvm.c | 15 +++++++++++++++ - target/i386/machine.c | 20 ++++++++++++++++++++ - 3 files changed, 38 insertions(+) - -diff --git a/target/i386/cpu.h b/target/i386/cpu.h -index c4602ca80d..cc322d6b39 100644 ---- a/target/i386/cpu.h -+++ b/target/i386/cpu.h -@@ -333,6 +333,7 @@ - #define MSR_IA32_APICBASE_BASE (0xfffffU<<12) - #define MSR_IA32_FEATURE_CONTROL 0x0000003a - #define MSR_TSC_ADJUST 0x0000003b -+#define MSR_IA32_SPEC_CTRL 0x48 - #define MSR_IA32_TSCDEADLINE 0x6e0 - - #define FEATURE_CONTROL_LOCKED (1<<0) -@@ -1080,6 +1081,8 @@ typedef struct CPUX86State { - - uint32_t pkru; - -+ uint64_t spec_ctrl; -+ - /* End of state preserved by INIT (dummy marker). */ - struct {} end_init_save; - -diff --git a/target/i386/kvm.c b/target/i386/kvm.c -index 55865dbee0..9f83c79338 100644 ---- a/target/i386/kvm.c -+++ b/target/i386/kvm.c -@@ -89,6 +89,7 @@ static bool has_msr_hv_runtime; - static bool has_msr_hv_synic; - static bool has_msr_hv_stimer; - static bool has_msr_xss; -+static bool has_msr_spec_ctrl; - - static bool has_msr_architectural_pmu; - static uint32_t num_architectural_pmu_counters; -@@ -1140,6 +1141,10 @@ static int kvm_get_supported_msrs(KVMState *s) - has_msr_hv_stimer = true; - continue; - } -+ if (kvm_msr_list->indices[i] == MSR_IA32_SPEC_CTRL) { -+ has_msr_spec_ctrl = true; -+ continue; -+ } - } - } - -@@ -1667,6 +1672,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) - if (has_msr_xss) { - kvm_msr_entry_add(cpu, MSR_IA32_XSS, env->xss); - } -+ if (has_msr_spec_ctrl) { -+ kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, env->spec_ctrl); -+ } - #ifdef TARGET_X86_64 - if (lm_capable_kernel) { - kvm_msr_entry_add(cpu, MSR_CSTAR, env->cstar); -@@ -1675,6 +1683,7 @@ static int kvm_put_msrs(X86CPU *cpu, int level) - kvm_msr_entry_add(cpu, MSR_LSTAR, env->lstar); - } - #endif -+ - /* - * The following MSRs have side effects on the guest or are too heavy - * for normal writeback. Limit them to reset or full state updates. -@@ -2081,6 +2090,9 @@ static int kvm_get_msrs(X86CPU *cpu) - if (has_msr_xss) { - kvm_msr_entry_add(cpu, MSR_IA32_XSS, 0); - } -+ if (has_msr_spec_ctrl) { -+ kvm_msr_entry_add(cpu, MSR_IA32_SPEC_CTRL, 0); -+ } - - - if (!env->tsc_valid) { -@@ -2430,6 +2442,9 @@ static int kvm_get_msrs(X86CPU *cpu) - env->mtrr_var[MSR_MTRRphysIndex(index)].base = msrs[i].data; - } - break; -+ case MSR_IA32_SPEC_CTRL: -+ env->spec_ctrl = msrs[i].data; -+ break; - } - } - -diff --git a/target/i386/machine.c b/target/i386/machine.c -index 78ae2f986b..8c0d5437fa 100644 ---- a/target/i386/machine.c -+++ b/target/i386/machine.c -@@ -927,6 +927,25 @@ static const VMStateDescription vmstate_mcg_ext_ctl = { - } - }; - -+static bool spec_ctrl_needed(void *opaque) -+{ -+ X86CPU *cpu = opaque; -+ CPUX86State *env = &cpu->env; -+ -+ return env->spec_ctrl != 0; -+} -+ -+static const VMStateDescription vmstate_spec_ctrl = { -+ .name = "cpu/spec_ctrl", -+ .version_id = 1, -+ .minimum_version_id = 1, -+ .needed = spec_ctrl_needed, -+ .fields = (VMStateField[]){ -+ VMSTATE_UINT64(env.spec_ctrl, X86CPU), -+ VMSTATE_END_OF_LIST() -+ } -+}; -+ - VMStateDescription vmstate_x86_cpu = { - .name = "cpu", - .version_id = 12, -@@ -1053,6 +1072,7 @@ VMStateDescription vmstate_x86_cpu = { - #ifdef TARGET_X86_64 - &vmstate_pkru, - #endif -+ &vmstate_spec_ctrl, - &vmstate_mcg_ext_ctl, - NULL - } --- -2.11.0 - diff --git a/debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch b/debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch deleted file mode 100644 index 254d17a..0000000 --- a/debian/patches/extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Tue, 9 Jan 2018 13:45:15 -0200 -Subject: [PATCH] i386: Add spec-ctrl CPUID bit - -Add the feature name and a CPUID_7_0_EDX_SPEC_CTRL macro. - -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 2 +- - target/i386/cpu.h | 1 + - 2 files changed, 2 insertions(+), 1 deletion(-) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index c673521016..faf1ff6dcc 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -460,7 +460,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, -- NULL, NULL, NULL, NULL, -+ NULL, NULL, "spec-ctrl", NULL, - NULL, NULL, NULL, NULL, - }, - .cpuid_eax = 7, -diff --git a/target/i386/cpu.h b/target/i386/cpu.h -index cc322d6b39..71261f4819 100644 ---- a/target/i386/cpu.h -+++ b/target/i386/cpu.h -@@ -640,6 +640,7 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; - - #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ - #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ -+#define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ - - #define CPUID_XSAVE_XSAVEOPT (1U << 0) - #define CPUID_XSAVE_XSAVEC (1U << 1) --- -2.11.0 - diff --git a/debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch b/debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch deleted file mode 100644 index 27b98fe..0000000 --- a/debian/patches/extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch +++ /dev/null @@ -1,83 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Tue, 9 Jan 2018 13:45:16 -0200 -Subject: [PATCH] i386: Add FEAT_8000_0008_EBX CPUID feature word - -Add the new feature word and the "ibpb" feature flag. - -Based on a patch by Paolo Bonzini. - -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 19 ++++++++++++++++++- - target/i386/cpu.h | 3 +++ - 2 files changed, 21 insertions(+), 1 deletion(-) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index faf1ff6dcc..eee365b78d 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -484,6 +484,22 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { - .tcg_features = TCG_APM_FEATURES, - .unmigratable_flags = CPUID_APM_INVTSC, - }, -+ [FEAT_8000_0008_EBX] = { -+ .feat_names = { -+ NULL, NULL, NULL, NULL, -+ NULL, NULL, NULL, NULL, -+ NULL, NULL, NULL, NULL, -+ "ibpb", NULL, NULL, NULL, -+ NULL, NULL, NULL, NULL, -+ NULL, NULL, NULL, NULL, -+ NULL, NULL, NULL, NULL, -+ NULL, NULL, NULL, NULL, -+ }, -+ .cpuid_eax = 0x80000008, -+ .cpuid_reg = R_EBX, -+ .tcg_features = 0, -+ .unmigratable_flags = 0, -+ }, - [FEAT_XSAVE] = { - .feat_names = { - "xsaveopt", "xsavec", "xgetbv1", "xsaves", -@@ -2984,7 +3000,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, - } else { - *eax = cpu->phys_bits; - } -- *ebx = 0; -+ *ebx = env->features[FEAT_8000_0008_EBX]; - *ecx = 0; - *edx = 0; - if (cs->nr_cores * cs->nr_threads > 1) { -@@ -3440,6 +3456,7 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp) - x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX); - x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_ECX); - x86_cpu_adjust_feat_level(cpu, FEAT_8000_0007_EDX); -+ x86_cpu_adjust_feat_level(cpu, FEAT_8000_0008_EBX); - x86_cpu_adjust_feat_level(cpu, FEAT_C000_0001_EDX); - x86_cpu_adjust_feat_level(cpu, FEAT_SVM); - x86_cpu_adjust_feat_level(cpu, FEAT_XSAVE); -diff --git a/target/i386/cpu.h b/target/i386/cpu.h -index 71261f4819..1ebee91930 100644 ---- a/target/i386/cpu.h -+++ b/target/i386/cpu.h -@@ -452,6 +452,7 @@ typedef enum FeatureWord { - FEAT_8000_0001_EDX, /* CPUID[8000_0001].EDX */ - FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */ - FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ -+ FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */ - FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ - FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ - FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */ -@@ -642,6 +643,8 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; - #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ - #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ - -+#define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */ -+ - #define CPUID_XSAVE_XSAVEOPT (1U << 0) - #define CPUID_XSAVE_XSAVEC (1U << 1) - #define CPUID_XSAVE_XGETBV1 (1U << 2) --- -2.11.0 - diff --git a/debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch b/debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch deleted file mode 100644 index 54e2835..0000000 --- a/debian/patches/extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch +++ /dev/null @@ -1,518 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Tue, 9 Jan 2018 13:45:17 -0200 -Subject: [PATCH] i386: Add new -IBRS versions of Intel CPU models - -The new MSR IA32_SPEC_CTRL MSR was introduced by a recent Intel -microcode updated and can be used by OSes to mitigate -CVE-2017-5715. Unfortunately we can't change the existing CPU -models without breaking existing setups, so users need to -explicitly update their VM configuration to use the new *-IBRS -CPU model if they want to expose IBRS to guests. - -The new CPU models are simple copies of the existing CPU models, -with just CPUID_7_0_EDX_SPEC_CTRL added and model_id updated. - -Cc: Jiri Denemark -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 427 +++++++++++++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 426 insertions(+), 1 deletion(-) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index eee365b78d..e4a2d5a012 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -1085,6 +1085,31 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Core i7 9xx (Nehalem Class Core i7)", - }, - { -+ .name = "Nehalem-IBRS", -+ .level = 11, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 26, -+ .stepping = 3, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | -+ CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_SSE3, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_LAHF_LM, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Core i7 9xx (Nehalem Core i7, IBRS update)", -+ }, -+ { - .name = "Westmere", - .level = 11, - .vendor = CPUID_VENDOR_INTEL, -@@ -1111,6 +1136,34 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Westmere E56xx/L56xx/X56xx (Nehalem-C)", - }, - { -+ .name = "Westmere-IBRS", -+ .level = 11, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 44, -+ .stepping = 1, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AES | CPUID_EXT_POPCNT | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_LAHF_LM, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Westmere E56xx/L56xx/X56xx (IBRS update)", -+ }, -+ { - .name = "SandyBridge", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1142,6 +1195,39 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Xeon E312xx (Sandy Bridge)", - }, - { -+ .name = "SandyBridge-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 42, -+ .stepping = 1, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT | -+ CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | -+ CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | -+ CPUID_EXT_SSE3, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_LAHF_LM, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Xeon E312xx (Sandy Bridge, IBRS update)", -+ }, -+ { - .name = "IvyBridge", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1176,6 +1262,42 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge)", - }, - { -+ .name = "IvyBridge-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 58, -+ .stepping = 9, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_POPCNT | -+ CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | -+ CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | CPUID_EXT_PCLMULQDQ | -+ CPUID_EXT_SSE3 | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_ERMS, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_LAHF_LM, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Xeon E3-12xx v2 (Ivy Bridge, IBRS)", -+ }, -+ { - .name = "Haswell-noTSX", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1210,7 +1332,46 @@ static X86CPUDefinition builtin_x86_defs[] = { - CPUID_6_EAX_ARAT, - .xlevel = 0x80000008, - .model_id = "Intel Core Processor (Haswell, no TSX)", -- }, { -+ }, -+ { -+ .name = "Haswell-noTSX-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 60, -+ .stepping = 1, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID, -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Core Processor (Haswell, no TSX, IBRS)", -+ }, -+ { - .name = "Haswell", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1248,6 +1409,45 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Core Processor (Haswell)", - }, - { -+ .name = "Haswell-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 60, -+ .stepping = 4, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | -+ CPUID_7_0_EBX_RTM, -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Core Processor (Haswell, IBRS)", -+ }, -+ { - .name = "Broadwell-noTSX", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1286,6 +1486,46 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Core Processor (Broadwell, no TSX)", - }, - { -+ .name = "Broadwell-noTSX-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 61, -+ .stepping = 2, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | -+ CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | -+ CPUID_7_0_EBX_SMAP, -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Core Processor (Broadwell, no TSX, IBRS)", -+ }, -+ { - .name = "Broadwell", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1324,6 +1564,46 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Core Processor (Broadwell)", - }, - { -+ .name = "Broadwell-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 61, -+ .stepping = 2, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | -+ CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | -+ CPUID_7_0_EBX_SMAP, -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Core Processor (Broadwell, IBRS)", -+ }, -+ { - .name = "Skylake-Client", - .level = 0xd, - .vendor = CPUID_VENDOR_INTEL, -@@ -1369,6 +1649,151 @@ static X86CPUDefinition builtin_x86_defs[] = { - .model_id = "Intel Core Processor (Skylake)", - }, - { -+ .name = "Skylake-Client-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 94, -+ .stepping = 3, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | -+ CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | -+ CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX, -+ /* Missing: XSAVES (not supported by some Linux versions, -+ * including v4.1 to v4.12). -+ * KVM doesn't yet expose any XSAVES state save component, -+ * and the only one defined in Skylake (processor tracing) -+ * probably will block migration anyway. -+ */ -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | -+ CPUID_XSAVE_XGETBV1, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Core Processor (Skylake, IBRS)", -+ }, -+ { -+ .name = "Skylake-Server", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 85, -+ .stepping = 4, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | -+ CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | -+ CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | -+ CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB | -+ CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | -+ CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | -+ CPUID_7_0_EBX_AVX512VL, -+ /* Missing: XSAVES (not supported by some Linux versions, -+ * including v4.1 to v4.12). -+ * KVM doesn't yet expose any XSAVES state save component, -+ * and the only one defined in Skylake (processor tracing) -+ * probably will block migration anyway. -+ */ -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | -+ CPUID_XSAVE_XGETBV1, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Xeon Processor (Skylake)", -+ }, -+ { -+ .name = "Skylake-Server-IBRS", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_INTEL, -+ .family = 6, -+ .model = 85, -+ .stepping = 4, -+ .features[FEAT_1_EDX] = -+ CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | -+ CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | -+ CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | -+ CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | -+ CPUID_DE | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | -+ CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | -+ CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | -+ CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | -+ CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | -+ CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | -+ CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, -+ .features[FEAT_7_0_EDX] = -+ CPUID_7_0_EDX_SPEC_CTRL, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | -+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | -+ CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | -+ CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | -+ CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB | -+ CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | -+ CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | -+ CPUID_7_0_EBX_AVX512VL, -+ /* Missing: XSAVES (not supported by some Linux versions, -+ * including v4.1 to v4.12). -+ * KVM doesn't yet expose any XSAVES state save component, -+ * and the only one defined in Skylake (processor tracing) -+ * probably will block migration anyway. -+ */ -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | -+ CPUID_XSAVE_XGETBV1, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x80000008, -+ .model_id = "Intel Xeon Processor (Skylake, IBRS)", -+ }, -+ { - .name = "Opteron_G1", - .level = 5, - .vendor = CPUID_VENDOR_AMD, --- -2.11.0 - diff --git a/debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch b/debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch deleted file mode 100644 index b6edf9b..0000000 --- a/debian/patches/extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch +++ /dev/null @@ -1,121 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Paolo Bonzini -Date: Thu, 1 Jun 2017 12:44:56 +0200 -Subject: [PATCH] nbd: make it thread-safe, fix qcow2 over nbd - -NBD is not thread safe, because it accesses s->in_flight without -a CoMutex. Fixing this will be required for multiqueue. -CoQueue doesn't have spurious wakeups but, when another coroutine can -run between qemu_co_queue_next's wakeup and qemu_co_queue_wait's -re-locking of the mutex, the wait condition can become false and -a loop is necessary. - -In fact, it turns out that the loop is necessary even without this -multi-threaded scenario. A particular sequence of coroutine wakeups -is happening ~80% of the time when starting a guest with qcow2 image -served over NBD (i.e. qemu-nbd --format=raw, and QEMU's -drive option -has -format=qcow2). This patch fixes that issue too. - -Signed-off-by: Paolo Bonzini ---- - block/nbd-client.c | 30 +++++++++--------------------- - 1 file changed, 9 insertions(+), 21 deletions(-) - -diff --git a/block/nbd-client.c b/block/nbd-client.c -index 56eb0e2e16..282679b4f8 100644 ---- a/block/nbd-client.c -+++ b/block/nbd-client.c -@@ -114,6 +114,10 @@ static int nbd_co_send_request(BlockDriverState *bs, - int rc, ret, i; - - qemu_co_mutex_lock(&s->send_mutex); -+ while (s->in_flight == MAX_NBD_REQUESTS) { -+ qemu_co_queue_wait(&s->free_sema, &s->send_mutex); -+ } -+ s->in_flight++; - - for (i = 0; i < MAX_NBD_REQUESTS; i++) { - if (s->recv_coroutine[i] == NULL) { -@@ -176,20 +180,6 @@ static void nbd_co_receive_reply(NBDClientSession *s, - } - } - --static void nbd_coroutine_start(NBDClientSession *s, -- NBDRequest *request) --{ -- /* Poor man semaphore. The free_sema is locked when no other request -- * can be accepted, and unlocked after receiving one reply. */ -- if (s->in_flight == MAX_NBD_REQUESTS) { -- qemu_co_queue_wait(&s->free_sema, NULL); -- assert(s->in_flight < MAX_NBD_REQUESTS); -- } -- s->in_flight++; -- -- /* s->recv_coroutine[i] is set as soon as we get the send_lock. */ --} -- - static void nbd_coroutine_end(BlockDriverState *bs, - NBDRequest *request) - { -@@ -197,13 +187,16 @@ static void nbd_coroutine_end(BlockDriverState *bs, - int i = HANDLE_TO_INDEX(s, request->handle); - - s->recv_coroutine[i] = NULL; -- s->in_flight--; -- qemu_co_queue_next(&s->free_sema); - - /* Kick the read_reply_co to get the next reply. */ - if (s->read_reply_co) { - aio_co_wake(s->read_reply_co); - } -+ -+ qemu_co_mutex_lock(&s->send_mutex); -+ s->in_flight--; -+ qemu_co_queue_next(&s->free_sema); -+ qemu_co_mutex_unlock(&s->send_mutex); - } - - int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, -@@ -221,7 +214,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, - assert(bytes <= NBD_MAX_BUFFER_SIZE); - assert(!flags); - -- nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(bs, &request, NULL); - if (ret < 0) { - reply.error = -ret; -@@ -251,7 +243,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, - - assert(bytes <= NBD_MAX_BUFFER_SIZE); - -- nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(bs, &request, qiov); - if (ret < 0) { - reply.error = -ret; -@@ -286,7 +277,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - request.flags |= NBD_CMD_FLAG_NO_HOLE; - } - -- nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(bs, &request, NULL); - if (ret < 0) { - reply.error = -ret; -@@ -311,7 +301,6 @@ int nbd_client_co_flush(BlockDriverState *bs) - request.from = 0; - request.len = 0; - -- nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(bs, &request, NULL); - if (ret < 0) { - reply.error = -ret; -@@ -337,7 +326,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) - return 0; - } - -- nbd_coroutine_start(client, &request); - ret = nbd_co_send_request(bs, &request, NULL); - if (ret < 0) { - reply.error = -ret; --- -2.11.0 - diff --git a/debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch b/debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch deleted file mode 100644 index c149913..0000000 --- a/debian/patches/extra/0037-nbd-strict-nbd_wr_syncv.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Vladimir Sementsov-Ogievskiy -Date: Tue, 16 May 2017 12:45:29 +0300 -Subject: [PATCH] nbd: strict nbd_wr_syncv - -nbd_wr_syncv is called either from coroutine or from client negotiation -code, when socket is in blocking mode. So, -EAGAIN is impossible. - -Furthermore, EAGAIN is confusing, as, what to read/write again? With -EAGAIN as a return code we don't know how much data is already -read or written by the function, so in case of EAGAIN the whole -communication is broken. - -Signed-off-by: Vladimir Sementsov-Ogievskiy -Message-Id: <20170516094533.6160-2-vsementsov@virtuozzo.com> -Signed-off-by: Paolo Bonzini ---- - nbd/common.c | 11 ++++++----- - 1 file changed, 6 insertions(+), 5 deletions(-) - -diff --git a/nbd/common.c b/nbd/common.c -index dccbb8e9de..4db45b3ede 100644 ---- a/nbd/common.c -+++ b/nbd/common.c -@@ -20,6 +20,10 @@ - #include "qapi/error.h" - #include "nbd-internal.h" - -+/* nbd_wr_syncv -+ * The function may be called from coroutine or from non-coroutine context. -+ * When called from non-coroutine context @ioc must be in blocking mode. -+ */ - ssize_t nbd_wr_syncv(QIOChannel *ioc, - struct iovec *iov, - size_t niov, -@@ -42,11 +46,8 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc, - len = qio_channel_writev(ioc, local_iov, nlocal_iov, &local_err); - } - if (len == QIO_CHANNEL_ERR_BLOCK) { -- if (qemu_in_coroutine()) { -- qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT); -- } else { -- return -EAGAIN; -- } -+ assert(qemu_in_coroutine()); -+ qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT); - continue; - } - if (len < 0) { --- -2.11.0 - diff --git a/debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch b/debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch deleted file mode 100644 index 91d5205..0000000 --- a/debian/patches/extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch +++ /dev/null @@ -1,606 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Vladimir Sementsov-Ogievskiy -Date: Tue, 16 May 2017 12:45:30 +0300 -Subject: [PATCH] nbd: read_sync and friends: return 0 on success - -functions read_sync, drop_sync, write_sync, and also -nbd_negotiate_write, nbd_negotiate_read, nbd_negotiate_drop_sync -returns number of processed bytes. But what this number can be, -except requested number of bytes? - -Actually, underlying nbd_wr_syncv function returns a value >= 0 and -!= requested_bytes only on eof on read operation. So, firstly, it is -impossible on write (let's add an assert) and on read it actually -means, that communication is broken (except nbd_receive_reply, see -below). - -Most of callers operate like this: - if (func(..., size) != size) { - /* error path */ - } -, i.e.: - 1. They are not interested in partial success - 2. Extra duplications in code (especially bad are duplications of - magic numbers) - 3. User doesn't see actual error message, as return code is lost. - (this patch doesn't fix this point, but it makes fixing easier) - -Several callers handles ret >= 0 and != requested-size separately, by -just returning EINVAL in this case. This patch makes read_sync and -friends return EINVAL in this case, so final behavior is the same. - -And only one caller - nbd_receive_reply() does something not so -obvious. It returns EINVAL for ret > 0 and != requested-size, like -previous group, but for ret == 0 it returns 0. The only caller of -nbd_receive_reply() - nbd_read_reply_entry() handles ret == 0 in the -same way as ret < 0, so for now it doesn't matter. However, in -following commits error path handling will be improved and we'll need -to distinguish success from fail in this case too. So, this patch adds -separate helper for this case - read_sync_eof. - -Signed-off-by: Vladimir Sementsov-Ogievskiy -Message-Id: <20170516094533.6160-3-vsementsov@virtuozzo.com> -Signed-off-by: Paolo Bonzini ---- - nbd/client.c | 63 ++++++++++++++++------------------------ - nbd/nbd-internal.h | 34 +++++++++++++++++++--- - nbd/server.c | 84 +++++++++++++++++++++--------------------------------- - 3 files changed, 88 insertions(+), 93 deletions(-) - -diff --git a/nbd/client.c b/nbd/client.c -index a58fb02cb4..6b74a628f1 100644 ---- a/nbd/client.c -+++ b/nbd/client.c -@@ -86,9 +86,9 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); - - */ - --/* Discard length bytes from channel. Return -errno on failure, or -- * the amount of bytes consumed. */ --static ssize_t drop_sync(QIOChannel *ioc, size_t size) -+/* Discard length bytes from channel. Return -errno on failure and 0 on -+ * success*/ -+static int drop_sync(QIOChannel *ioc, size_t size) - { - ssize_t ret = 0; - char small[1024]; -@@ -96,14 +96,13 @@ static ssize_t drop_sync(QIOChannel *ioc, size_t size) - - buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); - while (size > 0) { -- ssize_t count = read_sync(ioc, buffer, MIN(65536, size)); -+ ssize_t count = MIN(65536, size); -+ ret = read_sync(ioc, buffer, MIN(65536, size)); - -- if (count <= 0) { -+ if (ret < 0) { - goto cleanup; - } -- assert(count <= size); - size -= count; -- ret += count; - } - - cleanup: -@@ -136,12 +135,12 @@ static int nbd_send_option_request(QIOChannel *ioc, uint32_t opt, - stl_be_p(&req.option, opt); - stl_be_p(&req.length, len); - -- if (write_sync(ioc, &req, sizeof(req)) != sizeof(req)) { -+ if (write_sync(ioc, &req, sizeof(req)) < 0) { - error_setg(errp, "Failed to send option request header"); - return -1; - } - -- if (len && write_sync(ioc, (char *) data, len) != len) { -+ if (len && write_sync(ioc, (char *) data, len) < 0) { - error_setg(errp, "Failed to send option request data"); - return -1; - } -@@ -170,7 +169,7 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, - nbd_opt_reply *reply, Error **errp) - { - QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); -- if (read_sync(ioc, reply, sizeof(*reply)) != sizeof(*reply)) { -+ if (read_sync(ioc, reply, sizeof(*reply)) < 0) { - error_setg(errp, "failed to read option reply"); - nbd_send_opt_abort(ioc); - return -1; -@@ -219,7 +218,7 @@ static int nbd_handle_reply_err(QIOChannel *ioc, nbd_opt_reply *reply, - goto cleanup; - } - msg = g_malloc(reply->length + 1); -- if (read_sync(ioc, msg, reply->length) != reply->length) { -+ if (read_sync(ioc, msg, reply->length) < 0) { - error_setg(errp, "failed to read option error message"); - goto cleanup; - } -@@ -321,7 +320,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, - nbd_send_opt_abort(ioc); - return -1; - } -- if (read_sync(ioc, &namelen, sizeof(namelen)) != sizeof(namelen)) { -+ if (read_sync(ioc, &namelen, sizeof(namelen)) < 0) { - error_setg(errp, "failed to read option name length"); - nbd_send_opt_abort(ioc); - return -1; -@@ -334,7 +333,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, - return -1; - } - if (namelen != strlen(want)) { -- if (drop_sync(ioc, len) != len) { -+ if (drop_sync(ioc, len) < 0) { - error_setg(errp, "failed to skip export name with wrong length"); - nbd_send_opt_abort(ioc); - return -1; -@@ -343,14 +342,14 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, - } - - assert(namelen < sizeof(name)); -- if (read_sync(ioc, name, namelen) != namelen) { -+ if (read_sync(ioc, name, namelen) < 0) { - error_setg(errp, "failed to read export name"); - nbd_send_opt_abort(ioc); - return -1; - } - name[namelen] = '\0'; - len -= namelen; -- if (drop_sync(ioc, len) != len) { -+ if (drop_sync(ioc, len) < 0) { - error_setg(errp, "failed to read export description"); - nbd_send_opt_abort(ioc); - return -1; -@@ -477,7 +476,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - goto fail; - } - -- if (read_sync(ioc, buf, 8) != 8) { -+ if (read_sync(ioc, buf, 8) < 0) { - error_setg(errp, "Failed to read data"); - goto fail; - } -@@ -503,7 +502,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - goto fail; - } - -- if (read_sync(ioc, &magic, sizeof(magic)) != sizeof(magic)) { -+ if (read_sync(ioc, &magic, sizeof(magic)) < 0) { - error_setg(errp, "Failed to read magic"); - goto fail; - } -@@ -515,8 +514,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - uint16_t globalflags; - bool fixedNewStyle = false; - -- if (read_sync(ioc, &globalflags, sizeof(globalflags)) != -- sizeof(globalflags)) { -+ if (read_sync(ioc, &globalflags, sizeof(globalflags)) < 0) { - error_setg(errp, "Failed to read server flags"); - goto fail; - } -@@ -534,8 +532,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - } - /* client requested flags */ - clientflags = cpu_to_be32(clientflags); -- if (write_sync(ioc, &clientflags, sizeof(clientflags)) != -- sizeof(clientflags)) { -+ if (write_sync(ioc, &clientflags, sizeof(clientflags)) < 0) { - error_setg(errp, "Failed to send clientflags field"); - goto fail; - } -@@ -573,13 +570,13 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - } - - /* Read the response */ -- if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { -+ if (read_sync(ioc, &s, sizeof(s)) < 0) { - error_setg(errp, "Failed to read export length"); - goto fail; - } - *size = be64_to_cpu(s); - -- if (read_sync(ioc, flags, sizeof(*flags)) != sizeof(*flags)) { -+ if (read_sync(ioc, flags, sizeof(*flags)) < 0) { - error_setg(errp, "Failed to read export flags"); - goto fail; - } -@@ -596,14 +593,14 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - goto fail; - } - -- if (read_sync(ioc, &s, sizeof(s)) != sizeof(s)) { -+ if (read_sync(ioc, &s, sizeof(s)) < 0) { - error_setg(errp, "Failed to read export length"); - goto fail; - } - *size = be64_to_cpu(s); - TRACE("Size is %" PRIu64, *size); - -- if (read_sync(ioc, &oldflags, sizeof(oldflags)) != sizeof(oldflags)) { -+ if (read_sync(ioc, &oldflags, sizeof(oldflags)) < 0) { - error_setg(errp, "Failed to read export flags"); - goto fail; - } -@@ -619,7 +616,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - } - - TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags); -- if (zeroes && drop_sync(ioc, 124) != 124) { -+ if (zeroes && drop_sync(ioc, 124) < 0) { - error_setg(errp, "Failed to read reserved block"); - goto fail; - } -@@ -744,7 +741,6 @@ int nbd_disconnect(int fd) - ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request) - { - uint8_t buf[NBD_REQUEST_SIZE]; -- ssize_t ret; - - TRACE("Sending request to server: " - "{ .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 -@@ -759,16 +755,7 @@ ssize_t nbd_send_request(QIOChannel *ioc, NBDRequest *request) - stq_be_p(buf + 16, request->from); - stl_be_p(buf + 24, request->len); - -- ret = write_sync(ioc, buf, sizeof(buf)); -- if (ret < 0) { -- return ret; -- } -- -- if (ret != sizeof(buf)) { -- LOG("writing to socket failed"); -- return -EINVAL; -- } -- return 0; -+ return write_sync(ioc, buf, sizeof(buf)); - } - - ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply) -@@ -777,7 +764,7 @@ ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply) - uint32_t magic; - ssize_t ret; - -- ret = read_sync(ioc, buf, sizeof(buf)); -+ ret = read_sync_eof(ioc, buf, sizeof(buf)); - if (ret <= 0) { - return ret; - } -diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h -index f43d990a05..e6bbc7c4b4 100644 ---- a/nbd/nbd-internal.h -+++ b/nbd/nbd-internal.h -@@ -94,7 +94,13 @@ - #define NBD_ENOSPC 28 - #define NBD_ESHUTDOWN 108 - --static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size) -+/* read_sync_eof -+ * Tries to read @size bytes from @ioc. Returns number of bytes actually read. -+ * May return a value >= 0 and < size only on EOF, i.e. when iteratively called -+ * qio_channel_readv() returns 0. So, there are no needs to call read_sync_eof -+ * iteratively. -+ */ -+static inline ssize_t read_sync_eof(QIOChannel *ioc, void *buffer, size_t size) - { - struct iovec iov = { .iov_base = buffer, .iov_len = size }; - /* Sockets are kept in blocking mode in the negotiation phase. After -@@ -105,12 +111,32 @@ static inline ssize_t read_sync(QIOChannel *ioc, void *buffer, size_t size) - return nbd_wr_syncv(ioc, &iov, 1, size, true); - } - --static inline ssize_t write_sync(QIOChannel *ioc, const void *buffer, -- size_t size) -+/* read_sync -+ * Reads @size bytes from @ioc. Returns 0 on success. -+ */ -+static inline int read_sync(QIOChannel *ioc, void *buffer, size_t size) -+{ -+ ssize_t ret = read_sync_eof(ioc, buffer, size); -+ -+ if (ret >= 0 && ret != size) { -+ ret = -EINVAL; -+ } -+ -+ return ret < 0 ? ret : 0; -+} -+ -+/* write_sync -+ * Writes @size bytes to @ioc. Returns 0 on success. -+ */ -+static inline int write_sync(QIOChannel *ioc, const void *buffer, size_t size) - { - struct iovec iov = { .iov_base = (void *) buffer, .iov_len = size }; - -- return nbd_wr_syncv(ioc, &iov, 1, size, false); -+ ssize_t ret = nbd_wr_syncv(ioc, &iov, 1, size, false); -+ -+ assert(ret < 0 || ret == size); -+ -+ return ret < 0 ? ret : 0; - } - - struct NBDTLSHandshakeData { -diff --git a/nbd/server.c b/nbd/server.c -index 4d6da8ac06..c2a5909ad6 100644 ---- a/nbd/server.c -+++ b/nbd/server.c -@@ -112,7 +112,7 @@ static gboolean nbd_negotiate_continue(QIOChannel *ioc, - return TRUE; - } - --static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size) -+static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size) - { - ssize_t ret; - guint watch; -@@ -130,8 +130,7 @@ static ssize_t nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size) - - } - --static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer, -- size_t size) -+static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size) - { - ssize_t ret; - guint watch; -@@ -148,24 +147,24 @@ static ssize_t nbd_negotiate_write(QIOChannel *ioc, const void *buffer, - return ret; - } - --static ssize_t nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size) -+static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size) - { -- ssize_t ret, dropped = size; -+ ssize_t ret; - uint8_t *buffer = g_malloc(MIN(65536, size)); - - while (size > 0) { -- ret = nbd_negotiate_read(ioc, buffer, MIN(65536, size)); -+ size_t count = MIN(65536, size); -+ ret = nbd_negotiate_read(ioc, buffer, count); - if (ret < 0) { - g_free(buffer); - return ret; - } - -- assert(ret <= size); -- size -= ret; -+ size -= count; - } - - g_free(buffer); -- return dropped; -+ return 0; - } - - /* Basic flow for negotiation -@@ -206,22 +205,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type, - type, opt, len); - - magic = cpu_to_be64(NBD_REP_MAGIC); -- if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) != sizeof(magic)) { -+ if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) { - LOG("write failed (rep magic)"); - return -EINVAL; - } - opt = cpu_to_be32(opt); -- if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) != sizeof(opt)) { -+ if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) { - LOG("write failed (rep opt)"); - return -EINVAL; - } - type = cpu_to_be32(type); -- if (nbd_negotiate_write(ioc, &type, sizeof(type)) != sizeof(type)) { -+ if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) { - LOG("write failed (rep type)"); - return -EINVAL; - } - len = cpu_to_be32(len); -- if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { -+ if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) { - LOG("write failed (rep data length)"); - return -EINVAL; - } -@@ -256,7 +255,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type, - if (ret < 0) { - goto out; - } -- if (nbd_negotiate_write(ioc, msg, len) != len) { -+ if (nbd_negotiate_write(ioc, msg, len) < 0) { - LOG("write failed (error message)"); - ret = -EIO; - } else { -@@ -287,15 +286,15 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp) - } - - len = cpu_to_be32(name_len); -- if (nbd_negotiate_write(ioc, &len, sizeof(len)) != sizeof(len)) { -+ if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) { - LOG("write failed (name length)"); - return -EINVAL; - } -- if (nbd_negotiate_write(ioc, name, name_len) != name_len) { -+ if (nbd_negotiate_write(ioc, name, name_len) < 0) { - LOG("write failed (name buffer)"); - return -EINVAL; - } -- if (nbd_negotiate_write(ioc, desc, desc_len) != desc_len) { -+ if (nbd_negotiate_write(ioc, desc, desc_len) < 0) { - LOG("write failed (description buffer)"); - return -EINVAL; - } -@@ -309,7 +308,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length) - NBDExport *exp; - - if (length) { -- if (nbd_negotiate_drop_sync(client->ioc, length) != length) { -+ if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { - return -EIO; - } - return nbd_negotiate_send_rep_err(client->ioc, -@@ -340,7 +339,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length) - LOG("Bad length received"); - goto fail; - } -- if (nbd_negotiate_read(client->ioc, name, length) != length) { -+ if (nbd_negotiate_read(client->ioc, name, length) < 0) { - LOG("read failed"); - goto fail; - } -@@ -373,7 +372,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, - TRACE("Setting up TLS"); - ioc = client->ioc; - if (length) { -- if (nbd_negotiate_drop_sync(ioc, length) != length) { -+ if (nbd_negotiate_drop_sync(ioc, length) < 0) { - return NULL; - } - nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS, -@@ -437,8 +436,7 @@ static int nbd_negotiate_options(NBDClient *client) - ... Rest of request - */ - -- if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) != -- sizeof(flags)) { -+ if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) { - LOG("read failed"); - return -EIO; - } -@@ -464,8 +462,7 @@ static int nbd_negotiate_options(NBDClient *client) - uint32_t clientflags, length; - uint64_t magic; - -- if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) != -- sizeof(magic)) { -+ if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) { - LOG("read failed"); - return -EINVAL; - } -@@ -476,14 +473,14 @@ static int nbd_negotiate_options(NBDClient *client) - } - - if (nbd_negotiate_read(client->ioc, &clientflags, -- sizeof(clientflags)) != sizeof(clientflags)) { -+ sizeof(clientflags)) < 0) -+ { - LOG("read failed"); - return -EINVAL; - } - clientflags = be32_to_cpu(clientflags); - -- if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) != -- sizeof(length)) { -+ if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) { - LOG("read failed"); - return -EINVAL; - } -@@ -519,7 +516,7 @@ static int nbd_negotiate_options(NBDClient *client) - return -EINVAL; - - default: -- if (nbd_negotiate_drop_sync(client->ioc, length) != length) { -+ if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { - return -EIO; - } - ret = nbd_negotiate_send_rep_err(client->ioc, -@@ -557,7 +554,7 @@ static int nbd_negotiate_options(NBDClient *client) - return nbd_negotiate_handle_export_name(client, length); - - case NBD_OPT_STARTTLS: -- if (nbd_negotiate_drop_sync(client->ioc, length) != length) { -+ if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { - return -EIO; - } - if (client->tlscreds) { -@@ -576,7 +573,7 @@ static int nbd_negotiate_options(NBDClient *client) - } - break; - default: -- if (nbd_negotiate_drop_sync(client->ioc, length) != length) { -+ if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { - return -EIO; - } - ret = nbd_negotiate_send_rep_err(client->ioc, -@@ -665,12 +662,12 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) - TRACE("TLS cannot be enabled with oldstyle protocol"); - goto fail; - } -- if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) != sizeof(buf)) { -+ if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) { - LOG("write failed"); - goto fail; - } - } else { -- if (nbd_negotiate_write(client->ioc, buf, 18) != 18) { -+ if (nbd_negotiate_write(client->ioc, buf, 18) < 0) { - LOG("write failed"); - goto fail; - } -@@ -685,7 +682,7 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) - stq_be_p(buf + 18, client->exp->size); - stw_be_p(buf + 26, client->exp->nbdflags | myflags); - len = client->no_zeroes ? 10 : sizeof(buf) - 18; -- if (nbd_negotiate_write(client->ioc, buf + 18, len) != len) { -+ if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) { - LOG("write failed"); - goto fail; - } -@@ -708,11 +705,6 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request) - return ret; - } - -- if (ret != sizeof(buf)) { -- LOG("read failed"); -- return -EINVAL; -- } -- - /* Request - [ 0 .. 3] magic (NBD_REQUEST_MAGIC) - [ 4 .. 5] flags (NBD_CMD_FLAG_FUA, ...) -@@ -743,7 +735,6 @@ static ssize_t nbd_receive_request(QIOChannel *ioc, NBDRequest *request) - static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply) - { - uint8_t buf[NBD_REPLY_SIZE]; -- ssize_t ret; - - reply->error = system_errno_to_nbd_errno(reply->error); - -@@ -760,16 +751,7 @@ static ssize_t nbd_send_reply(QIOChannel *ioc, NBDReply *reply) - stl_be_p(buf + 4, reply->error); - stq_be_p(buf + 8, reply->handle); - -- ret = write_sync(ioc, buf, sizeof(buf)); -- if (ret < 0) { -- return ret; -- } -- -- if (ret != sizeof(buf)) { -- LOG("writing to socket failed"); -- return -EINVAL; -- } -- return 0; -+ return write_sync(ioc, buf, sizeof(buf)); - } - - #define MAX_NBD_REQUESTS 16 -@@ -1073,7 +1055,7 @@ static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply, - rc = nbd_send_reply(client->ioc, reply); - if (rc >= 0) { - ret = write_sync(client->ioc, req->data, len); -- if (ret != len) { -+ if (ret < 0) { - rc = -EIO; - } - } -@@ -1147,7 +1129,7 @@ static ssize_t nbd_co_receive_request(NBDRequestData *req, - if (request->type == NBD_CMD_WRITE) { - TRACE("Reading %" PRIu32 " byte(s)", request->len); - -- if (read_sync(client->ioc, req->data, request->len) != request->len) { -+ if (read_sync(client->ioc, req->data, request->len) < 0) { - LOG("reading from socket failed"); - rc = -EIO; - goto out; --- -2.11.0 - diff --git a/debian/patches/extra/0039-nbd-make-nbd_drop-public.patch b/debian/patches/extra/0039-nbd-make-nbd_drop-public.patch deleted file mode 100644 index 03bc7cf..0000000 --- a/debian/patches/extra/0039-nbd-make-nbd_drop-public.patch +++ /dev/null @@ -1,151 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 19 Jul 2017 18:02:01 +0200 -Subject: [PATCH] nbd: make nbd_drop public - -RH-Author: Eric Blake -Message-id: <20170719180202.23329-4-eblake@redhat.com> -Patchwork-id: 75814 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 3/4] nbd: make nbd_drop public -Bugzilla: 1467509 -RH-Acked-by: Stefan Hajnoczi -RH-Acked-by: Paolo Bonzini -RH-Acked-by: Jeffrey Cody - -From: Vladimir Sementsov-Ogievskiy - -Following commit will reuse it for nbd server too. - -Reviewed-by: Eric Blake -Signed-off-by: Vladimir Sementsov-Ogievskiy -Message-Id: <20170602150150.258222-3-vsementsov@virtuozzo.com> -Signed-off-by: Paolo Bonzini -(cherry picked from commit 44298024d30ad36439707b89715a76333f58791b) - - Conflicts: - nbd/client.c, nbd/nbd_internal.h, nbd/common.c - missing errp - addition (e44ed99) and bulk rename (d1fdf25) - -Signed-off-by: Eric Blake -Signed-off-by: Miroslav Rezanina ---- - nbd/client.c | 32 +++----------------------------- - nbd/common.c | 26 ++++++++++++++++++++++++++ - nbd/nbd-internal.h | 2 ++ - 3 files changed, 31 insertions(+), 29 deletions(-) - -diff --git a/nbd/client.c b/nbd/client.c -index 6b74a628f1..1652f28e9f 100644 ---- a/nbd/client.c -+++ b/nbd/client.c -@@ -86,32 +86,6 @@ static QTAILQ_HEAD(, NBDExport) exports = QTAILQ_HEAD_INITIALIZER(exports); - - */ - --/* Discard length bytes from channel. Return -errno on failure and 0 on -- * success*/ --static int drop_sync(QIOChannel *ioc, size_t size) --{ -- ssize_t ret = 0; -- char small[1024]; -- char *buffer; -- -- buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); -- while (size > 0) { -- ssize_t count = MIN(65536, size); -- ret = read_sync(ioc, buffer, MIN(65536, size)); -- -- if (ret < 0) { -- goto cleanup; -- } -- size -= count; -- } -- -- cleanup: -- if (buffer != small) { -- g_free(buffer); -- } -- return ret; --} -- - /* Send an option request. - * - * The request is for option @opt, with @data containing @len bytes of -@@ -333,7 +307,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, - return -1; - } - if (namelen != strlen(want)) { -- if (drop_sync(ioc, len) < 0) { -+ if (nbd_drop(ioc, len) < 0) { - error_setg(errp, "failed to skip export name with wrong length"); - nbd_send_opt_abort(ioc); - return -1; -@@ -349,7 +323,7 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, - } - name[namelen] = '\0'; - len -= namelen; -- if (drop_sync(ioc, len) < 0) { -+ if (nbd_drop(ioc, len) < 0) { - error_setg(errp, "failed to read export description"); - nbd_send_opt_abort(ioc); - return -1; -@@ -616,7 +590,7 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t *flags, - } - - TRACE("Size is %" PRIu64 ", export flags %" PRIx16, *size, *flags); -- if (zeroes && drop_sync(ioc, 124) < 0) { -+ if (zeroes && nbd_drop(ioc, 124) < 0) { - error_setg(errp, "Failed to read reserved block"); - goto fail; - } -diff --git a/nbd/common.c b/nbd/common.c -index 4db45b3ede..9a54010c25 100644 ---- a/nbd/common.c -+++ b/nbd/common.c -@@ -71,6 +71,32 @@ ssize_t nbd_wr_syncv(QIOChannel *ioc, - return done; - } - -+/* Discard length bytes from channel. Return -errno on failure and 0 on -+ * success */ -+int nbd_drop(QIOChannel *ioc, size_t size) -+{ -+ ssize_t ret = 0; -+ char small[1024]; -+ char *buffer; -+ -+ buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); -+ while (size > 0) { -+ ssize_t count = MIN(65536, size); -+ ret = read_sync(ioc, buffer, MIN(65536, size)); -+ -+ if (ret < 0) { -+ goto cleanup; -+ } -+ size -= count; -+ } -+ -+ cleanup: -+ if (buffer != small) { -+ g_free(buffer); -+ } -+ return ret; -+} -+ - - void nbd_tls_handshake(QIOTask *task, - void *opaque) -diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h -index e6bbc7c4b4..c02378c553 100644 ---- a/nbd/nbd-internal.h -+++ b/nbd/nbd-internal.h -@@ -149,4 +149,6 @@ struct NBDTLSHandshakeData { - void nbd_tls_handshake(QIOTask *task, - void *opaque); - -+int nbd_drop(QIOChannel *ioc, size_t size); -+ - #endif --- -2.11.0 - diff --git a/debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch b/debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch deleted file mode 100644 index 638e22f..0000000 --- a/debian/patches/extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch +++ /dev/null @@ -1,292 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 19 Jul 2017 18:02:02 +0200 -Subject: [PATCH] nbd/server: get rid of nbd_negotiate_read and friends - -RH-Author: Eric Blake -Message-id: <20170719180202.23329-5-eblake@redhat.com> -Patchwork-id: 75816 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 4/4] nbd/server: get rid of nbd_negotiate_read and friends -Bugzilla: 1467509 -RH-Acked-by: Stefan Hajnoczi -RH-Acked-by: Paolo Bonzini -RH-Acked-by: Jeffrey Cody - -From: Vladimir Sementsov-Ogievskiy - -Functions nbd_negotiate_{read,write,drop_sync} were introduced in -1a6245a5b, when nbd_rwv (was nbd_wr_sync) was working through -qemu_co_sendv_recvv (the path is nbd_wr_sync -> qemu_co_{recv/send} -> -qemu_co_send_recv -> qemu_co_sendv_recvv), which just yields, without -setting any handlers. But starting from ff82911cd nbd_rwv (was -nbd_wr_syncv) works through qio_channel_yield() which sets handlers, so -watchers are redundant in nbd_negotiate_{read,write,drop_sync}, then, -let's just use nbd_{read,write,drop} functions. - -Functions nbd_{read,write,drop} has errp parameter, which is unused in -this patch. This will be fixed later. - -Signed-off-by: Vladimir Sementsov-Ogievskiy -Reviewed-by: Eric Blake -Message-Id: <20170602150150.258222-4-vsementsov@virtuozzo.com> -Signed-off-by: Paolo Bonzini -(cherry picked from commit 2b0bbc4f8809c972bad134bc1a2570dbb01dea0b) - -Signed-off-by: Miroslav Rezanina - -Conflicts: - nbd/server.c - missing errp addition (e44ed99) and bulk - rename (d1fdf25) -Fixes CVE-2017-7539 - -Signed-off-by: Eric Blake ---- - nbd/server.c | 106 ++++++++++++----------------------------------------------- - 1 file changed, 21 insertions(+), 85 deletions(-) - -diff --git a/nbd/server.c b/nbd/server.c -index c2a5909ad6..ddf93d4717 100644 ---- a/nbd/server.c -+++ b/nbd/server.c -@@ -104,69 +104,6 @@ struct NBDClient { - - static void nbd_client_receive_next_request(NBDClient *client); - --static gboolean nbd_negotiate_continue(QIOChannel *ioc, -- GIOCondition condition, -- void *opaque) --{ -- qemu_coroutine_enter(opaque); -- return TRUE; --} -- --static int nbd_negotiate_read(QIOChannel *ioc, void *buffer, size_t size) --{ -- ssize_t ret; -- guint watch; -- -- assert(qemu_in_coroutine()); -- /* Negotiation are always in main loop. */ -- watch = qio_channel_add_watch(ioc, -- G_IO_IN, -- nbd_negotiate_continue, -- qemu_coroutine_self(), -- NULL); -- ret = read_sync(ioc, buffer, size); -- g_source_remove(watch); -- return ret; -- --} -- --static int nbd_negotiate_write(QIOChannel *ioc, const void *buffer, size_t size) --{ -- ssize_t ret; -- guint watch; -- -- assert(qemu_in_coroutine()); -- /* Negotiation are always in main loop. */ -- watch = qio_channel_add_watch(ioc, -- G_IO_OUT, -- nbd_negotiate_continue, -- qemu_coroutine_self(), -- NULL); -- ret = write_sync(ioc, buffer, size); -- g_source_remove(watch); -- return ret; --} -- --static int nbd_negotiate_drop_sync(QIOChannel *ioc, size_t size) --{ -- ssize_t ret; -- uint8_t *buffer = g_malloc(MIN(65536, size)); -- -- while (size > 0) { -- size_t count = MIN(65536, size); -- ret = nbd_negotiate_read(ioc, buffer, count); -- if (ret < 0) { -- g_free(buffer); -- return ret; -- } -- -- size -= count; -- } -- -- g_free(buffer); -- return 0; --} -- - /* Basic flow for negotiation - - Server Client -@@ -205,22 +142,22 @@ static int nbd_negotiate_send_rep_len(QIOChannel *ioc, uint32_t type, - type, opt, len); - - magic = cpu_to_be64(NBD_REP_MAGIC); -- if (nbd_negotiate_write(ioc, &magic, sizeof(magic)) < 0) { -+ if (write_sync(ioc, &magic, sizeof(magic)) < 0) { - LOG("write failed (rep magic)"); - return -EINVAL; - } - opt = cpu_to_be32(opt); -- if (nbd_negotiate_write(ioc, &opt, sizeof(opt)) < 0) { -+ if (write_sync(ioc, &opt, sizeof(opt)) < 0) { - LOG("write failed (rep opt)"); - return -EINVAL; - } - type = cpu_to_be32(type); -- if (nbd_negotiate_write(ioc, &type, sizeof(type)) < 0) { -+ if (write_sync(ioc, &type, sizeof(type)) < 0) { - LOG("write failed (rep type)"); - return -EINVAL; - } - len = cpu_to_be32(len); -- if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) { -+ if (write_sync(ioc, &len, sizeof(len)) < 0) { - LOG("write failed (rep data length)"); - return -EINVAL; - } -@@ -255,7 +192,7 @@ nbd_negotiate_send_rep_err(QIOChannel *ioc, uint32_t type, - if (ret < 0) { - goto out; - } -- if (nbd_negotiate_write(ioc, msg, len) < 0) { -+ if (write_sync(ioc, msg, len) < 0) { - LOG("write failed (error message)"); - ret = -EIO; - } else { -@@ -286,15 +223,15 @@ static int nbd_negotiate_send_rep_list(QIOChannel *ioc, NBDExport *exp) - } - - len = cpu_to_be32(name_len); -- if (nbd_negotiate_write(ioc, &len, sizeof(len)) < 0) { -+ if (write_sync(ioc, &len, sizeof(len)) < 0) { - LOG("write failed (name length)"); - return -EINVAL; - } -- if (nbd_negotiate_write(ioc, name, name_len) < 0) { -+ if (write_sync(ioc, name, name_len) < 0) { - LOG("write failed (name buffer)"); - return -EINVAL; - } -- if (nbd_negotiate_write(ioc, desc, desc_len) < 0) { -+ if (write_sync(ioc, desc, desc_len) < 0) { - LOG("write failed (description buffer)"); - return -EINVAL; - } -@@ -308,7 +245,7 @@ static int nbd_negotiate_handle_list(NBDClient *client, uint32_t length) - NBDExport *exp; - - if (length) { -- if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { -+ if (nbd_drop(client->ioc, length) < 0) { - return -EIO; - } - return nbd_negotiate_send_rep_err(client->ioc, -@@ -339,7 +276,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, uint32_t length) - LOG("Bad length received"); - goto fail; - } -- if (nbd_negotiate_read(client->ioc, name, length) < 0) { -+ if (read_sync(client->ioc, name, length) < 0) { - LOG("read failed"); - goto fail; - } -@@ -372,7 +309,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, - TRACE("Setting up TLS"); - ioc = client->ioc; - if (length) { -- if (nbd_negotiate_drop_sync(ioc, length) < 0) { -+ if (nbd_drop(ioc, length) < 0) { - return NULL; - } - nbd_negotiate_send_rep_err(ioc, NBD_REP_ERR_INVALID, NBD_OPT_STARTTLS, -@@ -436,7 +373,7 @@ static int nbd_negotiate_options(NBDClient *client) - ... Rest of request - */ - -- if (nbd_negotiate_read(client->ioc, &flags, sizeof(flags)) < 0) { -+ if (read_sync(client->ioc, &flags, sizeof(flags)) < 0) { - LOG("read failed"); - return -EIO; - } -@@ -462,7 +399,7 @@ static int nbd_negotiate_options(NBDClient *client) - uint32_t clientflags, length; - uint64_t magic; - -- if (nbd_negotiate_read(client->ioc, &magic, sizeof(magic)) < 0) { -+ if (read_sync(client->ioc, &magic, sizeof(magic)) < 0) { - LOG("read failed"); - return -EINVAL; - } -@@ -472,15 +409,14 @@ static int nbd_negotiate_options(NBDClient *client) - return -EINVAL; - } - -- if (nbd_negotiate_read(client->ioc, &clientflags, -- sizeof(clientflags)) < 0) -+ if (read_sync(client->ioc, &clientflags, sizeof(clientflags)) < 0) - { - LOG("read failed"); - return -EINVAL; - } - clientflags = be32_to_cpu(clientflags); - -- if (nbd_negotiate_read(client->ioc, &length, sizeof(length)) < 0) { -+ if (read_sync(client->ioc, &length, sizeof(length)) < 0) { - LOG("read failed"); - return -EINVAL; - } -@@ -516,7 +452,7 @@ static int nbd_negotiate_options(NBDClient *client) - return -EINVAL; - - default: -- if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { -+ if (nbd_drop(client->ioc, length) < 0) { - return -EIO; - } - ret = nbd_negotiate_send_rep_err(client->ioc, -@@ -554,7 +490,7 @@ static int nbd_negotiate_options(NBDClient *client) - return nbd_negotiate_handle_export_name(client, length); - - case NBD_OPT_STARTTLS: -- if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { -+ if (nbd_drop(client->ioc, length) < 0) { - return -EIO; - } - if (client->tlscreds) { -@@ -573,7 +509,7 @@ static int nbd_negotiate_options(NBDClient *client) - } - break; - default: -- if (nbd_negotiate_drop_sync(client->ioc, length) < 0) { -+ if (nbd_drop(client->ioc, length) < 0) { - return -EIO; - } - ret = nbd_negotiate_send_rep_err(client->ioc, -@@ -662,12 +598,12 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) - TRACE("TLS cannot be enabled with oldstyle protocol"); - goto fail; - } -- if (nbd_negotiate_write(client->ioc, buf, sizeof(buf)) < 0) { -+ if (write_sync(client->ioc, buf, sizeof(buf)) < 0) { - LOG("write failed"); - goto fail; - } - } else { -- if (nbd_negotiate_write(client->ioc, buf, 18) < 0) { -+ if (write_sync(client->ioc, buf, 18) < 0) { - LOG("write failed"); - goto fail; - } -@@ -682,7 +618,7 @@ static coroutine_fn int nbd_negotiate(NBDClientNewData *data) - stq_be_p(buf + 18, client->exp->size); - stw_be_p(buf + 26, client->exp->nbdflags | myflags); - len = client->no_zeroes ? 10 : sizeof(buf) - 18; -- if (nbd_negotiate_write(client->ioc, buf + 18, len) < 0) { -+ if (write_sync(client->ioc, buf + 18, len) < 0) { - LOG("write failed"); - goto fail; - } --- -2.11.0 - diff --git a/debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch b/debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch deleted file mode 100644 index 2d7560d..0000000 --- a/debian/patches/extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:19 +0200 -Subject: [PATCH] nbd-client: Fix regression when server sends garbage - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-2-eblake@redhat.com> -Patchwork-id: 76672 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 1/7] nbd-client: Fix regression when server sends garbage -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -When we switched NBD to use coroutines for qemu 2.9 (in particular, -commit a12a712a), we introduced a regression: if a server sends us -garbage (such as a corrupted magic number), we quit the read loop -but do not stop sending further queued commands, resulting in the -client hanging when it never reads the response to those additional -commands. In qemu 2.8, we properly detected that the server is no -longer reliable, and cancelled all existing pending commands with -EIO, then tore down the socket so that all further command attempts -get EPIPE. - -Restore the proper behavior of quitting (almost) all communication -with a broken server: Once we know we are out of sync or otherwise -can't trust the server, we must assume that any further incoming -data is unreliable and therefore end all pending commands with EIO, -and quit trying to send any further commands. As an exception, we -still (try to) send NBD_CMD_DISC to let the server know we are going -away (in part, because it is easier to do that than to further -refactor nbd_teardown_connection, and in part because it is the -only command where we do not have to wait for a reply). - -Based on a patch by Vladimir Sementsov-Ogievskiy. - -A malicious server can be created with the following hack, -followed by setting NBD_SERVER_DEBUG to a non-zero value in the -environment when running qemu-nbd: - -| --- a/nbd/server.c -| +++ b/nbd/server.c -| @@ -919,6 +919,17 @@ static int nbd_send_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) -| stl_be_p(buf + 4, reply->error); -| stq_be_p(buf + 8, reply->handle); -| -| + static int debug; -| + static int count; -| + if (!count++) { -| + const char *str = getenv("NBD_SERVER_DEBUG"); -| + if (str) { -| + debug = atoi(str); -| + } -| + } -| + if (debug && !(count % debug)) { -| + buf[0] = 0; -| + } -| return nbd_write(ioc, buf, sizeof(buf), errp); -| } - -Reported-by: Vladimir Sementsov-Ogievskiy -Signed-off-by: Eric Blake -Message-Id: <20170814213426.24681-1-eblake@redhat.com> -Reviewed-by: Stefan Hajnoczi -(cherry picked from commit 72b6ffc76653214b69a94a7b1643ff80df134486) -Signed-off-by: Eric Blake -Signed-off-by: Miroslav Rezanina - -Conflicts: - block/nbd-client.c ---- - block/nbd-client.c | 17 +++++++++++++---- - block/nbd-client.h | 1 + - 2 files changed, 14 insertions(+), 4 deletions(-) - -diff --git a/block/nbd-client.c b/block/nbd-client.c -index 282679b4f8..701b4ce2eb 100644 ---- a/block/nbd-client.c -+++ b/block/nbd-client.c -@@ -71,7 +71,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) - uint64_t i; - int ret; - -- for (;;) { -+ while (!s->quit) { - assert(s->reply.handle == 0); - ret = nbd_receive_reply(s->ioc, &s->reply); - if (ret <= 0) { -@@ -102,6 +102,9 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) - qemu_coroutine_yield(); - } - -+ if (ret < 0) { -+ s->quit = true; -+ } - nbd_recv_coroutines_enter_all(s); - s->read_reply_co = NULL; - } -@@ -130,6 +133,10 @@ static int nbd_co_send_request(BlockDriverState *bs, - assert(i < MAX_NBD_REQUESTS); - request->handle = INDEX_TO_HANDLE(s, i); - -+ if (s->quit) { -+ qemu_co_mutex_unlock(&s->send_mutex); -+ return -EIO; -+ } - if (!s->ioc) { - qemu_co_mutex_unlock(&s->send_mutex); - return -EPIPE; -@@ -138,7 +145,7 @@ static int nbd_co_send_request(BlockDriverState *bs, - if (qiov) { - qio_channel_set_cork(s->ioc, true); - rc = nbd_send_request(s->ioc, request); -- if (rc >= 0) { -+ if (rc >= 0 && !s->quit) { - ret = nbd_wr_syncv(s->ioc, qiov->iov, qiov->niov, request->len, - false); - if (ret != request->len) { -@@ -149,6 +156,9 @@ static int nbd_co_send_request(BlockDriverState *bs, - } else { - rc = nbd_send_request(s->ioc, request); - } -+ if (rc < 0) { -+ s->quit = true; -+ } - qemu_co_mutex_unlock(&s->send_mutex); - return rc; - } -@@ -163,8 +173,7 @@ static void nbd_co_receive_reply(NBDClientSession *s, - /* Wait until we're woken up by nbd_read_reply_entry. */ - qemu_coroutine_yield(); - *reply = s->reply; -- if (reply->handle != request->handle || -- !s->ioc) { -+ if (reply->handle != request->handle || !s->ioc || s->quit) { - reply->error = EIO; - } else { - if (qiov && reply->error == 0) { -diff --git a/block/nbd-client.h b/block/nbd-client.h -index 891ba44a20..9774a8ebbb 100644 ---- a/block/nbd-client.h -+++ b/block/nbd-client.h -@@ -30,6 +30,7 @@ typedef struct NBDClientSession { - - Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; - NBDReply reply; -+ bool quit; - } NBDClientSession; - - NBDClientSession *nbd_get_client_session(BlockDriverState *bs); --- -2.11.0 - diff --git a/debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch b/debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch deleted file mode 100644 index 27421cd..0000000 --- a/debian/patches/extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:20 +0200 -Subject: [PATCH] fix build failure in nbd_read_reply_entry() -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-3-eblake@redhat.com> -Patchwork-id: 76668 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 2/7] fix build failure in nbd_read_reply_entry() -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -From: Igor Mammedov - -travis builds fail at HEAD at rc3 master with - - block/nbd-client.c: In function ‘nbd_read_reply_entry’: - block/nbd-client.c:110:8: error: ‘ret’ may be used uninitialized in this function [-Werror=uninitialized] - -fix it by initializing 'ret' to 0 - -Signed-off-by: Igor Mammedov -Reviewed-by: Eric Blake -Signed-off-by: Peter Maydell -(cherry picked from commit d0a180131c6655487b47ea72e7da0a909a479a3c) -Signed-off-by: Eric Blake -Signed-off-by: Miroslav Rezanina - -Conflicts: - block/nbd-client.c - context ---- - block/nbd-client.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/block/nbd-client.c b/block/nbd-client.c -index 701b4ce2eb..256dabeeef 100644 ---- a/block/nbd-client.c -+++ b/block/nbd-client.c -@@ -69,7 +69,7 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) - { - NBDClientSession *s = opaque; - uint64_t i; -- int ret; -+ int ret = 0; - - while (!s->quit) { - assert(s->reply.handle == 0); --- -2.11.0 - diff --git a/debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch b/debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch deleted file mode 100644 index ec3c61e..0000000 --- a/debian/patches/extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch +++ /dev/null @@ -1,184 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:21 +0200 -Subject: [PATCH] nbd-client: avoid spurious qio_channel_yield() re-entry - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-4-eblake@redhat.com> -Patchwork-id: 76671 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 3/7] nbd-client: avoid spurious qio_channel_yield() re-entry -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -From: Stefan Hajnoczi - -The following scenario leads to an assertion failure in -qio_channel_yield(): - -1. Request coroutine calls qio_channel_yield() successfully when sending - would block on the socket. It is now yielded. -2. nbd_read_reply_entry() calls nbd_recv_coroutines_enter_all() because - nbd_receive_reply() failed. -3. Request coroutine is entered and returns from qio_channel_yield(). - Note that the socket fd handler has not fired yet so - ioc->write_coroutine is still set. -4. Request coroutine attempts to send the request body with nbd_rwv() - but the socket would still block. qio_channel_yield() is called - again and assert(!ioc->write_coroutine) is hit. - -The problem is that nbd_read_reply_entry() does not distinguish between -request coroutines that are waiting to receive a reply and those that -are not. - -This patch adds a per-request bool receiving flag so -nbd_read_reply_entry() can avoid spurious aio_wake() calls. - -Reported-by: Dr. David Alan Gilbert -Signed-off-by: Stefan Hajnoczi -Message-Id: <20170822125113.5025-1-stefanha@redhat.com> -Reviewed-by: Vladimir Sementsov-Ogievskiy -Tested-by: Eric Blake -Reviewed-by: Paolo Bonzini -Signed-off-by: Eric Blake -(cherry picked from commit 40f4a21895b5a7eae4011593837069f63460d983) -Signed-off-by: Miroslav Rezanina ---- - block/nbd-client.c | 35 ++++++++++++++++++++++------------- - block/nbd-client.h | 7 ++++++- - 2 files changed, 28 insertions(+), 14 deletions(-) - -diff --git a/block/nbd-client.c b/block/nbd-client.c -index 256dabeeef..f7bca3f996 100644 ---- a/block/nbd-client.c -+++ b/block/nbd-client.c -@@ -38,8 +38,10 @@ static void nbd_recv_coroutines_enter_all(NBDClientSession *s) - int i; - - for (i = 0; i < MAX_NBD_REQUESTS; i++) { -- if (s->recv_coroutine[i]) { -- aio_co_wake(s->recv_coroutine[i]); -+ NBDClientRequest *req = &s->requests[i]; -+ -+ if (req->coroutine && req->receiving) { -+ aio_co_wake(req->coroutine); - } - } - } -@@ -83,28 +85,28 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) - * one coroutine is called until the reply finishes. - */ - i = HANDLE_TO_INDEX(s, s->reply.handle); -- if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) { -+ if (i >= MAX_NBD_REQUESTS || -+ !s->requests[i].coroutine || -+ !s->requests[i].receiving) { - break; - } - -- /* We're woken up by the recv_coroutine itself. Note that there -+ /* We're woken up again by the request itself. Note that there - * is no race between yielding and reentering read_reply_co. This - * is because: - * -- * - if recv_coroutine[i] runs on the same AioContext, it is only -+ * - if the request runs on the same AioContext, it is only - * entered after we yield - * -- * - if recv_coroutine[i] runs on a different AioContext, reentering -+ * - if the request runs on a different AioContext, reentering - * read_reply_co happens through a bottom half, which can only - * run after we yield. - */ -- aio_co_wake(s->recv_coroutine[i]); -+ aio_co_wake(s->requests[i].coroutine); - qemu_coroutine_yield(); - } - -- if (ret < 0) { -- s->quit = true; -- } -+ s->quit = true; - nbd_recv_coroutines_enter_all(s); - s->read_reply_co = NULL; - } -@@ -123,14 +125,17 @@ static int nbd_co_send_request(BlockDriverState *bs, - s->in_flight++; - - for (i = 0; i < MAX_NBD_REQUESTS; i++) { -- if (s->recv_coroutine[i] == NULL) { -- s->recv_coroutine[i] = qemu_coroutine_self(); -+ if (s->requests[i].coroutine == NULL) { - break; - } - } - - g_assert(qemu_in_coroutine()); - assert(i < MAX_NBD_REQUESTS); -+ -+ s->requests[i].coroutine = qemu_coroutine_self(); -+ s->requests[i].receiving = false; -+ - request->handle = INDEX_TO_HANDLE(s, i); - - if (s->quit) { -@@ -168,10 +173,13 @@ static void nbd_co_receive_reply(NBDClientSession *s, - NBDReply *reply, - QEMUIOVector *qiov) - { -+ int i = HANDLE_TO_INDEX(s, request->handle); - int ret; - - /* Wait until we're woken up by nbd_read_reply_entry. */ -+ s->requests[i].receiving = true; - qemu_coroutine_yield(); -+ s->requests[i].receiving = false; - *reply = s->reply; - if (reply->handle != request->handle || !s->ioc || s->quit) { - reply->error = EIO; -@@ -181,6 +189,7 @@ static void nbd_co_receive_reply(NBDClientSession *s, - true); - if (ret != request->len) { - reply->error = EIO; -+ s->quit = true; - } - } - -@@ -195,7 +204,7 @@ static void nbd_coroutine_end(BlockDriverState *bs, - NBDClientSession *s = nbd_get_client_session(bs); - int i = HANDLE_TO_INDEX(s, request->handle); - -- s->recv_coroutine[i] = NULL; -+ s->requests[i].coroutine = NULL; - - /* Kick the read_reply_co to get the next reply. */ - if (s->read_reply_co) { -diff --git a/block/nbd-client.h b/block/nbd-client.h -index 9774a8ebbb..f97792ff49 100644 ---- a/block/nbd-client.h -+++ b/block/nbd-client.h -@@ -17,6 +17,11 @@ - - #define MAX_NBD_REQUESTS 16 - -+typedef struct { -+ Coroutine *coroutine; -+ bool receiving; /* waiting for read_reply_co? */ -+} NBDClientRequest; -+ - typedef struct NBDClientSession { - QIOChannelSocket *sioc; /* The master data channel */ - QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ -@@ -28,7 +33,7 @@ typedef struct NBDClientSession { - Coroutine *read_reply_co; - int in_flight; - -- Coroutine *recv_coroutine[MAX_NBD_REQUESTS]; -+ NBDClientRequest requests[MAX_NBD_REQUESTS]; - NBDReply reply; - bool quit; - } NBDClientSession; --- -2.11.0 - diff --git a/debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch b/debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch deleted file mode 100644 index 17baf21..0000000 --- a/debian/patches/extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch +++ /dev/null @@ -1,160 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:22 +0200 -Subject: [PATCH] nbd-client: avoid read_reply_co entry if send failed - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-5-eblake@redhat.com> -Patchwork-id: 76674 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 4/7] nbd-client: avoid read_reply_co entry if send failed -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -From: Stefan Hajnoczi - -The following segfault is encountered if the NBD server closes the UNIX -domain socket immediately after negotiation: - - Program terminated with signal SIGSEGV, Segmentation fault. - #0 aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441 - 441 QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines, - (gdb) bt - #0 0x000000d3c01a50f8 in aio_co_schedule (ctx=0x0, co=0xd3c0ff2ef0) at util/async.c:441 - #1 0x000000d3c012fa90 in nbd_coroutine_end (bs=bs@entry=0xd3c0fec650, request=) at block/nbd-client.c:207 - #2 0x000000d3c012fb58 in nbd_client_co_preadv (bs=0xd3c0fec650, offset=0, bytes=, qiov=0x7ffc10a91b20, flags=0) at block/nbd-client.c:237 - #3 0x000000d3c0128e63 in bdrv_driver_preadv (bs=bs@entry=0xd3c0fec650, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=0) at block/io.c:836 - #4 0x000000d3c012c3e0 in bdrv_aligned_preadv (child=child@entry=0xd3c0ff51d0, req=req@entry=0x7f31885d6e90, offset=offset@entry=0, bytes=bytes@entry=512, align=align@entry=1, qiov=qiov@entry=0x7ffc10a91b20, f -+lags=0) at block/io.c:1086 - #5 0x000000d3c012c6b8 in bdrv_co_preadv (child=0xd3c0ff51d0, offset=offset@entry=0, bytes=bytes@entry=512, qiov=qiov@entry=0x7ffc10a91b20, flags=flags@entry=0) at block/io.c:1182 - #6 0x000000d3c011cc17 in blk_co_preadv (blk=0xd3c0ff4f80, offset=0, bytes=512, qiov=0x7ffc10a91b20, flags=0) at block/block-backend.c:1032 - #7 0x000000d3c011ccec in blk_read_entry (opaque=0x7ffc10a91b40) at block/block-backend.c:1079 - #8 0x000000d3c01bbb96 in coroutine_trampoline (i0=, i1=) at util/coroutine-ucontext.c:79 - #9 0x00007f3196cb8600 in __start_context () at /lib64/libc.so.6 - -The problem is that nbd_client_init() uses -nbd_client_attach_aio_context() -> aio_co_schedule(new_context, -client->read_reply_co). Execution of read_reply_co is deferred to a BH -which doesn't run until later. - -In the mean time blk_co_preadv() can be called and nbd_coroutine_end() -calls aio_wake() on read_reply_co. At this point in time -read_reply_co's ctx isn't set because it has never been entered yet. - -This patch simplifies the nbd_co_send_request() -> -nbd_co_receive_reply() -> nbd_coroutine_end() lifecycle to just -nbd_co_send_request() -> nbd_co_receive_reply(). The request is "ended" -if an error occurs at any point. Callers no longer have to invoke -nbd_coroutine_end(). - -This cleanup also eliminates the segfault because we don't call -aio_co_schedule() to wake up s->read_reply_co if sending the request -failed. It is only necessary to wake up s->read_reply_co if a reply was -received. - -Note this only happens with UNIX domain sockets on Linux. It doesn't -seem possible to reproduce this with TCP sockets. - -Suggested-by: Paolo Bonzini -Signed-off-by: Stefan Hajnoczi -Message-Id: <20170829122745.14309-2-stefanha@redhat.com> -Signed-off-by: Eric Blake -(cherry picked from commit 3c2d5183f9fa4eac3d17d841e26da65a0181ae7b) -Signed-off-by: Miroslav Rezanina ---- - block/nbd-client.c | 25 +++++++++---------------- - 1 file changed, 9 insertions(+), 16 deletions(-) - -diff --git a/block/nbd-client.c b/block/nbd-client.c -index f7bca3f996..434acf647f 100644 ---- a/block/nbd-client.c -+++ b/block/nbd-client.c -@@ -139,12 +139,12 @@ static int nbd_co_send_request(BlockDriverState *bs, - request->handle = INDEX_TO_HANDLE(s, i); - - if (s->quit) { -- qemu_co_mutex_unlock(&s->send_mutex); -- return -EIO; -+ rc = -EIO; -+ goto err; - } - if (!s->ioc) { -- qemu_co_mutex_unlock(&s->send_mutex); -- return -EPIPE; -+ rc = -EPIPE; -+ goto err; - } - - if (qiov) { -@@ -161,8 +161,13 @@ static int nbd_co_send_request(BlockDriverState *bs, - } else { - rc = nbd_send_request(s->ioc, request); - } -+ -+err: - if (rc < 0) { - s->quit = true; -+ s->requests[i].coroutine = NULL; -+ s->in_flight--; -+ qemu_co_queue_next(&s->free_sema); - } - qemu_co_mutex_unlock(&s->send_mutex); - return rc; -@@ -196,13 +201,6 @@ static void nbd_co_receive_reply(NBDClientSession *s, - /* Tell the read handler to read another header. */ - s->reply.handle = 0; - } --} -- --static void nbd_coroutine_end(BlockDriverState *bs, -- NBDRequest *request) --{ -- NBDClientSession *s = nbd_get_client_session(bs); -- int i = HANDLE_TO_INDEX(s, request->handle); - - s->requests[i].coroutine = NULL; - -@@ -238,7 +236,6 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, - } else { - nbd_co_receive_reply(client, &request, &reply, qiov); - } -- nbd_coroutine_end(bs, &request); - return -reply.error; - } - -@@ -267,7 +264,6 @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, - } else { - nbd_co_receive_reply(client, &request, &reply, NULL); - } -- nbd_coroutine_end(bs, &request); - return -reply.error; - } - -@@ -301,7 +297,6 @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, - } else { - nbd_co_receive_reply(client, &request, &reply, NULL); - } -- nbd_coroutine_end(bs, &request); - return -reply.error; - } - -@@ -325,7 +320,6 @@ int nbd_client_co_flush(BlockDriverState *bs) - } else { - nbd_co_receive_reply(client, &request, &reply, NULL); - } -- nbd_coroutine_end(bs, &request); - return -reply.error; - } - -@@ -350,7 +344,6 @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) - } else { - nbd_co_receive_reply(client, &request, &reply, NULL); - } -- nbd_coroutine_end(bs, &request); - return -reply.error; - - } --- -2.11.0 - diff --git a/debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch b/debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch deleted file mode 100644 index 1053a7f..0000000 --- a/debian/patches/extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch +++ /dev/null @@ -1,60 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:23 +0200 -Subject: [PATCH] qemu-iotests: improve nbd-fault-injector.py startup protocol - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-6-eblake@redhat.com> -Patchwork-id: 76675 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 5/7] qemu-iotests: improve nbd-fault-injector.py startup protocol -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -From: Stefan Hajnoczi - -Currently 083 waits for the nbd-fault-injector.py server to start up by -looping until netstat shows the TCP listen socket. - -The startup protocol can be simplified by passing a 0 port number to -nbd-fault-injector.py. The kernel will allocate a port in bind(2) and -the final port number can be printed by nbd-fault-injector.py. - -This should make it slightly nicer and less TCP-specific to wait for -server startup. This patch changes nbd-fault-injector.py, the next one -will rewrite server startup in 083. - -Reviewed-by: Eric Blake -Signed-off-by: Stefan Hajnoczi -Message-Id: <20170829122745.14309-3-stefanha@redhat.com> -Signed-off-by: Eric Blake -(cherry picked from commit 6e592fc92234a58c7156c385840633c17dedd24f) -Signed-off-by: Miroslav Rezanina ---- - tests/qemu-iotests/nbd-fault-injector.py | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py -index 6c07191a5a..1c10dcb51c 100755 ---- a/tests/qemu-iotests/nbd-fault-injector.py -+++ b/tests/qemu-iotests/nbd-fault-injector.py -@@ -235,11 +235,15 @@ def open_socket(path): - sock = socket.socket() - sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - sock.bind((host, int(port))) -+ -+ # If given port was 0 the final port number is now available -+ path = '%s:%d' % sock.getsockname() - else: - sock = socket.socket(socket.AF_UNIX) - sock.bind(path) - sock.listen(0) - print 'Listening on %s' % path -+ sys.stdout.flush() # another process may be waiting, show message now - return sock - - def usage(args): --- -2.11.0 - diff --git a/debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch b/debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch deleted file mode 100644 index 4c4d618..0000000 --- a/debian/patches/extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch +++ /dev/null @@ -1,454 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:24 +0200 -Subject: [PATCH] qemu-iotests: test NBD over UNIX domain sockets in 083 - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-7-eblake@redhat.com> -Patchwork-id: 76670 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 6/7] qemu-iotests: test NBD over UNIX domain sockets in 083 -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -From: Stefan Hajnoczi - -083 only tests TCP. Some failures might be specific to UNIX domain -sockets. - -A few adjustments are necessary: - -1. Generating a port number and waiting for server startup is - TCP-specific. Use the new nbd-fault-injector.py startup protocol to - fetch the address. This is a little more elegant because we don't - need netstat anymore. - -2. The NBD filter does not work for the UNIX domain sockets URIs we - generate and must be extended. - -3. Run all tests twice: once for TCP and once for UNIX domain sockets. - -Reviewed-by: Eric Blake -Signed-off-by: Stefan Hajnoczi -Message-Id: <20170829122745.14309-4-stefanha@redhat.com> -Signed-off-by: Eric Blake -(cherry picked from commit 02d2d860d25e439f0e88658c701668ab684568fb) -Signed-off-by: Miroslav Rezanina - -Conflicts: - tests/qemu-iotests/083.out - error message improvements not backported - -Signed-off-by: Eric Blake ---- - tests/qemu-iotests/083 | 136 +++++++++++++++++++++++-------------- - tests/qemu-iotests/083.out | 143 ++++++++++++++++++++++++++++++++++----- - tests/qemu-iotests/common.filter | 4 +- - 3 files changed, 212 insertions(+), 71 deletions(-) - -diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 -index bff9360048..0306f112da 100755 ---- a/tests/qemu-iotests/083 -+++ b/tests/qemu-iotests/083 -@@ -27,6 +27,14 @@ echo "QA output created by $seq" - here=`pwd` - status=1 # failure is the default! - -+_cleanup() -+{ -+ rm -f nbd.sock -+ rm -f nbd-fault-injector.out -+ rm -f nbd-fault-injector.conf -+} -+trap "_cleanup; exit \$status" 0 1 2 3 15 -+ - # get standard environment, filters and checks - . ./common.rc - . ./common.filter -@@ -35,81 +43,105 @@ _supported_fmt generic - _supported_proto nbd - _supported_os Linux - --# Pick a TCP port based on our pid. This way multiple instances of this test --# can run in parallel without conflicting. --choose_tcp_port() { -- echo $((($$ % 31744) + 1024)) # 1024 <= port < 32768 --} -- --wait_for_tcp_port() { -- while ! (netstat --tcp --listening --numeric | \ -- grep "$1.*0\\.0\\.0\\.0:\\*.*LISTEN") >/dev/null 2>&1; do -- sleep 0.1 -+check_disconnect() { -+ local event export_name=foo extra_args nbd_addr nbd_url proto when -+ -+ while true; do -+ case $1 in -+ --classic-negotiation) -+ shift -+ extra_args=--classic-negotiation -+ export_name= -+ ;; -+ --tcp) -+ shift -+ proto=tcp -+ ;; -+ --unix) -+ shift -+ proto=unix -+ ;; -+ *) -+ break -+ ;; -+ esac - done --} - --check_disconnect() { - event=$1 - when=$2 -- negotiation=$3 - echo "=== Check disconnect $when $event ===" - echo - -- port=$(choose_tcp_port) -- - cat > "$TEST_DIR/nbd-fault-injector.conf" <"$TEST_DIR/nbd-fault-injector.out" 2>&1 & -+ -+ # Wait for server to be ready -+ while ! grep -q 'Listening on ' "$TEST_DIR/nbd-fault-injector.out"; do -+ sleep 0.1 -+ done -+ -+ # Extract the final address (port number has now been assigned in tcp case) -+ nbd_addr=$(sed 's/Listening on \(.*\)$/\1/' "$TEST_DIR/nbd-fault-injector.out") -+ -+ if [ "$proto" = "tcp" ]; then -+ nbd_url="nbd+tcp://$nbd_addr/$export_name" -+ else -+ nbd_url="nbd+unix:///$export_name?socket=$nbd_addr" - fi - -- $PYTHON nbd-fault-injector.py $extra_args "127.0.0.1:$port" "$TEST_DIR/nbd-fault-injector.conf" >/dev/null 2>&1 & -- wait_for_tcp_port "127\\.0\\.0\\.1:$port" - $QEMU_IO -c "read 0 512" "$nbd_url" 2>&1 | _filter_qemu_io | _filter_nbd - - echo - } - --for event in neg1 "export" neg2 request reply data; do -- for when in before after; do -- check_disconnect "$event" "$when" -- done -- -- # Also inject short replies from the NBD server -- case "$event" in -- neg1) -- for when in 8 16; do -- check_disconnect "$event" "$when" -- done -- ;; -- "export") -- for when in 4 12 16; do -- check_disconnect "$event" "$when" -+for proto in tcp unix; do -+ for event in neg1 "export" neg2 request reply data; do -+ for when in before after; do -+ check_disconnect "--$proto" "$event" "$when" - done -- ;; -- neg2) -- for when in 8 10; do -- check_disconnect "$event" "$when" -- done -- ;; -- reply) -- for when in 4 8; do -- check_disconnect "$event" "$when" -- done -- ;; -- esac --done - --# Also check classic negotiation without export information --for when in before 8 16 24 28 after; do -- check_disconnect "neg-classic" "$when" --classic-negotiation -+ # Also inject short replies from the NBD server -+ case "$event" in -+ neg1) -+ for when in 8 16; do -+ check_disconnect "--$proto" "$event" "$when" -+ done -+ ;; -+ "export") -+ for when in 4 12 16; do -+ check_disconnect "--$proto" "$event" "$when" -+ done -+ ;; -+ neg2) -+ for when in 8 10; do -+ check_disconnect "--$proto" "$event" "$when" -+ done -+ ;; -+ reply) -+ for when in 4 8; do -+ check_disconnect "--$proto" "$event" "$when" -+ done -+ ;; -+ esac -+ done -+ -+ # Also check classic negotiation without export information -+ for when in before 8 16 24 28 after; do -+ check_disconnect "--$proto" --classic-negotiation "neg-classic" "$when" -+ done - done - - # success, all done -diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out -index 0c13888ba1..7419722cd7 100644 ---- a/tests/qemu-iotests/083.out -+++ b/tests/qemu-iotests/083.out -@@ -1,43 +1,43 @@ - QA output created by 083 - === Check disconnect before neg1 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect after neg1 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect 8 neg1 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect 16 neg1 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect before export === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect after export === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect 4 export === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect 12 export === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect 16 export === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect before neg2 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect after neg2 === - -@@ -45,11 +45,11 @@ read failed: Input/output error - - === Check disconnect 8 neg2 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect 10 neg2 === - --can't open device nbd:127.0.0.1:PORT:exportname=foo -+can't open device nbd+tcp://127.0.0.1:PORT/foo - - === Check disconnect before request === - -@@ -86,23 +86,132 @@ read 512/512 bytes at offset 0 - - === Check disconnect before neg-classic === - --can't open device nbd:127.0.0.1:PORT -+can't open device nbd+tcp://127.0.0.1:PORT/ - - === Check disconnect 8 neg-classic === - --can't open device nbd:127.0.0.1:PORT -+can't open device nbd+tcp://127.0.0.1:PORT/ - - === Check disconnect 16 neg-classic === - --can't open device nbd:127.0.0.1:PORT -+can't open device nbd+tcp://127.0.0.1:PORT/ - - === Check disconnect 24 neg-classic === - --can't open device nbd:127.0.0.1:PORT -+can't open device nbd+tcp://127.0.0.1:PORT/ - - === Check disconnect 28 neg-classic === - --can't open device nbd:127.0.0.1:PORT -+can't open device nbd+tcp://127.0.0.1:PORT/ -+ -+=== Check disconnect after neg-classic === -+ -+read failed: Input/output error -+ -+=== Check disconnect before neg1 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect after neg1 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 8 neg1 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 16 neg1 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect before export === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect after export === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 4 export === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 12 export === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 16 export === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect before neg2 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect after neg2 === -+ -+read failed: Input/output error -+ -+=== Check disconnect 8 neg2 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 10 neg2 === -+ -+can't open device nbd+unix:///foo?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect before request === -+ -+read failed: Input/output error -+ -+=== Check disconnect after request === -+ -+read failed: Input/output error -+ -+=== Check disconnect before reply === -+ -+read failed: Input/output error -+ -+=== Check disconnect after reply === -+ -+read failed: Input/output error -+ -+=== Check disconnect 4 reply === -+ -+read failed: Input/output error -+ -+=== Check disconnect 8 reply === -+ -+read failed: Input/output error -+ -+=== Check disconnect before data === -+ -+read failed: Input/output error -+ -+=== Check disconnect after data === -+ -+read 512/512 bytes at offset 0 -+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -+ -+=== Check disconnect before neg-classic === -+ -+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 8 neg-classic === -+ -+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 16 neg-classic === -+ -+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 24 neg-classic === -+ -+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock -+ -+=== Check disconnect 28 neg-classic === -+ -+can't open device nbd+unix:///?socket=TEST_DIR/nbd.sock - - === Check disconnect after neg-classic === - -diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter -index 104001358b..65ec315f25 100644 ---- a/tests/qemu-iotests/common.filter -+++ b/tests/qemu-iotests/common.filter -@@ -153,9 +153,9 @@ _filter_nbd() - # - # Filter out the TCP port number since this changes between runs. - sed -e '/nbd\/.*\.c:/d' \ -- -e 's#nbd:\(//\)\?127\.0\.0\.1:[0-9]*#nbd:\1127.0.0.1:PORT#g' \ -+ -e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \ - -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \ -- -e 's#\(exportname=foo\|PORT\): Failed to .*$#\1#' -+ -e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#' - } - - # make sure this script returns success --- -2.11.0 - diff --git a/debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch b/debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch deleted file mode 100644 index 84b7815..0000000 --- a/debian/patches/extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch +++ /dev/null @@ -1,45 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eric Blake -Date: Wed, 27 Sep 2017 17:57:25 +0200 -Subject: [PATCH] block/nbd-client: nbd_co_send_request: fix return code - -RH-Author: Eric Blake -Message-id: <20170927175725.20023-8-eblake@redhat.com> -Patchwork-id: 76673 -O-Subject: [RHEV-7.4.z qemu-kvm-rhev PATCH 7/7] block/nbd-client: nbd_co_send_request: fix return code -Bugzilla: 1495474 -RH-Acked-by: Max Reitz -RH-Acked-by: Jeffrey Cody -RH-Acked-by: Stefan Hajnoczi - -From: Vladimir Sementsov-Ogievskiy - -It's incorrect to return success rc >= 0 if we skip qio_channel_writev_all() -call due to s->quit. - -Signed-off-by: Vladimir Sementsov-Ogievskiy -Reviewed-by: Eric Blake -Message-Id: <20170920124507.18841-4-vsementsov@virtuozzo.com> -Signed-off-by: Eric Blake -(cherry picked from commit a693437037328a95d815ad5aec37ac2f8e130e58) -Signed-off-by: Miroslav Rezanina ---- - block/nbd-client.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/block/nbd-client.c b/block/nbd-client.c -index 434acf647f..76789c1829 100644 ---- a/block/nbd-client.c -+++ b/block/nbd-client.c -@@ -156,6 +156,8 @@ static int nbd_co_send_request(BlockDriverState *bs, - if (ret != request->len) { - rc = -EIO; - } -+ } else if (rc >= 0) { -+ rc = -EIO; - } - qio_channel_set_cork(s->ioc, false); - } else { --- -2.11.0 - diff --git a/debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch b/debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch deleted file mode 100644 index 8a20217..0000000 --- a/debian/patches/extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch +++ /dev/null @@ -1,86 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Brijesh Singh -Date: Tue, 15 Aug 2017 12:00:51 -0500 -Subject: [PATCH] target-i386/cpu: Add new EPYC CPU model - -Add a new base CPU model called 'EPYC' to model processors from AMD EPYC -family (which includes EPYC 76xx,75xx,74xx, 73xx and 72xx). - -The following features bits have been added/removed compare to Opteron_G5 - -Added: monitor, movbe, rdrand, mmxext, ffxsr, rdtscp, cr8legacy, osvw, - fsgsbase, bmi1, avx2, smep, bmi2, rdseed, adx, smap, clfshopt, sha - xsaveopt, xsavec, xgetbv1, arat - -Removed: xop, fma4, tbm - -Cc: Paolo Bonzini -Cc: Richard Henderson -Cc: Eduardo Habkost -Cc: Tom Lendacky -Signed-off-by: Brijesh Singh -Message-Id: <20170815170051.127257-1-brijesh.singh@amd.com> -Reviewed-by: Eduardo Habkost -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 44 insertions(+) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index e4a2d5a012..e977c6c616 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -1923,6 +1923,50 @@ static X86CPUDefinition builtin_x86_defs[] = { - .xlevel = 0x8000001A, - .model_id = "AMD Opteron 63xx class CPU", - }, -+ { -+ .name = "EPYC", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_AMD, -+ .family = 23, -+ .model = 1, -+ .stepping = 2, -+ .features[FEAT_1_EDX] = -+ CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | -+ CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | -+ CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | -+ CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | -+ CPUID_VME | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | -+ CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | -+ CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | -+ CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | -+ CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | -+ CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | -+ CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | -+ CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | -+ CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | -+ CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | -+ CPUID_7_0_EBX_SHA_NI, -+ /* Missing: XSAVES (not supported by some Linux versions, -+ * including v4.1 to v4.12). -+ * KVM doesn't yet expose any XSAVES state save component. -+ */ -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | -+ CPUID_XSAVE_XGETBV1, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x8000000A, -+ .model_id = "AMD EPYC Processor", -+ }, - }; - - typedef struct PropValue { --- -2.11.0 - diff --git a/debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch b/debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch deleted file mode 100644 index 665aefe..0000000 --- a/debian/patches/extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch +++ /dev/null @@ -1,78 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Eduardo Habkost -Date: Tue, 9 Jan 2018 13:45:18 -0200 -Subject: [PATCH] i386: Add EPYC-IBPB CPU model - -EPYC-IBPB is a copy of the EPYC CPU model with -just CPUID_8000_0008_EBX_IBPB added. - -Cc: Jiri Denemark -Cc: Tom Lendacky -Cc: Brijesh Singh -Signed-off-by: Eduardo Habkost -Message-Id: <20180109154519.25634-7-ehabkost@redhat.com> -Signed-off-by: Eduardo Habkost ---- - target/i386/cpu.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 46 insertions(+) - -diff --git a/target/i386/cpu.c b/target/i386/cpu.c -index e977c6c616..95e6a1f122 100644 ---- a/target/i386/cpu.c -+++ b/target/i386/cpu.c -@@ -1967,6 +1967,52 @@ static X86CPUDefinition builtin_x86_defs[] = { - .xlevel = 0x8000000A, - .model_id = "AMD EPYC Processor", - }, -+ { -+ .name = "EPYC-IBPB", -+ .level = 0xd, -+ .vendor = CPUID_VENDOR_AMD, -+ .family = 23, -+ .model = 1, -+ .stepping = 2, -+ .features[FEAT_1_EDX] = -+ CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | -+ CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | -+ CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | -+ CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | -+ CPUID_VME | CPUID_FP87, -+ .features[FEAT_1_ECX] = -+ CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | -+ CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | -+ CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | -+ CPUID_EXT_CX16 | CPUID_EXT_FMA | CPUID_EXT_SSSE3 | -+ CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3, -+ .features[FEAT_8000_0001_EDX] = -+ CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | -+ CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | -+ CPUID_EXT2_SYSCALL, -+ .features[FEAT_8000_0001_ECX] = -+ CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | -+ CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | -+ CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, -+ .features[FEAT_8000_0008_EBX] = -+ CPUID_8000_0008_EBX_IBPB, -+ .features[FEAT_7_0_EBX] = -+ CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | -+ CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_RDSEED | -+ CPUID_7_0_EBX_ADX | CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | -+ CPUID_7_0_EBX_SHA_NI, -+ /* Missing: XSAVES (not supported by some Linux versions, -+ * including v4.1 to v4.12). -+ * KVM doesn't yet expose any XSAVES state save component. -+ */ -+ .features[FEAT_XSAVE] = -+ CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | -+ CPUID_XSAVE_XGETBV1, -+ .features[FEAT_6_EAX] = -+ CPUID_6_EAX_ARAT, -+ .xlevel = 0x8000000A, -+ .model_id = "AMD EPYC Processor (with IBPB)", -+ }, - }; - - typedef struct PropValue { --- -2.11.0 - diff --git a/debian/patches/pve/0001-block-file-change-locking-default-to-off.patch b/debian/patches/pve/0001-block-file-change-locking-default-to-off.patch new file mode 100644 index 0000000..370795c --- /dev/null +++ b/debian/patches/pve/0001-block-file-change-locking-default-to-off.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +Date: Wed, 29 Nov 2017 11:11:46 +0100 +Subject: [PATCH] block/file: change locking default to off + +'auto' only checks whether the system generally supports OFD +locks but not whether the storage the file resides on +supports any locking, causing issues with NFS. + +Signed-off-by: Wolfgang Bumiller +--- + block/file-posix.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/block/file-posix.c b/block/file-posix.c +index 36ee89e940..4bff572d07 100644 +--- a/block/file-posix.c ++++ b/block/file-posix.c +@@ -405,7 +405,7 @@ static QemuOptsList raw_runtime_opts = { + { + .name = "locking", + .type = QEMU_OPT_STRING, +- .help = "file locking mode (on/off/auto, default: auto)", ++ .help = "file locking mode (on/off/auto, default: off)", + }, + { + .name = "pr-manager", +@@ -481,7 +481,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, + s->use_lock = false; + break; + case ON_OFF_AUTO_AUTO: +- s->use_lock = qemu_has_ofd_lock(); ++ s->use_lock = false; + break; + default: + abort(); +-- +2.11.0 + diff --git a/debian/patches/pve/0001-fr-ca-keymap-corrections.patch b/debian/patches/pve/0001-fr-ca-keymap-corrections.patch deleted file mode 100644 index acbfd93..0000000 --- a/debian/patches/pve/0001-fr-ca-keymap-corrections.patch +++ /dev/null @@ -1,48 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 9 Dec 2015 14:15:49 +0100 -Subject: [PATCH] fr-ca keymap corrections - ---- - pc-bios/keymaps/fr-ca | 9 +++++++++ - 1 file changed, 9 insertions(+) - -diff --git a/pc-bios/keymaps/fr-ca b/pc-bios/keymaps/fr-ca -index b645208e42..92912409e1 100644 ---- a/pc-bios/keymaps/fr-ca -+++ b/pc-bios/keymaps/fr-ca -@@ -14,22 +14,31 @@ bar 0x29 shift - twosuperior 0x9 altgr - threesuperior 0xa altgr - onequarter 0xb altgr -+minus 0x0c - onehalf 0xc altgr -+equal 0xd - threequarters 0xd altgr - section 0x18 altgr - paragraph 0x19 altgr - bracketleft 0x1a altgr - bracketright 0x1b altgr -+semicolon 0x27 -+colon 0x27 shift - asciitilde 0x27 altgr - braceleft 0x28 altgr -+numbersign 0x29 - braceright 0x2b altgr - less 0x2b - greater 0x2b shift - guillemotleft 0x56 - guillemotright 0x56 shift - degree 0x56 altgr -+comma 0x33 - mu 0x32 altgr -+apostrophe 0x33 shift -+period 0x34 shift - eacute 0x35 -+Eacute 0x35 shift - dead_acute 0x35 altgr - dead_grave 0x28 - dead_circumflex 0x1a --- -2.11.0 - diff --git a/debian/patches/pve/0002-Adjust-network-script-path-to-etc-kvm.patch b/debian/patches/pve/0002-Adjust-network-script-path-to-etc-kvm.patch index ec0ad70..f5bb564 100644 --- a/debian/patches/pve/0002-Adjust-network-script-path-to-etc-kvm.patch +++ b/debian/patches/pve/0002-Adjust-network-script-path-to-etc-kvm.patch @@ -8,10 +8,10 @@ Subject: [PATCH] Adjust network script path to /etc/kvm/ 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/net/net.h b/include/net/net.h -index 99b28d5b38..40c39f00e6 100644 +index 1c55a93588..13ecb9cc8c 100644 --- a/include/net/net.h +++ b/include/net/net.h -@@ -214,8 +214,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); +@@ -220,8 +220,9 @@ void qmp_netdev_add(QDict *qdict, QObject **ret, Error **errp); int net_hub_id_for_client(NetClientState *nc, int *id); NetClientState *net_hub_port_find(int hub_id); diff --git a/debian/patches/pve/0003-qemu-img-return-success-on-info-without-snapshots.patch b/debian/patches/pve/0003-qemu-img-return-success-on-info-without-snapshots.patch index cd5ed3d..71d8588 100644 --- a/debian/patches/pve/0003-qemu-img-return-success-on-info-without-snapshots.patch +++ b/debian/patches/pve/0003-qemu-img-return-success-on-info-without-snapshots.patch @@ -8,12 +8,12 @@ Subject: [PATCH] qemu-img: return success on info without snapshots 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c -index e4a2686f56..c7804d63ee 100644 +index 68b375f998..f8a39dd195 100644 --- a/qemu-img.c +++ b/qemu-img.c -@@ -2596,7 +2596,8 @@ static int img_info(int argc, char **argv) - - list = collect_image_info_list(image_opts, filename, fmt, chain); +@@ -2594,7 +2594,8 @@ static int img_info(int argc, char **argv) + list = collect_image_info_list(image_opts, filename, fmt, chain, + force_share); if (!list) { - return 1; + // return success if snapshot does not exists diff --git a/debian/patches/pve/0004-use-kvm-by-default.patch b/debian/patches/pve/0004-use-kvm-by-default.patch index be95fa4..3b859c9 100644 --- a/debian/patches/pve/0004-use-kvm-by-default.patch +++ b/debian/patches/pve/0004-use-kvm-by-default.patch @@ -4,24 +4,24 @@ Date: Wed, 9 Dec 2015 14:27:05 +0100 Subject: [PATCH] use kvm by default --- - accel.c | 4 ++-- + accel/accel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) -diff --git a/accel.c b/accel.c -index 664bb88422..ddb23a3f1d 100644 ---- a/accel.c -+++ b/accel.c -@@ -87,8 +87,8 @@ void configure_accelerator(MachineState *ms) +diff --git a/accel/accel.c b/accel/accel.c +index 8ae40e1e13..df21981026 100644 +--- a/accel/accel.c ++++ b/accel/accel.c +@@ -79,8 +79,8 @@ void configure_accelerator(MachineState *ms) - p = qemu_opt_get(qemu_get_machine_opts(), "accel"); - if (p == NULL) { + accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); + if (accel == NULL) { - /* Use the default "accelerator", tcg */ -- p = "tcg"; +- accel = "tcg"; + /* Use the default "accelerator", kvm */ -+ p = "kvm"; ++ accel = "kvm"; } - while (!accel_initialised && *p != '\0') { + p = accel; -- 2.11.0 diff --git a/debian/patches/pve/0005-virtio-balloon-fix-query.patch b/debian/patches/pve/0005-virtio-balloon-fix-query.patch index deb2109..58e0202 100644 --- a/debian/patches/pve/0005-virtio-balloon-fix-query.patch +++ b/debian/patches/pve/0005-virtio-balloon-fix-query.patch @@ -8,14 +8,14 @@ command. --- hmp.c | 30 +++++++++++++++++++++++++++++- hw/virtio/virtio-balloon.c | 33 +++++++++++++++++++++++++++++++-- - qapi-schema.json | 23 +++++++++++++++++++++-- - 3 files changed, 81 insertions(+), 5 deletions(-) + qapi-schema.json | 22 +++++++++++++++++++++- + 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/hmp.c b/hmp.c -index edb8970461..904542d026 100644 +index 35a7041824..4e1d571003 100644 --- a/hmp.c +++ b/hmp.c -@@ -723,7 +723,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) +@@ -789,7 +789,35 @@ void hmp_info_balloon(Monitor *mon, const QDict *qdict) return; } @@ -53,10 +53,10 @@ index edb8970461..904542d026 100644 qapi_free_BalloonInfo(info); } diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c -index a705e0ec55..158e13e795 100644 +index 37cde38982..1feaf77223 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c -@@ -379,8 +379,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f, +@@ -380,8 +380,37 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f, static void virtio_balloon_stat(void *opaque, BalloonInfo *info) { VirtIOBalloon *dev = opaque; @@ -97,14 +97,13 @@ index a705e0ec55..158e13e795 100644 static void virtio_balloon_to_target(void *opaque, ram_addr_t target) diff --git a/qapi-schema.json b/qapi-schema.json -index 250e4dc49b..f38b85bf6a 100644 +index 18457954a8..ebc22fe5a6 100644 --- a/qapi-schema.json +++ b/qapi-schema.json -@@ -1900,10 +1900,29 @@ +@@ -623,10 +623,30 @@ # # @actual: the number of bytes the balloon currently contains # --# Since: 0.14.0 +# @last_update: time when stats got updated from guest +# +# @mem_swapped_in: number of pages swapped in within the guest @@ -112,7 +111,7 @@ index 250e4dc49b..f38b85bf6a 100644 +# @mem_swapped_out: number of pages swapped out within the guest +# +# @major_page_faults: number of major page faults within the guest - # ++# +# @minor_page_faults: number of minor page faults within the guest +# +# @free_mem: amount of memory (in bytes) free in the guest @@ -120,8 +119,9 @@ index 250e4dc49b..f38b85bf6a 100644 +# @total_mem: amount of memory (in bytes) visible to the guest +# +# @max_mem: amount of memory (in bytes) assigned to the guest -+# -+# Since: 0.14.0 ++# + # Since: 0.14.0 + # ## -{ 'struct': 'BalloonInfo', 'data': {'actual': 'int' } } +{ 'struct': 'BalloonInfo', diff --git a/debian/patches/pve/0006-set-the-CPU-model-to-kvm64-32-instead-of-qemu64-32.patch b/debian/patches/pve/0006-set-the-CPU-model-to-kvm64-32-instead-of-qemu64-32.patch index 25dd6fc..b1952b5 100644 --- a/debian/patches/pve/0006-set-the-CPU-model-to-kvm64-32-instead-of-qemu64-32.patch +++ b/debian/patches/pve/0006-set-the-CPU-model-to-kvm64-32-instead-of-qemu64-32.patch @@ -4,25 +4,25 @@ Date: Wed, 9 Dec 2015 14:30:21 +0100 Subject: [PATCH] set the CPU model to kvm64/32 instead of qemu64/32 --- - hw/i386/pc.c | 4 ++-- + target/i386/cpu.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) -diff --git a/hw/i386/pc.c b/hw/i386/pc.c -index 610050eb4f..e96901435a 100644 ---- a/hw/i386/pc.c -+++ b/hw/i386/pc.c -@@ -1151,9 +1151,9 @@ void pc_cpus_init(PCMachineState *pcms) - /* init CPUs */ - if (machine->cpu_model == NULL) { +diff --git a/target/i386/cpu.h b/target/i386/cpu.h +index f3d0ebb673..660e42977b 100644 +--- a/target/i386/cpu.h ++++ b/target/i386/cpu.h +@@ -1521,9 +1521,9 @@ uint64_t cpu_get_tsc(CPUX86State *env); + #define X86_CPU_TYPE_NAME(name) (name X86_CPU_TYPE_SUFFIX) + #ifdef TARGET_X86_64 -- machine->cpu_model = "qemu64"; -+ machine->cpu_model = "kvm64"; +-#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu64") ++#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("kvm64") #else -- machine->cpu_model = "qemu32"; -+ machine->cpu_model = "kvm32"; +-#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu32") ++#define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("kvm32") #endif - } + #define cpu_signal_handler cpu_x86_signal_handler -- 2.11.0 diff --git a/debian/patches/pve/0007-qapi-modify-query-machines.patch b/debian/patches/pve/0007-qapi-modify-query-machines.patch index 09cb67d..7f38023 100644 --- a/debian/patches/pve/0007-qapi-modify-query-machines.patch +++ b/debian/patches/pve/0007-qapi-modify-query-machines.patch @@ -10,10 +10,10 @@ provide '*is-current' in MachineInfo struct 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/qapi-schema.json b/qapi-schema.json -index f38b85bf6a..51e150c6c1 100644 +index ebc22fe5a6..8f436ba1f3 100644 --- a/qapi-schema.json +++ b/qapi-schema.json -@@ -4242,6 +4242,8 @@ +@@ -1914,6 +1914,8 @@ # # @is-default: whether the machine is default # @@ -22,7 +22,7 @@ index f38b85bf6a..51e150c6c1 100644 # @cpu-max: maximum number of CPUs supported by the machine type # (since 1.5.0) # -@@ -4251,7 +4253,7 @@ +@@ -1923,7 +1925,7 @@ ## { 'struct': 'MachineInfo', 'data': { 'name': 'str', '*alias': 'str', @@ -32,10 +32,10 @@ index f38b85bf6a..51e150c6c1 100644 ## diff --git a/vl.c b/vl.c -index b719cc432e..46de1b9087 100644 +index 1ad1c04637..2e0fe15978 100644 --- a/vl.c +++ b/vl.c -@@ -1518,6 +1518,11 @@ MachineInfoList *qmp_query_machines(Error **errp) +@@ -1604,6 +1604,11 @@ MachineInfoList *qmp_query_machines(Error **errp) info->cpu_max = !mc->max_cpus ? 1 : mc->max_cpus; info->hotpluggable_cpus = mc->has_hotpluggable_cpus; diff --git a/debian/patches/pve/0008-qapi-modify-spice-query.patch b/debian/patches/pve/0008-qapi-modify-spice-query.patch index e73d319..fd0b66c 100644 --- a/debian/patches/pve/0008-qapi-modify-spice-query.patch +++ b/debian/patches/pve/0008-qapi-modify-spice-query.patch @@ -5,15 +5,15 @@ Subject: [PATCH] qapi: modify spice query Provide the last ticket in the SpiceInfo struct optionally. --- - qapi-schema.json | 3 +++ - ui/spice-core.c | 5 +++++ + qapi/ui.json | 3 +++ + ui/spice-core.c | 5 +++++ 2 files changed, 8 insertions(+) -diff --git a/qapi-schema.json b/qapi-schema.json -index 51e150c6c1..1b14ff2476 100644 ---- a/qapi-schema.json -+++ b/qapi-schema.json -@@ -1841,11 +1841,14 @@ +diff --git a/qapi/ui.json b/qapi/ui.json +index 07b468f625..78c906ddcf 100644 +--- a/qapi/ui.json ++++ b/qapi/ui.json +@@ -199,11 +199,14 @@ # # @channels: a list of @SpiceChannel for each active spice channel # @@ -29,10 +29,10 @@ index 51e150c6c1..1b14ff2476 100644 ## diff --git a/ui/spice-core.c b/ui/spice-core.c -index 804abc5c0f..4a417310d3 100644 +index ea04dc69b5..05f5958b14 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c -@@ -552,6 +552,11 @@ SpiceInfo *qmp_query_spice(Error **errp) +@@ -551,6 +551,11 @@ SpiceInfo *qmp_query_spice(Error **errp) micro = SPICE_SERVER_VERSION & 0xff; info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro); diff --git a/debian/patches/pve/0009-ui-spice-default-to-pve-certs-unless-otherwise-speci.patch b/debian/patches/pve/0009-ui-spice-default-to-pve-certs-unless-otherwise-speci.patch index 402854b..25b66dd 100644 --- a/debian/patches/pve/0009-ui-spice-default-to-pve-certs-unless-otherwise-speci.patch +++ b/debian/patches/pve/0009-ui-spice-default-to-pve-certs-unless-otherwise-speci.patch @@ -8,10 +8,10 @@ Subject: [PATCH] ui/spice: default to pve certs unless otherwise specified 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/ui/spice-core.c b/ui/spice-core.c -index 4a417310d3..af1dc8c653 100644 +index 05f5958b14..6e1a46d1e5 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c -@@ -685,32 +685,35 @@ void qemu_spice_init(void) +@@ -684,32 +684,35 @@ void qemu_spice_init(void) if (tls_port) { x509_dir = qemu_opt_get(opts, "x509-dir"); diff --git a/debian/patches/pve/0010-internal-snapshot-async.patch b/debian/patches/pve/0010-internal-snapshot-async.patch index 28a637b..4244b68 100644 --- a/debian/patches/pve/0010-internal-snapshot-async.patch +++ b/debian/patches/pve/0010-internal-snapshot-async.patch @@ -4,37 +4,45 @@ Date: Wed, 9 Dec 2015 16:04:32 +0100 Subject: [PATCH] internal snapshot async --- - Makefile.objs | 1 + - hmp-commands-info.hx | 13 ++ - hmp-commands.hx | 32 +++ - hmp.c | 57 ++++++ - hmp.h | 5 + - include/sysemu/sysemu.h | 5 +- - migration/savevm.c | 12 +- - qapi-schema.json | 68 +++++++ - qemu-options.hx | 13 ++ - savevm-async.c | 523 ++++++++++++++++++++++++++++++++++++++++++++++++ - vl.c | 8 + - 11 files changed, 730 insertions(+), 7 deletions(-) + Makefile.objs | 2 +- + hmp-commands-info.hx | 13 ++ + hmp-commands.hx | 32 +++ + hmp.c | 57 +++++ + hmp.h | 5 + + include/migration/snapshot.h | 1 + + qapi-schema.json | 32 +++ + qapi/migration.json | 34 +++ + qemu-options.hx | 13 ++ + savevm-async.c | 524 +++++++++++++++++++++++++++++++++++++++++++ + vl.c | 10 + + 11 files changed, 722 insertions(+), 1 deletion(-) create mode 100644 savevm-async.c diff --git a/Makefile.objs b/Makefile.objs -index 6167e7b17d..fbfbbb7f70 100644 +index 285c6f3c15..686247b556 100644 --- a/Makefile.objs +++ b/Makefile.objs -@@ -50,6 +50,7 @@ common-obj-$(CONFIG_LINUX) += fsdev/ - - common-obj-y += migration/ - common-obj-y += page_cache.o #aio.o +@@ -41,6 +41,7 @@ io-obj-y = io/ + ifeq ($(CONFIG_SOFTMMU),y) + common-obj-y = blockdev.o blockdev-nbd.o block/ + common-obj-y += bootdevice.o iothread.o +common-obj-y += savevm-async.o + common-obj-y += net/ + common-obj-y += qdev-monitor.o device-hotplug.o + common-obj-$(CONFIG_WIN32) += os-win32.o +@@ -49,7 +50,6 @@ common-obj-$(CONFIG_POSIX) += os-posix.o + common-obj-$(CONFIG_LINUX) += fsdev/ - common-obj-$(CONFIG_SPICE) += spice-qemu-char.o + common-obj-y += migration/ +- + common-obj-y += audio/ + common-obj-y += hw/ diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx -index a53f105c52..5fc57a2210 100644 +index 54c3e5eac6..3bf69a193c 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx -@@ -560,6 +560,19 @@ Show current migration xbzrle cache size. +@@ -566,6 +566,19 @@ Show current migration xbzrle cache size. ETEXI { @@ -55,10 +63,10 @@ index a53f105c52..5fc57a2210 100644 .args_type = "", .params = "", diff --git a/hmp-commands.hx b/hmp-commands.hx -index 88192817b2..58940a762b 100644 +index 4afd57cf5f..b35bc6ab6c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx -@@ -1777,3 +1777,35 @@ ETEXI +@@ -1873,3 +1873,35 @@ ETEXI STEXI @end table ETEXI @@ -95,11 +103,11 @@ index 88192817b2..58940a762b 100644 + .cmd = hmp_savevm_end, + }, diff --git a/hmp.c b/hmp.c -index 904542d026..f725d061e6 100644 +index 4e1d571003..b9ade681f0 100644 --- a/hmp.c +++ b/hmp.c -@@ -2207,6 +2207,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) - qapi_free_MemoryDeviceInfoList(info_list); +@@ -2486,6 +2486,63 @@ void hmp_info_memory_devices(Monitor *mon, const QDict *qdict) + hmp_handle_error(mon, &err); } +void hmp_savevm_start(Monitor *mon, const QDict *qdict) @@ -163,7 +171,7 @@ index 904542d026..f725d061e6 100644 { IOThreadInfoList *info_list = qmp_query_iothreads(NULL); diff --git a/hmp.h b/hmp.h -index 799fd371fa..0497afbf65 100644 +index a6f56b1f29..45ada581b6 100644 --- a/hmp.h +++ b/hmp.h @@ -26,6 +26,7 @@ void hmp_info_status(Monitor *mon, const QDict *qdict); @@ -174,7 +182,7 @@ index 799fd371fa..0497afbf65 100644 void hmp_info_migrate(Monitor *mon, const QDict *qdict); void hmp_info_migrate_capabilities(Monitor *mon, const QDict *qdict); void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict); -@@ -92,6 +93,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict); +@@ -97,6 +98,10 @@ void hmp_netdev_add(Monitor *mon, const QDict *qdict); void hmp_netdev_del(Monitor *mon, const QDict *qdict); void hmp_getfd(Monitor *mon, const QDict *qdict); void hmp_closefd(Monitor *mon, const QDict *qdict); @@ -185,97 +193,65 @@ index 799fd371fa..0497afbf65 100644 void hmp_sendkey(Monitor *mon, const QDict *qdict); void hmp_screendump(Monitor *mon, const QDict *qdict); void hmp_nbd_server_start(Monitor *mon, const QDict *qdict); -diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h -index 576c7ce640..74623de16c 100644 ---- a/include/sysemu/sysemu.h -+++ b/include/sysemu/sysemu.h -@@ -78,6 +78,7 @@ void qemu_remove_machine_init_done_notifier(Notifier *notify); - void hmp_savevm(Monitor *mon, const QDict *qdict); - int save_vmstate(Monitor *mon, const char *name); - int load_vmstate(const char *name); -+int load_state_from_blockdev(const char *filename); - void hmp_delvm(Monitor *mon, const QDict *qdict); - void hmp_info_snapshots(Monitor *mon, const QDict *qdict); - -@@ -105,13 +106,13 @@ enum qemu_vm_cmd { - #define MAX_VM_CMD_PACKAGED_SIZE (1ul << 24) - - bool qemu_savevm_state_blocked(Error **errp); --void qemu_savevm_state_begin(QEMUFile *f, -+int qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params); - void qemu_savevm_state_header(QEMUFile *f); - int qemu_savevm_state_iterate(QEMUFile *f, bool postcopy); - void qemu_savevm_state_cleanup(void); - void qemu_savevm_state_complete_postcopy(QEMUFile *f); --void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only); -+int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only); - void qemu_savevm_state_pending(QEMUFile *f, uint64_t max_size, - uint64_t *res_non_postcopiable, - uint64_t *res_postcopiable); -diff --git a/migration/savevm.c b/migration/savevm.c -index 3b19a4a274..feb0dc6834 100644 ---- a/migration/savevm.c -+++ b/migration/savevm.c -@@ -970,11 +970,11 @@ void qemu_savevm_state_header(QEMUFile *f) - - } - --void qemu_savevm_state_begin(QEMUFile *f, -+int qemu_savevm_state_begin(QEMUFile *f, - const MigrationParams *params) - { - SaveStateEntry *se; -- int ret; -+ int ret = 0; - - trace_savevm_state_begin(); - QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { -@@ -1002,6 +1002,7 @@ void qemu_savevm_state_begin(QEMUFile *f, - break; - } - } -+ return ret; - } - - /* -@@ -1105,7 +1106,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) - qemu_fflush(f); - } - --void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) -+int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) - { - QJSON *vmdesc; - int vmdesc_len; -@@ -1139,12 +1140,12 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) - save_section_footer(f, se); - if (ret < 0) { - qemu_file_set_error(f, ret); -- return; -+ return ret; - } - } +diff --git a/include/migration/snapshot.h b/include/migration/snapshot.h +index c85b6ec75b..4411b7121d 100644 +--- a/include/migration/snapshot.h ++++ b/include/migration/snapshot.h +@@ -17,5 +17,6 @@ - if (iterable_only) { -- return; -+ return ret; - } + int save_snapshot(const char *name, Error **errp); + int load_snapshot(const char *name, Error **errp); ++int load_snapshot_from_blockdev(const char *filename, Error **errp); - vmdesc = qjson_new(); -@@ -1191,6 +1192,7 @@ void qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) - qjson_destroy(vmdesc); - - qemu_fflush(f); -+ return qemu_file_get_error(f); - } - - /* Give an estimate of the amount left to be transferred, + #endif diff --git a/qapi-schema.json b/qapi-schema.json -index 1b14ff2476..361700d37c 100644 +index 8f436ba1f3..348b527681 100644 --- a/qapi-schema.json +++ b/qapi-schema.json -@@ -723,6 +723,40 @@ +@@ -2439,6 +2439,38 @@ + { 'command': 'query-target', 'returns': 'TargetInfo' } + + ## ++# @savevm-start: ++# ++# Prepare for snapshot and halt VM. Save VM state to statefile. ++# ++## ++{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } } ++ ++## ++# @snapshot-drive: ++# ++# Create an internal drive snapshot. ++# ++## ++{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } } ++ ++## ++# @delete-drive-snapshot: ++# ++# Delete a drive snapshot. ++# ++## ++{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } } ++ ++## ++# @savevm-end: ++# ++# Resume VM after a snapshot. ++# ++## ++{ 'command': 'savevm-end' } ++ ++## + # @AcpiTableOptions: + # + # Specify an ACPI table on the command line to load. +diff --git a/qapi/migration.json b/qapi/migration.json +index 03f57c9616..9ae55b81a2 100644 +--- a/qapi/migration.json ++++ b/qapi/migration.json +@@ -170,6 +170,40 @@ '*error-desc': 'str'} } ## @@ -316,55 +292,11 @@ index 1b14ff2476..361700d37c 100644 # @query-migrate: # # Returns information about current migration process. If migration -@@ -4735,9 +4769,43 @@ - # - # Since: 1.2.0 - ## -+ - { 'command': 'query-target', 'returns': 'TargetInfo' } - - ## -+# @savevm-start: -+# -+# Prepare for snapshot and halt VM. Save VM state to statefile. -+# -+## -+{ 'command': 'savevm-start', 'data': { '*statefile': 'str' } } -+ -+## -+# @snapshot-drive: -+# -+# Create an internal drive snapshot. -+# -+## -+{ 'command': 'snapshot-drive', 'data': { 'device': 'str', 'name': 'str' } } -+ -+## -+# @delete-drive-snapshot: -+# -+# Delete a drive snapshot. -+# -+## -+{ 'command': 'delete-drive-snapshot', 'data': { 'device': 'str', 'name': 'str' } } -+ -+## -+# @savevm-end: -+# -+# Resume VM after a snapshot. -+# -+## -+{ 'command': 'savevm-end' } -+ -+ -+## - # @QKeyCode: - # - # An enumeration of key name. diff --git a/qemu-options.hx b/qemu-options.hx -index 48dfffd86a..cbcb27da9a 100644 +index 57f2c6a75f..7c054af8f9 100644 --- a/qemu-options.hx +++ b/qemu-options.hx -@@ -3396,6 +3396,19 @@ STEXI +@@ -3698,6 +3698,19 @@ STEXI Start right away with a saved state (@code{loadvm} in monitor) ETEXI @@ -386,28 +318,23 @@ index 48dfffd86a..cbcb27da9a 100644 "-daemonize daemonize QEMU after initializing\n", QEMU_ARCH_ALL) diff --git a/savevm-async.c b/savevm-async.c new file mode 100644 -index 0000000000..5fcb56d373 +index 0000000000..897134ab5a --- /dev/null +++ b/savevm-async.c -@@ -0,0 +1,523 @@ +@@ -0,0 +1,524 @@ +#include "qemu/osdep.h" -+#include "qemu-common.h" ++#include "migration/migration.h" ++#include "migration/savevm.h" ++#include "migration/snapshot.h" ++#include "migration/global_state.h" ++#include "migration/ram.h" ++#include "migration/qemu-file.h" +#include "qapi/qmp/qerror.h" -+#include "qemu/error-report.h" +#include "sysemu/sysemu.h" +#include "qmp-commands.h" -+#include "qemu-options.h" -+#include "migration/qemu-file.h" -+#include "qom/qom-qobject.h" -+#include "migration/migration.h" -+#include "block/snapshot.h" -+#include "block/qapi.h" +#include "block/block.h" -+#include "qemu/timer.h" +#include "sysemu/block-backend.h" +#include "qapi/qmp/qstring.h" -+#include "qemu/rcu.h" -+#include "qemu/thread.h" +#include "qemu/cutils.h" + +/* #define DEBUG_SAVEVM_STATE */ @@ -498,7 +425,7 @@ index 0000000000..5fcb56d373 + * note: bdrv_read() need whole blocks, so we round up + */ + size_t size = (snap_state.bs_pos + BDRV_SECTOR_SIZE) & BDRV_SECTOR_MASK; -+ blk_truncate(snap_state.target, size, NULL); ++ blk_truncate(snap_state.target, size, PREALLOC_MODE_OFF, NULL); + blk_op_unblock_all(snap_state.target, snap_state.blocker); + error_free(snap_state.blocker); + snap_state.blocker = NULL; @@ -578,20 +505,17 @@ index 0000000000..5fcb56d373 +{ + int ret; + int64_t maxlen; -+ MigrationParams params = { -+ .blk = 0, -+ .shared = 0 -+ }; + + snap_state.state = SAVE_STATE_ACTIVE; + + qemu_mutex_unlock_iothread(); + qemu_savevm_state_header(snap_state.file); -+ ret = qemu_savevm_state_begin(snap_state.file, ¶ms); ++ qemu_savevm_state_setup(snap_state.file); ++ ret = qemu_file_get_error(snap_state.file); + qemu_mutex_lock_iothread(); + + if (ret < 0) { -+ save_snapshot_error("qemu_savevm_state_begin failed"); ++ save_snapshot_error("qemu_savevm_state_setup failed"); + return; + } + @@ -613,7 +537,16 @@ index 0000000000..5fcb56d373 + if (store_and_stop()) + break; + DPRINTF("savevm inerate finished\n"); -+ qemu_savevm_state_complete_precopy(snap_state.file, false); ++ /* upstream made the return value here inconsistent ++ * (-1 instead of 'ret' in one case and 0 after flush which can ++ * still set a file error...) ++ */ ++ (void)qemu_savevm_state_complete_precopy(snap_state.file, false, false); ++ ret = qemu_file_get_error(snap_state.file); ++ if (ret < 0) { ++ save_snapshot_error("qemu_savevm_state_iterate error %d", ret); ++ break; ++ } + DPRINTF("save complete\n"); + save_snapshot_completed(); + break; @@ -867,7 +800,7 @@ index 0000000000..5fcb56d373 + .get_buffer = loadstate_get_buffer, +}; + -+int load_state_from_blockdev(const char *filename) ++int load_snapshot_from_blockdev(const char *filename, Error **errp) +{ + BlockBackend *be; + Error *local_err = NULL; @@ -879,7 +812,7 @@ index 0000000000..5fcb56d373 + be = blk_new_open(filename, NULL, NULL, 0, &local_err); + + if (!be) { -+ error_report("Could not open VM state file"); ++ error_setg(errp, "Could not open VM state file"); + goto the_end; + } + @@ -889,17 +822,17 @@ index 0000000000..5fcb56d373 + /* restore the VM state */ + f = qemu_fopen_ops(be, &loadstate_file_ops); + if (!f) { -+ error_report("Could not open VM state file"); ++ error_setg(errp, "Could not open VM state file"); + goto the_end; + } + -+ qemu_system_reset(VMRESET_SILENT); ++ qemu_system_reset(SHUTDOWN_CAUSE_NONE); + ret = qemu_loadvm_state(f); + + qemu_fclose(f); + migration_incoming_state_destroy(); + if (ret < 0) { -+ error_report("Error %d while loading VM state", ret); ++ error_setg_errno(errp, -ret, "Error while loading VM state"); + goto the_end; + } + @@ -914,10 +847,10 @@ index 0000000000..5fcb56d373 + return ret; +} diff --git a/vl.c b/vl.c -index 46de1b9087..2132a77129 100644 +index 2e0fe15978..1bfbe95b22 100644 --- a/vl.c +++ b/vl.c -@@ -2960,6 +2960,7 @@ int main(int argc, char **argv, char **envp) +@@ -3109,6 +3109,7 @@ int main(int argc, char **argv, char **envp) int optind; const char *optarg; const char *loadvm = NULL; @@ -925,7 +858,7 @@ index 46de1b9087..2132a77129 100644 MachineClass *machine_class; const char *cpu_model; const char *vga_model = NULL; -@@ -3635,6 +3636,9 @@ int main(int argc, char **argv, char **envp) +@@ -3785,6 +3786,9 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_loadvm: loadvm = optarg; break; @@ -935,12 +868,14 @@ index 46de1b9087..2132a77129 100644 case QEMU_OPTION_full_screen: full_screen = 1; break; -@@ -4693,6 +4697,10 @@ int main(int argc, char **argv, char **envp) - if (load_vmstate(loadvm) < 0) { +@@ -4891,6 +4895,12 @@ int main(int argc, char **argv, char **envp) + error_report_err(local_err); autostart = 0; } + } else if (loadstate) { -+ if (load_state_from_blockdev(loadstate) < 0) { ++ Error *local_err = NULL; ++ if (load_snapshot_from_blockdev(loadstate, &local_err) < 0) { ++ error_report_err(local_err); + autostart = 0; + } } diff --git a/debian/patches/pve/0011-convert-savevm-async-to-threads.patch b/debian/patches/pve/0011-convert-savevm-async-to-threads.patch index 67d09b2..bacbd91 100644 --- a/debian/patches/pve/0011-convert-savevm-async-to-threads.patch +++ b/debian/patches/pve/0011-convert-savevm-async-to-threads.patch @@ -4,14 +4,14 @@ Date: Tue, 8 Nov 2016 11:13:06 +0100 Subject: [PATCH] convert savevm-async to threads --- - savevm-async.c | 144 +++++++++++++++++++++++++++++++++++---------------------- - 1 file changed, 88 insertions(+), 56 deletions(-) + savevm-async.c | 143 +++++++++++++++++++++++++++++++++++---------------------- + 1 file changed, 87 insertions(+), 56 deletions(-) diff --git a/savevm-async.c b/savevm-async.c -index 5fcb56d373..97d51d3edd 100644 +index 897134ab5a..96523c88ae 100644 --- a/savevm-async.c +++ b/savevm-async.c -@@ -48,6 +48,8 @@ static struct SnapshotState { +@@ -43,6 +43,8 @@ static struct SnapshotState { int saved_vm_running; QEMUFile *file; int64_t total_time; @@ -20,7 +20,7 @@ index 5fcb56d373..97d51d3edd 100644 } snap_state; SaveVMInfo *qmp_query_savevm(Error **errp) -@@ -135,19 +137,6 @@ static void save_snapshot_error(const char *fmt, ...) +@@ -130,19 +132,6 @@ static void save_snapshot_error(const char *fmt, ...) g_free (msg); snap_state.state = SAVE_STATE_ERROR; @@ -40,7 +40,7 @@ index 5fcb56d373..97d51d3edd 100644 } static int block_state_close(void *opaque) -@@ -156,51 +145,90 @@ static int block_state_close(void *opaque) +@@ -151,48 +140,86 @@ static int block_state_close(void *opaque) return blk_flush(snap_state.target); } @@ -125,29 +125,25 @@ index 5fcb56d373..97d51d3edd 100644 { int ret; int64_t maxlen; -+ - MigrationParams params = { - .blk = 0, - .shared = 0 - }; - snap_state.state = SAVE_STATE_ACTIVE; + rcu_register_thread(); - qemu_mutex_unlock_iothread(); qemu_savevm_state_header(snap_state.file); - ret = qemu_savevm_state_begin(snap_state.file, ¶ms); + qemu_savevm_state_setup(snap_state.file); + ret = qemu_file_get_error(snap_state.file); - qemu_mutex_lock_iothread(); if (ret < 0) { - save_snapshot_error("qemu_savevm_state_begin failed"); + save_snapshot_error("qemu_savevm_state_setup failed"); - return; + rcu_unregister_thread(); + return NULL; } while (snap_state.state == SAVE_STATE_ACTIVE) { -@@ -209,41 +237,43 @@ static void process_savevm_co(void *opaque) +@@ -201,17 +228,30 @@ static void process_savevm_co(void *opaque) qemu_savevm_state_pending(snap_state.file, 0, &pend_nonpost, &pend_post); pending_size = pend_post + pend_nonpost; @@ -185,7 +181,12 @@ index 5fcb56d373..97d51d3edd 100644 + break; + } DPRINTF("savevm inerate finished\n"); - qemu_savevm_state_complete_precopy(snap_state.file, false); + /* upstream made the return value here inconsistent + * (-1 instead of 'ret' in one case and 0 after flush which can +@@ -223,28 +263,17 @@ static void process_savevm_co(void *opaque) + save_snapshot_error("qemu_savevm_state_iterate error %d", ret); + break; + } + qemu_savevm_state_cleanup(); DPRINTF("save complete\n"); - save_snapshot_completed(); @@ -216,7 +217,7 @@ index 5fcb56d373..97d51d3edd 100644 } static const QEMUFileOps block_file_ops = { -@@ -306,8 +336,10 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp) +@@ -307,8 +336,10 @@ void qmp_savevm_start(bool has_statefile, const char *statefile, Error **errp) error_setg(&snap_state.blocker, "block device is in use by savevm"); blk_op_block_all(snap_state.target, snap_state.blocker); diff --git a/debian/patches/pve/0012-qmp-add-get_link_status.patch b/debian/patches/pve/0012-qmp-add-get_link_status.patch index e359662..28d3edb 100644 --- a/debian/patches/pve/0012-qmp-add-get_link_status.patch +++ b/debian/patches/pve/0012-qmp-add-get_link_status.patch @@ -4,15 +4,15 @@ Date: Wed, 9 Dec 2015 16:34:41 +0100 Subject: [PATCH] qmp: add get_link_status --- - net/net.c | 27 +++++++++++++++++++++++++++ - qapi-schema.json | 16 ++++++++++++++++ - 2 files changed, 43 insertions(+) + net/net.c | 27 +++++++++++++++++++++++++++ + qapi/net.json | 15 +++++++++++++++ + 2 files changed, 42 insertions(+) diff --git a/net/net.c b/net/net.c -index 0ac3b9e80c..7410c1e5f3 100644 +index 39ef546708..3681aa2173 100644 --- a/net/net.c +++ b/net/net.c -@@ -1373,6 +1373,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict) +@@ -1399,6 +1399,33 @@ void hmp_info_network(Monitor *mon, const QDict *qdict) } } @@ -46,19 +46,11 @@ index 0ac3b9e80c..7410c1e5f3 100644 void qmp_set_link(const char *name, bool up, Error **errp) { NetClientState *ncs[MAX_QUEUE_NUM]; -diff --git a/qapi-schema.json b/qapi-schema.json -index 361700d37c..5e82933ca1 100644 ---- a/qapi-schema.json -+++ b/qapi-schema.json -@@ -56,6 +56,7 @@ - { 'pragma': { - # Commands allowed to return a non-dictionary: - 'returns-whitelist': [ -+ 'get_link_status', - 'human-monitor-command', - 'qom-get', - 'query-migrate-cache-size', -@@ -2537,6 +2538,21 @@ +diff --git a/qapi/net.json b/qapi/net.json +index 4beff5d582..73334c8f3c 100644 +--- a/qapi/net.json ++++ b/qapi/net.json +@@ -35,6 +35,21 @@ { 'command': 'set_link', 'data': {'name': 'str', 'up': 'bool'} } ## @@ -77,9 +69,9 @@ index 361700d37c..5e82933ca1 100644 +{ 'command': 'get_link_status', 'data': {'name': 'str'}, 'returns': 'int'} + +## - # @balloon: + # @netdev_add: # - # Request the balloon driver to change its balloon size. + # Add a network backend. -- 2.11.0 diff --git a/debian/patches/pve/0013-smm_available-false.patch b/debian/patches/pve/0013-smm_available-false.patch index 6abdb45..bd356e1 100644 --- a/debian/patches/pve/0013-smm_available-false.patch +++ b/debian/patches/pve/0013-smm_available-false.patch @@ -9,10 +9,10 @@ Signed-off-by: Alexandre Derumier 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c -index e96901435a..f049bbca9a 100644 +index 186545d2a4..603a7ce6bf 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c -@@ -2123,7 +2123,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) +@@ -2135,7 +2135,7 @@ bool pc_machine_is_smm_enabled(PCMachineState *pcms) if (tcg_enabled() || qtest_enabled()) { smm_available = true; } else if (kvm_enabled()) { diff --git a/debian/patches/pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch b/debian/patches/pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch index 1b5ade7..4b3a63f 100644 --- a/debian/patches/pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch +++ b/debian/patches/pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch @@ -10,10 +10,10 @@ the version string, see PVE::QemuServer::kvm_user_version() 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vl.c b/vl.c -index 2132a77129..5d888cd179 100644 +index 1bfbe95b22..75fde82180 100644 --- a/vl.c +++ b/vl.c -@@ -1909,7 +1909,7 @@ static void main_loop(void) +@@ -2006,7 +2006,7 @@ static void main_loop(void) static void version(void) { diff --git a/debian/patches/pve/0015-vnc-altgr-emulation.patch b/debian/patches/pve/0015-vnc-altgr-emulation.patch index 7f8b3b8..5e3115b 100644 --- a/debian/patches/pve/0015-vnc-altgr-emulation.patch +++ b/debian/patches/pve/0015-vnc-altgr-emulation.patch @@ -8,10 +8,10 @@ Subject: [PATCH] vnc: altgr emulation 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ui/vnc.c b/ui/vnc.c -index b0314441c4..f30687884b 100644 +index 06abe7360e..03f8f61b2e 100644 --- a/ui/vnc.c +++ b/ui/vnc.c -@@ -1631,6 +1631,10 @@ static void kbd_leds(void *opaque, int ledstate) +@@ -1775,6 +1775,10 @@ static void kbd_leds(void *opaque, int ledstate) static void do_key_event(VncState *vs, int down, int keycode, int sym) { @@ -22,7 +22,7 @@ index b0314441c4..f30687884b 100644 /* QEMU console switch */ switch(keycode) { case 0x2a: /* Left Shift */ -@@ -1711,8 +1715,27 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) +@@ -1855,8 +1859,27 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } if (qemu_console_is_graphic(NULL)) { @@ -50,7 +50,7 @@ index b0314441c4..f30687884b 100644 } else { bool numlock = vs->modifiers_state[0x45]; bool control = (vs->modifiers_state[0x1d] || -@@ -1852,7 +1875,8 @@ static void key_event(VncState *vs, int down, uint32_t sym) +@@ -1996,7 +2019,8 @@ static void key_event(VncState *vs, int down, uint32_t sym) lsym = lsym - 'A' + 'a'; } diff --git a/debian/patches/pve/0016-vnc-make-x509-imply-tls-again.patch b/debian/patches/pve/0016-vnc-make-x509-imply-tls-again.patch index 8c74f13..f7f213e 100644 --- a/debian/patches/pve/0016-vnc-make-x509-imply-tls-again.patch +++ b/debian/patches/pve/0016-vnc-make-x509-imply-tls-again.patch @@ -8,10 +8,10 @@ Subject: [PATCH] vnc: make x509 imply tls again 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c -index f30687884b..a345bf0d78 100644 +index 03f8f61b2e..4494cb1dd4 100644 --- a/ui/vnc.c +++ b/ui/vnc.c -@@ -3881,9 +3881,8 @@ void vnc_display_open(const char *id, Error **errp) +@@ -4034,9 +4034,8 @@ void vnc_display_open(const char *id, Error **errp) const char *path; bool tls = false, x509 = false, x509verify = false; tls = qemu_opt_get_bool(opts, "tls", false); diff --git a/debian/patches/pve/0017-vnc-PVE-VNC-authentication.patch b/debian/patches/pve/0017-vnc-PVE-VNC-authentication.patch index e4c915a..ae2b3ce 100644 --- a/debian/patches/pve/0017-vnc-PVE-VNC-authentication.patch +++ b/debian/patches/pve/0017-vnc-PVE-VNC-authentication.patch @@ -4,21 +4,22 @@ Date: Mon, 11 Jan 2016 10:40:31 +0100 Subject: [PATCH] vnc: PVE VNC authentication --- - crypto/tlscreds.c | 47 +++++++++++ + crypto/tlscreds.c | 47 ++++++++++++ crypto/tlscredspriv.h | 2 + - crypto/tlscredsx509.c | 13 +-- + crypto/tlscredsx509.c | 13 ++-- crypto/tlssession.c | 1 + include/crypto/tlscreds.h | 1 + include/ui/console.h | 1 + + qapi-schema.json | 1 + qemu-options.hx | 3 + - ui/vnc-auth-vencrypt.c | 197 ++++++++++++++++++++++++++++++++++++++-------- - ui/vnc.c | 140 +++++++++++++++++++++++++++++++- + ui/vnc-auth-vencrypt.c | 182 ++++++++++++++++++++++++++++++++++++++-------- + ui/vnc.c | 140 ++++++++++++++++++++++++++++++++++- ui/vnc.h | 4 + vl.c | 9 +++ - 11 files changed, 377 insertions(+), 41 deletions(-) + 12 files changed, 364 insertions(+), 40 deletions(-) diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c -index a8965531b6..e9ae13ce47 100644 +index 3cd41035bb..e982da3451 100644 --- a/crypto/tlscreds.c +++ b/crypto/tlscreds.c @@ -158,6 +158,33 @@ qcrypto_tls_creds_prop_get_verify(Object *obj, @@ -75,7 +76,7 @@ index a8965531b6..e9ae13ce47 100644 + NULL); + object_property_add_enum(obj, "endpoint", + "QCryptoTLSCredsEndpoint", -+ QCryptoTLSCredsEndpoint_lookup, ++ &QCryptoTLSCredsEndpoint_lookup, + qcrypto_tls_creds_prop_get_endpoint, + qcrypto_tls_creds_prop_set_endpoint, + NULL); @@ -168,10 +169,10 @@ index ad47d88be7..f86d379f26 100644 diff --git a/include/ui/console.h b/include/ui/console.h -index d759338816..69f010e1db 100644 +index 580dfc57ee..383e5c88bd 100644 --- a/include/ui/console.h +++ b/include/ui/console.h -@@ -462,6 +462,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen) +@@ -466,6 +466,7 @@ static inline void cocoa_display_init(DisplayState *ds, int full_screen) #endif /* vnc.c */ @@ -179,11 +180,23 @@ index d759338816..69f010e1db 100644 void vnc_display_init(const char *id); void vnc_display_open(const char *id, Error **errp); void vnc_display_add_client(const char *id, int csock, bool skipauth); +diff --git a/qapi-schema.json b/qapi-schema.json +index 348b527681..d2155cb00f 100644 +--- a/qapi-schema.json ++++ b/qapi-schema.json +@@ -56,6 +56,7 @@ + { 'pragma': { + # Commands allowed to return a non-dictionary: + 'returns-whitelist': [ ++ 'get_link_status', + 'human-monitor-command', + 'qom-get', + 'query-migrate-cache-size', diff --git a/qemu-options.hx b/qemu-options.hx -index cbcb27da9a..0b1957c034 100644 +index 7c054af8f9..07129d55bc 100644 --- a/qemu-options.hx +++ b/qemu-options.hx -@@ -513,6 +513,9 @@ STEXI +@@ -583,6 +583,9 @@ STEXI @table @option ETEXI @@ -194,13 +207,13 @@ index cbcb27da9a..0b1957c034 100644 "-fda/-fdb file use 'file' as floppy disk 0/1 image\n", QEMU_ARCH_ALL) DEF("fdb", HAS_ARG, QEMU_OPTION_fdb, "", QEMU_ARCH_ALL) diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c -index ffaab57550..594ca737a9 100644 +index 7833631275..c42acd3714 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c -@@ -28,6 +28,108 @@ - #include "vnc.h" +@@ -29,6 +29,108 @@ #include "qapi/error.h" #include "qemu/main-loop.h" + #include "trace.h" +#include "io/channel-socket.h" + +static int protocol_client_auth_plain(VncState *vs, uint8_t *data, size_t len) @@ -228,7 +241,7 @@ index ffaab57550..594ca737a9 100644 + + VNC_DEBUG("AUTH PLAIN username: %s pw: %s\n", username, passwd); + -+ if (pve_auth_verify(clientip->u.inet.data->host, username, passwd) == 0) { ++ if (pve_auth_verify(clientip->u.inet.host, username, passwd) == 0) { + vnc_write_u32(vs, 0); /* Accept auth completion */ + start_client_init(vs); + qapi_free_SocketAddress(clientip); @@ -323,21 +336,20 @@ index ffaab57550..594ca737a9 100644 + case VNC_AUTH_VENCRYPT_TLSVNC: case VNC_AUTH_VENCRYPT_X509VNC: - VNC_DEBUG("Start TLS auth VNC\n"); -@@ -88,45 +201,64 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len - { + start_auth_vnc(vs); +@@ -90,45 +203,51 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len int auth = read_u32(data, 0); + trace_vnc_auth_vencrypt_subauth(vs, auth); - if (auth != vs->subauth) { + if (auth != vs->subauth && auth != VNC_AUTH_VENCRYPT_PLAIN) { - VNC_DEBUG("Rejecting auth %d\n", auth); + trace_vnc_auth_fail(vs, vs->auth, "Unsupported sub-auth version", ""); vnc_write_u8(vs, 0); /* Reject auth */ vnc_flush(vs); vnc_client_error(vs); } else { - Error *err = NULL; - QIOChannelTLS *tls; -- VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); - vnc_write_u8(vs, 1); /* Accept auth */ - vnc_flush(vs); - @@ -352,7 +364,6 @@ index ffaab57550..594ca737a9 100644 + { + Error *err = NULL; + QIOChannelTLS *tls; -+ VNC_DEBUG("Accepting auth %d, setting up TLS for handshake\n", auth); + vnc_write_u8(vs, 1); /* Accept auth */ + vnc_flush(vs); @@ -362,7 +373,8 @@ index ffaab57550..594ca737a9 100644 - vs->vd->tlsaclname, - &err); - if (!tls) { -- VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); +- trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed", +- error_get_pretty(err)); - error_free(err); - vnc_client_error(vs); - return 0; @@ -371,46 +383,34 @@ index ffaab57550..594ca737a9 100644 + g_source_remove(vs->ioc_tag); + vs->ioc_tag = 0; + } - -- qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls"); -- VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); -- object_unref(OBJECT(vs->ioc)); -- vs->ioc = QIO_CHANNEL(tls); -- vs->tls = qio_channel_tls_get_session(tls); + tls = qio_channel_tls_new_server( + vs->ioc, + vs->vd->tlscreds, + vs->vd->tlsaclname, + &err); + if (!tls) { -+ VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); ++ trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed", ++ error_get_pretty(err)); + error_free(err); + vnc_client_error(vs); + return 0; -+ vs->tls = qcrypto_tls_session_new(vs->vd->tlscreds, -+ NULL, -+ vs->vd->tlsaclname, -+ QCRYPTO_TLS_CREDS_ENDPOINT_SERVER, -+ &err); -+ if (!vs->tls) { -+ VNC_DEBUG("Failed to setup TLS %s\n", -+ error_get_pretty(err)); -+ error_free(err); -+ vnc_client_error(vs); -+ return 0; -+ } + } + +- qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls"); +- object_unref(OBJECT(vs->ioc)); +- vs->ioc = QIO_CHANNEL(tls); +- trace_vnc_client_io_wrap(vs, vs->ioc, "tls"); +- vs->tls = qio_channel_tls_get_session(tls); + qio_channel_set_name(QIO_CHANNEL(tls), "vnc-server-tls"); ++ object_unref(OBJECT(vs->ioc)); ++ vs->ioc = QIO_CHANNEL(tls); ++ trace_vnc_client_io_wrap(vs, vs->ioc, "tls"); ++ vs->tls = qio_channel_tls_get_session(tls); - qio_channel_tls_handshake(tls, - vnc_tls_handshake_done, - vs, - NULL); -+ VNC_DEBUG("Start TLS VeNCrypt handshake process\n"); -+ object_unref(OBJECT(vs->ioc)); -+ vs->ioc = QIO_CHANNEL(tls); -+ vs->tls = qio_channel_tls_get_session(tls); -+ + qio_channel_tls_handshake(tls, + vnc_tls_handshake_done, + vs, @@ -419,12 +419,9 @@ index ffaab57550..594ca737a9 100644 } return 0; } -@@ -140,10 +272,11 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len - vnc_flush(vs); +@@ -144,8 +263,9 @@ static int protocol_client_vencrypt_init(VncState *vs, uint8_t *data, size_t len vnc_client_error(vs); } else { -- VNC_DEBUG("Sending allowed auth %d\n", vs->subauth); -+ VNC_DEBUG("Sending allowed auths %d %d\n", vs->subauth, VNC_AUTH_VENCRYPT_PLAIN); vnc_write_u8(vs, 0); /* Accept version */ - vnc_write_u8(vs, 1); /* Number of sub-auths */ + vnc_write_u8(vs, 2); /* Number of sub-auths */ @@ -434,10 +431,10 @@ index ffaab57550..594ca737a9 100644 vnc_read_when(vs, protocol_client_vencrypt_auth, 4); } diff --git a/ui/vnc.c b/ui/vnc.c -index a345bf0d78..42db7e386b 100644 +index 4494cb1dd4..1589cbe1b3 100644 --- a/ui/vnc.c +++ b/ui/vnc.c -@@ -56,6 +56,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; +@@ -55,6 +55,125 @@ static const struct timeval VNC_REFRESH_LOSSY = { 2, 0 }; #include "vnc_keysym.h" #include "crypto/cipher.h" @@ -563,7 +560,7 @@ index a345bf0d78..42db7e386b 100644 static QTAILQ_HEAD(, VncDisplay) vnc_displays = QTAILQ_HEAD_INITIALIZER(vnc_displays); -@@ -3356,10 +3475,16 @@ vnc_display_setup_auth(int *auth, +@@ -3507,10 +3626,16 @@ vnc_display_setup_auth(int *auth, if (password) { if (is_x509) { VNC_DEBUG("Initializing VNC server with x509 password auth\n"); @@ -582,7 +579,7 @@ index a345bf0d78..42db7e386b 100644 } } else if (sasl) { -@@ -3393,6 +3518,7 @@ vnc_display_create_creds(bool x509, +@@ -3544,6 +3669,7 @@ vnc_display_create_creds(bool x509, bool x509verify, const char *dir, const char *id, @@ -590,7 +587,7 @@ index a345bf0d78..42db7e386b 100644 Error **errp) { gchar *credsid = g_strdup_printf("tlsvnc%s", id); -@@ -3408,6 +3534,7 @@ vnc_display_create_creds(bool x509, +@@ -3559,6 +3685,7 @@ vnc_display_create_creds(bool x509, "endpoint", "server", "dir", dir, "verify-peer", x509verify ? "yes" : "no", @@ -598,7 +595,7 @@ index a345bf0d78..42db7e386b 100644 NULL); } else { creds = object_new_with_props(TYPE_QCRYPTO_TLS_CREDS_ANON, -@@ -3415,6 +3542,7 @@ vnc_display_create_creds(bool x509, +@@ -3566,6 +3693,7 @@ vnc_display_create_creds(bool x509, credsid, &err, "endpoint", "server", @@ -606,7 +603,7 @@ index a345bf0d78..42db7e386b 100644 NULL); } -@@ -3879,12 +4007,17 @@ void vnc_display_open(const char *id, Error **errp) +@@ -4032,12 +4160,17 @@ void vnc_display_open(const char *id, Error **errp) } } else { const char *path; @@ -625,7 +622,7 @@ index a345bf0d78..42db7e386b 100644 } else { path = qemu_opt_get(opts, "x509verify"); if (path) { -@@ -3896,6 +4029,7 @@ void vnc_display_open(const char *id, Error **errp) +@@ -4049,6 +4182,7 @@ void vnc_display_open(const char *id, Error **errp) x509verify, path, vd->id, @@ -634,10 +631,10 @@ index a345bf0d78..42db7e386b 100644 if (!vd->tlscreds) { goto fail; diff --git a/ui/vnc.h b/ui/vnc.h -index 694cf32ca9..78d622ab84 100644 +index bbda0540a7..8cc6367ed3 100644 --- a/ui/vnc.h +++ b/ui/vnc.h -@@ -284,6 +284,8 @@ struct VncState +@@ -290,6 +290,8 @@ struct VncState int auth; int subauth; /* Used by VeNCrypt */ char challenge[VNC_AUTH_CHALLENGE_SIZE]; @@ -646,7 +643,7 @@ index 694cf32ca9..78d622ab84 100644 QCryptoTLSSession *tls; /* Borrowed pointer from channel, don't free */ #ifdef CONFIG_VNC_SASL VncStateSASL sasl; -@@ -577,4 +579,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +@@ -595,4 +597,6 @@ int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); void vnc_zrle_clear(VncState *vs); @@ -654,10 +651,10 @@ index 694cf32ca9..78d622ab84 100644 + #endif /* QEMU_VNC_H */ diff --git a/vl.c b/vl.c -index 5d888cd179..1000a4a259 100644 +index 75fde82180..255d989009 100644 --- a/vl.c +++ b/vl.c -@@ -2947,6 +2947,7 @@ static int qemu_read_default_config_file(void) +@@ -3096,6 +3096,7 @@ static void register_global_properties(MachineState *ms) int main(int argc, char **argv, char **envp) { int i; @@ -665,7 +662,7 @@ index 5d888cd179..1000a4a259 100644 int snapshot, linux_boot; const char *initrd_filename; const char *kernel_filename, *kernel_cmdline; -@@ -3778,6 +3779,14 @@ int main(int argc, char **argv, char **envp) +@@ -3922,6 +3923,14 @@ int main(int argc, char **argv, char **envp) exit(1); } break; diff --git a/debian/patches/pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch b/debian/patches/pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch similarity index 88% rename from debian/patches/pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch rename to debian/patches/pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch index 1cca949..e75da3c 100644 --- a/debian/patches/pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch +++ b/debian/patches/pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch @@ -10,10 +10,10 @@ Signed-off-by: Alexandre Derumier 1 file changed, 4 insertions(+) diff --git a/block/rbd.c b/block/rbd.c -index 2354ffcc64..b7700648ff 100644 +index a76a5e8755..a33738a254 100644 --- a/block/rbd.c +++ b/block/rbd.c -@@ -623,6 +623,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, +@@ -642,6 +642,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, rados_conf_set(s->cluster, "rbd_cache", "true"); } diff --git a/debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch b/debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch deleted file mode 100644 index 6fd3e19..0000000 --- a/debian/patches/pve/0018-migrate-fix-possible-unitialised-return-value.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Thomas Lamprecht -Date: Wed, 6 Apr 2016 16:45:15 +0200 -Subject: [PATCH] migrate: fix possible unitialised return value - ---- - migration/savevm.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/migration/savevm.c b/migration/savevm.c -index feb0dc6834..d2615f4620 100644 ---- a/migration/savevm.c -+++ b/migration/savevm.c -@@ -1111,7 +1111,7 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only) - QJSON *vmdesc; - int vmdesc_len; - SaveStateEntry *se; -- int ret; -+ int ret = -1; - bool in_postcopy = migration_in_postcopy(migrate_get_current()); - - trace_savevm_state_complete_precopy(); --- -2.11.0 - diff --git a/debian/patches/pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch b/debian/patches/pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch similarity index 98% rename from debian/patches/pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch rename to debian/patches/pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch index 05b172c..a3a5828 100644 --- a/debian/patches/pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch +++ b/debian/patches/pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch @@ -9,7 +9,7 @@ Signed-off-by: Alexandre Derumier 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/savevm-async.c b/savevm-async.c -index 97d51d3edd..ece193a065 100644 +index 96523c88ae..06dcd29fc7 100644 --- a/savevm-async.c +++ b/savevm-async.c @@ -377,6 +377,7 @@ void qmp_snapshot_drive(const char *device, const char *name, Error **errp) diff --git a/debian/patches/pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch b/debian/patches/pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch similarity index 97% rename from debian/patches/pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch rename to debian/patches/pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch index bf98d48..8cbef36 100644 --- a/debian/patches/pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch +++ b/debian/patches/pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch @@ -11,7 +11,7 @@ Signed-off-by: Alexandre Derumier 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/savevm-async.c b/savevm-async.c -index ece193a065..716dd2d7a1 100644 +index 06dcd29fc7..e6f86ef865 100644 --- a/savevm-async.c +++ b/savevm-async.c @@ -459,6 +459,7 @@ void qmp_delete_drive_snapshot(const char *device, const char *name, diff --git a/debian/patches/pve/0022-glusterfs-no-default-logfile-if-daemonized.patch b/debian/patches/pve/0021-glusterfs-no-default-logfile-if-daemonized.patch similarity index 86% rename from debian/patches/pve/0022-glusterfs-no-default-logfile-if-daemonized.patch rename to debian/patches/pve/0021-glusterfs-no-default-logfile-if-daemonized.patch index ac2046b..8725559 100644 --- a/debian/patches/pve/0022-glusterfs-no-default-logfile-if-daemonized.patch +++ b/debian/patches/pve/0021-glusterfs-no-default-logfile-if-daemonized.patch @@ -8,10 +8,10 @@ Subject: [PATCH] glusterfs: no default logfile if daemonized 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/block/gluster.c b/block/gluster.c -index cf29b5f9a4..bc44c50db0 100644 +index 0f4265a3a4..8fab26481c 100644 --- a/block/gluster.c +++ b/block/gluster.c -@@ -33,7 +33,7 @@ +@@ -32,7 +32,7 @@ #define GLUSTER_DEBUG_DEFAULT 4 #define GLUSTER_DEBUG_MAX 9 #define GLUSTER_OPT_LOGFILE "logfile" @@ -20,15 +20,15 @@ index cf29b5f9a4..bc44c50db0 100644 #define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n" -@@ -398,6 +398,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, +@@ -396,6 +396,7 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, int old_errno; - SocketAddressFlatList *server; + SocketAddressList *server; unsigned long long port; + const char *logfile; glfs = glfs_find_preopened(gconf->volume); if (glfs) { -@@ -440,9 +441,15 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, +@@ -438,9 +439,15 @@ static struct glfs *qemu_gluster_glfs_init(BlockdevOptionsGluster *gconf, } } diff --git a/debian/patches/pve/0023-glusterfs-allow-partial-reads.patch b/debian/patches/pve/0022-glusterfs-allow-partial-reads.patch similarity index 86% rename from debian/patches/pve/0023-glusterfs-allow-partial-reads.patch rename to debian/patches/pve/0022-glusterfs-allow-partial-reads.patch index 6648a2b..ec2bc4d 100644 --- a/debian/patches/pve/0023-glusterfs-allow-partial-reads.patch +++ b/debian/patches/pve/0022-glusterfs-allow-partial-reads.patch @@ -14,10 +14,10 @@ sense. 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/block/gluster.c b/block/gluster.c -index bc44c50db0..4fdf68f1fc 100644 +index 8fab26481c..24296a39b3 100644 --- a/block/gluster.c +++ b/block/gluster.c -@@ -42,6 +42,7 @@ typedef struct GlusterAIOCB { +@@ -41,6 +41,7 @@ typedef struct GlusterAIOCB { int ret; Coroutine *coroutine; AioContext *aio_context; @@ -25,7 +25,7 @@ index bc44c50db0..4fdf68f1fc 100644 } GlusterAIOCB; typedef struct BDRVGlusterState { -@@ -713,8 +714,10 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) +@@ -709,8 +710,10 @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) acb->ret = 0; /* Success */ } else if (ret < 0) { acb->ret = -errno; /* Read/Write failed */ @@ -37,7 +37,7 @@ index bc44c50db0..4fdf68f1fc 100644 } aio_co_schedule(acb->aio_context, acb->coroutine); -@@ -962,6 +965,7 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, +@@ -958,6 +961,7 @@ static coroutine_fn int qemu_gluster_co_pwrite_zeroes(BlockDriverState *bs, acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); @@ -45,7 +45,7 @@ index bc44c50db0..4fdf68f1fc 100644 ret = glfs_zerofill_async(s->fd, offset, size, gluster_finish_aiocb, &acb); if (ret < 0) { -@@ -1084,9 +1088,11 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, +@@ -1083,9 +1087,11 @@ static coroutine_fn int qemu_gluster_co_rw(BlockDriverState *bs, acb.aio_context = bdrv_get_aio_context(bs); if (write) { @@ -57,7 +57,7 @@ index bc44c50db0..4fdf68f1fc 100644 ret = glfs_preadv_async(s->fd, qiov->iov, qiov->niov, offset, 0, gluster_finish_aiocb, &acb); } -@@ -1150,6 +1156,7 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) +@@ -1158,6 +1164,7 @@ static coroutine_fn int qemu_gluster_co_flush_to_disk(BlockDriverState *bs) acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); @@ -65,7 +65,7 @@ index bc44c50db0..4fdf68f1fc 100644 ret = glfs_fsync_async(s->fd, gluster_finish_aiocb, &acb); if (ret < 0) { -@@ -1196,6 +1203,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, +@@ -1204,6 +1211,7 @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs, acb.ret = 0; acb.coroutine = qemu_coroutine_self(); acb.aio_context = bdrv_get_aio_context(bs); diff --git a/debian/patches/pve/0024-block-add-the-zeroinit-block-driver-filter.patch b/debian/patches/pve/0023-block-add-the-zeroinit-block-driver-filter.patch similarity index 76% rename from debian/patches/pve/0024-block-add-the-zeroinit-block-driver-filter.patch rename to debian/patches/pve/0023-block-add-the-zeroinit-block-driver-filter.patch index e3f9fa5..1a0847d 100644 --- a/debian/patches/pve/0024-block-add-the-zeroinit-block-driver-filter.patch +++ b/debian/patches/pve/0023-block-add-the-zeroinit-block-driver-filter.patch @@ -5,15 +5,15 @@ Subject: [PATCH] block: add the zeroinit block driver filter --- block/Makefile.objs | 1 + - block/zeroinit.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++ - 2 files changed, 221 insertions(+) + block/zeroinit.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 203 insertions(+) create mode 100644 block/zeroinit.c diff --git a/block/Makefile.objs b/block/Makefile.objs -index de96f8ee80..8cdac08db5 100644 +index 6eaf78a046..823e60cda6 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs -@@ -4,6 +4,7 @@ block-obj-y += qed.o qed-gencb.o qed-l2-cache.o qed-table.o qed-cluster.o +@@ -4,6 +4,7 @@ block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o block-obj-y += qed-check.o block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += quorum.o @@ -23,10 +23,10 @@ index de96f8ee80..8cdac08db5 100644 block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o diff --git a/block/zeroinit.c b/block/zeroinit.c new file mode 100644 -index 0000000000..305185512e +index 0000000000..37f588f75c --- /dev/null +++ b/block/zeroinit.c -@@ -0,0 +1,220 @@ +@@ -0,0 +1,202 @@ +/* + * Filter to fake a zero-initialized block device. + * @@ -134,12 +134,10 @@ index 0000000000..305185512e + return bdrv_getlength(bs->file->bs); +} + -+static BlockAIOCB *zeroinit_aio_readv(BlockDriverState *bs, -+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, -+ BlockCompletionFunc *cb, void *opaque) ++static int coroutine_fn zeroinit_co_preadv(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) +{ -+ return bdrv_aio_readv(bs->file, sector_num, qiov, nb_sectors, -+ cb, opaque); ++ return bdrv_co_preadv(bs->file, offset, bytes, qiov, flags); +} + +static int coroutine_fn zeroinit_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, @@ -151,23 +149,14 @@ index 0000000000..305185512e + return bdrv_pwrite_zeroes(bs->file, offset, count, flags); +} + -+static BlockAIOCB *zeroinit_aio_writev(BlockDriverState *bs, -+ int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, -+ BlockCompletionFunc *cb, void *opaque) ++static int coroutine_fn zeroinit_co_pwritev(BlockDriverState *bs, ++ uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) +{ + BDRVZeroinitState *s = bs->opaque; -+ int64_t extents = (sector_num << BDRV_SECTOR_BITS) + ((nb_sectors + 1) << BDRV_SECTOR_BITS); ++ int64_t extents = offset + bytes; + if (extents > s->extents) + s->extents = extents; -+ return bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, -+ cb, opaque); -+} -+ -+static BlockAIOCB *zeroinit_aio_flush(BlockDriverState *bs, -+ BlockCompletionFunc *cb, -+ void *opaque) -+{ -+ return bdrv_aio_flush(bs->file->bs, cb, opaque); ++ return bdrv_co_pwritev(bs->file, offset, bytes, qiov, flags); +} + +static bool zeroinit_recurse_is_first_non_filter(BlockDriverState *bs, @@ -187,23 +176,16 @@ index 0000000000..305185512e + return s->has_zero_init; +} + -+static int64_t coroutine_fn zeroinit_co_get_block_status(BlockDriverState *bs, -+ int64_t sector_num, -+ int nb_sectors, int *pnum, -+ BlockDriverState **file) -+{ -+ return bdrv_get_block_status(bs->file->bs, sector_num, nb_sectors, pnum, file); -+} -+ +static int coroutine_fn zeroinit_co_pdiscard(BlockDriverState *bs, + int64_t offset, int count) +{ + return bdrv_co_pdiscard(bs->file->bs, offset, count); +} + -+static int zeroinit_truncate(BlockDriverState *bs, int64_t offset) ++static int zeroinit_truncate(BlockDriverState *bs, int64_t offset, ++ PreallocMode prealloc, Error **errp) +{ -+ return bdrv_truncate(bs->file, offset, NULL); ++ return bdrv_truncate(bs->file, offset, prealloc, errp); +} + +static int zeroinit_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -224,16 +206,16 @@ index 0000000000..305185512e + .bdrv_co_flush_to_disk = zeroinit_co_flush, + + .bdrv_co_pwrite_zeroes = zeroinit_co_pwrite_zeroes, -+ .bdrv_aio_writev = zeroinit_aio_writev, -+ .bdrv_aio_readv = zeroinit_aio_readv, -+ .bdrv_aio_flush = zeroinit_aio_flush, ++ .bdrv_co_pwritev = zeroinit_co_pwritev, ++ .bdrv_co_preadv = zeroinit_co_preadv, ++ .bdrv_co_flush = zeroinit_co_flush, + + .is_filter = true, + .bdrv_recurse_is_first_non_filter = zeroinit_recurse_is_first_non_filter, + + .bdrv_has_zero_init = zeroinit_has_zero_init, + -+ .bdrv_co_get_block_status = zeroinit_co_get_block_status, ++ .bdrv_co_get_block_status = bdrv_co_get_block_status_from_file, + + .bdrv_co_pdiscard = zeroinit_co_pdiscard, + diff --git a/debian/patches/pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch b/debian/patches/pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch similarity index 79% rename from debian/patches/pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch rename to debian/patches/pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch index b730dbe..db04e8d 100644 --- a/debian/patches/pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch +++ b/debian/patches/pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch @@ -31,30 +31,30 @@ override the output file's size. Signed-off-by: Wolfgang Bumiller --- qemu-img-cmds.hx | 4 +- - qemu-img.c | 176 +++++++++++++++++++++++++++++++++++-------------------- - 2 files changed, 115 insertions(+), 65 deletions(-) + qemu-img.c | 192 ++++++++++++++++++++++++++++++++++--------------------- + 2 files changed, 122 insertions(+), 74 deletions(-) diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx -index 8ac78222af..16bee83987 100644 +index 2fe31893cf..52042f2773 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx -@@ -46,9 +46,9 @@ STEXI +@@ -53,9 +53,9 @@ STEXI ETEXI DEF("dd", img_dd, -- "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output") -+ "dd [--image-opts] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] [if=input] [of=output]") +- "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] if=input of=output") ++ "dd [--image-opts] [-U] [-f fmt] [-O output_fmt] [bs=block_size] [count=blocks] [skip=blocks] [osize=output_size] if=input of=output") STEXI --@item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} -+@item dd [--image-opts] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [osize=output_size] [if=@var{input}] [of=@var{output}] +-@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] if=@var{input} of=@var{output} ++@item dd [--image-opts] [-U] [-f @var{fmt}] [-O @var{output_fmt}] [bs=@var{block_size}] [count=@var{blocks}] [skip=@var{blocks}] [osize=output_size] if=@var{input} of=@var{output} ETEXI DEF("info", img_info, diff --git a/qemu-img.c b/qemu-img.c -index c7804d63ee..ee7816e727 100644 +index f8a39dd195..876a3623a7 100644 --- a/qemu-img.c +++ b/qemu-img.c -@@ -4026,10 +4026,12 @@ out: +@@ -4088,10 +4088,12 @@ out: #define C_IF 04 #define C_OF 010 #define C_SKIP 020 @@ -67,7 +67,7 @@ index c7804d63ee..ee7816e727 100644 }; struct DdIo { -@@ -4108,6 +4110,20 @@ static int img_dd_skip(const char *arg, +@@ -4170,6 +4172,20 @@ static int img_dd_skip(const char *arg, return 0; } @@ -88,7 +88,7 @@ index c7804d63ee..ee7816e727 100644 static int img_dd(int argc, char **argv) { int ret = 0; -@@ -4147,6 +4163,7 @@ static int img_dd(int argc, char **argv) +@@ -4210,6 +4226,7 @@ static int img_dd(int argc, char **argv) { "if", img_dd_if, C_IF }, { "of", img_dd_of, C_OF }, { "skip", img_dd_skip, C_SKIP }, @@ -96,7 +96,7 @@ index c7804d63ee..ee7816e727 100644 { NULL, NULL, 0 } }; const struct option long_options[] = { -@@ -4214,84 +4231,106 @@ static int img_dd(int argc, char **argv) +@@ -4288,8 +4305,13 @@ static int img_dd(int argc, char **argv) arg = NULL; } @@ -104,42 +104,25 @@ index c7804d63ee..ee7816e727 100644 - error_report("Must specify both input and output files"); + if (!(dd.flags & C_IF) && (!fmt || strcmp(fmt, "raw") != 0)) { + error_report("Input format must be raw when readin from stdin"); - ret = -1; - goto out; - } -- blk1 = img_open(image_opts, in.filename, fmt, 0, false, false); -- -- if (!blk1) { ++ ret = -1; ++ goto out; ++ } + if (!(dd.flags & C_OF) && strcmp(out_fmt, "raw") != 0) { + error_report("Output format must be raw when writing to stdout"); ret = -1; goto out; } +@@ -4301,85 +4323,101 @@ static int img_dd(int argc, char **argv) + goto out; + } + +- blk1 = img_open(image_opts, in.filename, fmt, 0, false, false, +- force_share); + if (dd.flags & C_IF) { -+ blk1 = img_open(image_opts, in.filename, fmt, 0, false, false); ++ blk1 = img_open(image_opts, in.filename, fmt, 0, false, false, ++ force_share); -- drv = bdrv_find_format(out_fmt); -- if (!drv) { -- error_report("Unknown file format"); -- ret = -1; -- goto out; -- } -- proto_drv = bdrv_find_protocol(out.filename, true, &local_err); -- -- if (!proto_drv) { -- error_report_err(local_err); -- ret = -1; -- goto out; -- } -- if (!drv->create_opts) { -- error_report("Format driver '%s' does not support image creation", -- drv->format_name); -- ret = -1; -- goto out; -- } -- if (!proto_drv->create_opts) { -- error_report("Protocol driver '%s' does not support image creation", -- proto_drv->format_name); +- if (!blk1) { - ret = -1; - goto out; + if (!blk1) { @@ -147,14 +130,10 @@ index c7804d63ee..ee7816e727 100644 + goto out; + } } -- create_opts = qemu_opts_append(create_opts, drv->create_opts); -- create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); -- -- opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); -- size = blk_getlength(blk1); -- if (size < 0) { -- error_report("Failed to get size for '%s'", in.filename); +- drv = bdrv_find_format(out_fmt); +- if (!drv) { +- error_report("Unknown file format"); + if (dd.flags & C_OSIZE) { + size = dd.osize; + } else if (dd.flags & C_IF) { @@ -171,30 +150,32 @@ index c7804d63ee..ee7816e727 100644 ret = -1; goto out; } +- proto_drv = bdrv_find_protocol(out.filename, true, &local_err); -- if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz && -+ if (!(dd.flags & C_OSIZE) && dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz && - dd.count * in.bsz < size) { - size = dd.count * in.bsz; - } - -- /* Overflow means the specified offset is beyond input image's size */ -- if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || -- size < in.bsz * in.offset)) { -- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort); -- } else { -- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, -- size - in.bsz * in.offset, &error_abort); +- if (!proto_drv) { +- error_report_err(local_err); +- ret = -1; +- goto out; - } - -- ret = bdrv_create(drv, out.filename, opts, &local_err); -- if (ret < 0) { -- error_reportf_err(local_err, -- "%s: error while creating output image: ", -- out.filename); +- if (!drv->create_opts) { +- error_report("Format driver '%s' does not support image creation", +- drv->format_name); - ret = -1; - goto out; - } +- if (!proto_drv->create_opts) { +- error_report("Protocol driver '%s' does not support image creation", +- proto_drv->format_name); +- ret = -1; +- goto out; ++ if (!(dd.flags & C_OSIZE) && dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz && ++ dd.count * in.bsz < size) { ++ size = dd.count * in.bsz; + } +- create_opts = qemu_opts_append(create_opts, drv->create_opts); +- create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); + +- opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); + if (dd.flags & C_OF) { + drv = bdrv_find_format(out_fmt); + if (!drv) { @@ -203,7 +184,13 @@ index c7804d63ee..ee7816e727 100644 + goto out; + } + proto_drv = bdrv_find_protocol(out.filename, true, &local_err); -+ + +- size = blk_getlength(blk1); +- if (size < 0) { +- error_report("Failed to get size for '%s'", in.filename); +- ret = -1; +- goto out; +- } + if (!proto_drv) { + error_report_err(local_err); + ret = -1; @@ -224,24 +211,39 @@ index c7804d63ee..ee7816e727 100644 + create_opts = qemu_opts_append(create_opts, drv->create_opts); + create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); -- blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR, -- false, false); +- if (dd.flags & C_COUNT && dd.count <= INT64_MAX / in.bsz && +- dd.count * in.bsz < size) { +- size = dd.count * in.bsz; +- } + opts = qemu_opts_create(create_opts, NULL, 0, &error_abort); -- if (!blk2) { -- ret = -1; -- goto out; +- /* Overflow means the specified offset is beyond input image's size */ +- if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || +- size < in.bsz * in.offset)) { +- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort); +- } else { +- qemu_opt_set_number(opts, BLOCK_OPT_SIZE, +- size - in.bsz * in.offset, &error_abort); +- } + /* Overflow means the specified offset is beyond input image's size */ + if (dd.flags & C_OSIZE) { + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, size, &error_abort); + } else if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || -+ size < in.bsz * in.offset)) { ++ size < in.bsz * in.offset)) { + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, 0, &error_abort); + } else { + qemu_opt_set_number(opts, BLOCK_OPT_SIZE, + size - in.bsz * in.offset, &error_abort); + } -+ + +- ret = bdrv_create(drv, out.filename, opts, &local_err); +- if (ret < 0) { +- error_reportf_err(local_err, +- "%s: error while creating output image: ", +- out.filename); +- ret = -1; +- goto out; +- } + ret = bdrv_create(drv, out.filename, opts, &local_err); + if (ret < 0) { + error_reportf_err(local_err, @@ -250,10 +252,25 @@ index c7804d63ee..ee7816e727 100644 + ret = -1; + goto out; + } -+ -+ blk2 = img_open(image_opts, out.filename, out_fmt, BDRV_O_RDWR, -+ false, false); -+ + +- /* TODO, we can't honour --image-opts for the target, +- * since it needs to be given in a format compatible +- * with the bdrv_create() call above which does not +- * support image-opts style. +- */ +- blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR, +- false, false, false); ++ /* TODO, we can't honour --image-opts for the target, ++ * since it needs to be given in a format compatible ++ * with the bdrv_create() call above which does not ++ * support image-opts style. ++ */ ++ blk2 = img_open_file(out.filename, NULL, out_fmt, BDRV_O_RDWR, ++ false, false, false); + +- if (!blk2) { +- ret = -1; +- goto out; + if (!blk2) { + ret = -1; + goto out; @@ -261,14 +278,14 @@ index c7804d63ee..ee7816e727 100644 } if (dd.flags & C_SKIP && (in.offset > INT64_MAX / in.bsz || -@@ -4309,11 +4348,18 @@ static int img_dd(int argc, char **argv) +@@ -4397,11 +4435,17 @@ static int img_dd(int argc, char **argv) for (out_pos = 0; in_pos < size; block_count++) { int in_ret, out_ret; -+ size_t in_bsz = in_pos + in.bsz > size ? size - in_pos : in.bsz; - +- - if (in_pos + in.bsz > size) { - in_ret = blk_pread(blk1, in_pos, in.buf, size - in_pos); ++ size_t in_bsz = in_pos + in.bsz > size ? size - in_pos : in.bsz; + if (blk1) { + in_ret = blk_pread(blk1, in_pos, in.buf, in_bsz); } else { @@ -283,7 +300,7 @@ index c7804d63ee..ee7816e727 100644 } if (in_ret < 0) { error_report("error while reading from input image file: %s", -@@ -4323,9 +4369,13 @@ static int img_dd(int argc, char **argv) +@@ -4411,9 +4455,13 @@ static int img_dd(int argc, char **argv) } in_pos += in_ret; diff --git a/debian/patches/pve/0026-backup-modify-job-api.patch b/debian/patches/pve/0025-backup-modify-job-api.patch similarity index 85% rename from debian/patches/pve/0026-backup-modify-job-api.patch rename to debian/patches/pve/0025-backup-modify-job-api.patch index 3b73e9f..bb03935 100644 --- a/debian/patches/pve/0026-backup-modify-job-api.patch +++ b/debian/patches/pve/0025-backup-modify-job-api.patch @@ -16,10 +16,10 @@ having been started at the same point in time. 5 files changed, 7 insertions(+), 4 deletions(-) diff --git a/block/backup.c b/block/backup.c -index a4fb2884f9..1ede70c061 100644 +index 99e6bcc748..8c2967a8cb 100644 --- a/block/backup.c +++ b/block/backup.c -@@ -558,6 +558,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -539,6 +539,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, @@ -27,7 +27,7 @@ index a4fb2884f9..1ede70c061 100644 BlockJobTxn *txn, Error **errp) { int64_t len; -@@ -682,6 +683,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -663,6 +664,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); job->common.len = len; @@ -36,10 +36,10 @@ index a4fb2884f9..1ede70c061 100644 return &job->common; diff --git a/block/replication.c b/block/replication.c -index bf3c395eb4..1c41d9e6bf 100644 +index e41e293d2b..1b08b242eb 100644 --- a/block/replication.c +++ b/block/replication.c -@@ -531,7 +531,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, +@@ -561,7 +561,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, 0, MIRROR_SYNC_MODE_NONE, NULL, false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, @@ -49,10 +49,10 @@ index bf3c395eb4..1c41d9e6bf 100644 error_propagate(errp, local_err); backup_job_cleanup(bs); diff --git a/blockdev.c b/blockdev.c -index e8a9a65167..9b6cfafd33 100644 +index 56a6b24a0b..a9ed9034b5 100644 --- a/blockdev.c +++ b/blockdev.c -@@ -3262,7 +3262,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +@@ -3293,7 +3293,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, bmap, backup->compress, backup->on_source_error, backup->on_target_error, @@ -61,7 +61,7 @@ index e8a9a65167..9b6cfafd33 100644 bdrv_unref(target_bs); if (local_err != NULL) { error_propagate(errp, local_err); -@@ -3341,7 +3341,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +@@ -3372,7 +3372,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, NULL, backup->compress, backup->on_source_error, backup->on_target_error, @@ -71,10 +71,10 @@ index e8a9a65167..9b6cfafd33 100644 error_propagate(errp, local_err); } diff --git a/blockjob.c b/blockjob.c -index 6e489327ff..764d41863e 100644 +index 715c2c2680..c1b6b6a810 100644 --- a/blockjob.c +++ b/blockjob.c -@@ -289,7 +289,7 @@ void block_job_start(BlockJob *job) +@@ -322,7 +322,7 @@ void block_job_start(BlockJob *job) job->co = qemu_coroutine_create(block_job_co_entry, job); job->pause_count--; job->busy = true; @@ -84,10 +84,10 @@ index 6e489327ff..764d41863e 100644 } diff --git a/include/block/block_int.h b/include/block/block_int.h -index 89d7b458e7..19b84b027f 100644 +index a5482775ec..1dbbdafd31 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h -@@ -879,6 +879,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -985,6 +985,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_target_error, int creation_flags, BlockCompletionFunc *cb, void *opaque, diff --git a/debian/patches/pve/0027-backup-introduce-vma-archive-format.patch b/debian/patches/pve/0026-backup-introduce-vma-archive-format.patch similarity index 85% rename from debian/patches/pve/0027-backup-introduce-vma-archive-format.patch rename to debian/patches/pve/0026-backup-introduce-vma-archive-format.patch index b09ac39..3f9d588 100644 --- a/debian/patches/pve/0027-backup-introduce-vma-archive-format.patch +++ b/debian/patches/pve/0026-backup-introduce-vma-archive-format.patch @@ -6,23 +6,23 @@ Subject: [PATCH] backup: introduce vma archive format --- MAINTAINERS | 6 + block/Makefile.objs | 3 + - block/vma.c | 424 +++++++++++++++++++++++++++++++++++++++++++ - blockdev.c | 498 +++++++++++++++++++++++++++++++++++++++++++++++++++ - configure | 30 ++++ + block/vma.c | 424 ++++++++++++++++++++++++++++++++++++++++ + blockdev.c | 536 +++++++++++++++++++++++++++++++++++++++++++++++++++ + blockjob.c | 3 +- + configure | 30 +++ hmp-commands-info.hx | 13 ++ - hmp-commands.hx | 31 ++++ - hmp.c | 63 +++++++ + hmp-commands.hx | 31 +++ + hmp.c | 63 ++++++ hmp.h | 3 + - qapi-schema.json | 91 ++++++++++ - qapi/block-core.json | 20 ++- - 11 files changed, 1180 insertions(+), 2 deletions(-) + qapi/block-core.json | 109 ++++++++++- + 11 files changed, 1219 insertions(+), 2 deletions(-) create mode 100644 block/vma.c diff --git a/MAINTAINERS b/MAINTAINERS -index 430efb0ab7..6a7d338aad 100644 +index 0255113470..581d80d144 100644 --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -1811,6 +1811,12 @@ L: qemu-block@nongnu.org +@@ -1950,6 +1950,12 @@ L: qemu-block@nongnu.org S: Supported F: block/vvfat.c @@ -36,18 +36,18 @@ index 430efb0ab7..6a7d338aad 100644 M: Stefan Hajnoczi L: qemu-block@nongnu.org diff --git a/block/Makefile.objs b/block/Makefile.objs -index 8cdac08db5..6df5567dd3 100644 +index 823e60cda6..d74140e413 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs -@@ -21,6 +21,7 @@ block-obj-$(CONFIG_CURL) += curl.o - block-obj-$(CONFIG_RBD) += rbd.o +@@ -22,6 +22,7 @@ block-obj-$(CONFIG_RBD) += rbd.o block-obj-$(CONFIG_GLUSTERFS) += gluster.o + block-obj-$(CONFIG_VXHS) += vxhs.o block-obj-$(CONFIG_LIBSSH2) += ssh.o +block-obj-$(CONFIG_VMA) += vma.o block-obj-y += accounting.o dirty-bitmap.o block-obj-y += write-threshold.o block-obj-y += backup.o -@@ -45,3 +46,5 @@ block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o +@@ -48,3 +49,5 @@ block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o dmg-bz2.o-libs := $(BZIP2_LIBS) qcow.o-libs := -lz linux-aio.o-libs := -laio @@ -484,7 +484,7 @@ index 0000000000..7151514f94 + +block_init(bdrv_vma_init); diff --git a/blockdev.c b/blockdev.c -index 9b6cfafd33..534c00f5da 100644 +index a9ed9034b5..3ffd064c48 100644 --- a/blockdev.c +++ b/blockdev.c @@ -31,10 +31,12 @@ @@ -500,7 +500,7 @@ index 9b6cfafd33..534c00f5da 100644 #include "block/throttle-groups.h" #include "monitor/monitor.h" #include "qemu/error-report.h" -@@ -2932,6 +2934,502 @@ out: +@@ -2963,6 +2965,540 @@ out: aio_context_release(aio_context); } @@ -521,6 +521,8 @@ index 9b6cfafd33..534c00f5da 100644 + size_t total; + size_t transferred; + size_t zero_bytes; ++ QemuMutex backup_mutex; ++ bool backup_mutex_initialized; +} backup_state; + +typedef struct PVEBackupDevInfo { @@ -536,6 +538,13 @@ index 9b6cfafd33..534c00f5da 100644 + +static void pvebackup_cleanup(void) +{ ++ qemu_mutex_lock(&backup_state.backup_mutex); ++ // Avoid race between block jobs and backup-cancel command: ++ if (!backup_state.vmaw) { ++ qemu_mutex_unlock(&backup_state.backup_mutex); ++ return; ++ } ++ + backup_state.end_time = time(NULL); + + if (backup_state.vmaobj) { @@ -543,20 +552,15 @@ index 9b6cfafd33..534c00f5da 100644 + backup_state.vmaobj = NULL; + } + -+ if (backup_state.di_list) { -+ GList *l = backup_state.di_list; -+ while (l) { -+ PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -+ l = g_list_next(l); -+ g_free(di); -+ } -+ g_list_free(backup_state.di_list); -+ backup_state.di_list = NULL; -+ } ++ g_list_free(backup_state.di_list); ++ backup_state.di_list = NULL; ++ qemu_mutex_unlock(&backup_state.backup_mutex); +} + +static void pvebackup_complete_cb(void *opaque, int ret) +{ ++ // This always runs in the main loop ++ + PVEBackupDevInfo *di = opaque; + + di->completed = true; @@ -574,6 +578,12 @@ index 9b6cfafd33..534c00f5da 100644 + backup_state.vmaobj = NULL; + } + ++ // remove self from job queue ++ qemu_mutex_lock(&backup_state.backup_mutex); ++ backup_state.di_list = g_list_remove(backup_state.di_list, di); ++ g_free(di); ++ qemu_mutex_unlock(&backup_state.backup_mutex); ++ + if (!backup_state.cancel) { + pvebackup_run_next_job(); + } @@ -582,6 +592,12 @@ index 9b6cfafd33..534c00f5da 100644 +static void pvebackup_cancel(void *opaque) +{ + backup_state.cancel = true; ++ qemu_mutex_lock(&backup_state.backup_mutex); ++ // Avoid race between block jobs and backup-cancel command: ++ if (!backup_state.vmaw) { ++ qemu_mutex_unlock(&backup_state.backup_mutex); ++ return; ++ } + + if (!backup_state.error) { + error_setg(&backup_state.error, "backup cancelled"); @@ -604,18 +620,24 @@ index 9b6cfafd33..534c00f5da 100644 + if (!di->completed && di->bs) { + BlockJob *job = di->bs->job; + if (job) { ++ AioContext *aio_context = blk_get_aio_context(job->blk); ++ aio_context_acquire(aio_context); + if (!di->completed) { -+ block_job_cancel_sync(job); ++ block_job_cancel(job); + } ++ aio_context_release(aio_context); + } + } + } + ++ qemu_mutex_unlock(&backup_state.backup_mutex); + pvebackup_cleanup(); +} + +void qmp_backup_cancel(Error **errp) +{ ++ if (!backup_state.backup_mutex_initialized) ++ return; + Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL); + qemu_coroutine_enter(co); + @@ -633,53 +655,61 @@ index 9b6cfafd33..534c00f5da 100644 + const char *backup_dir, + Error **errp) +{ -+ char *cdata = NULL; -+ gsize clen = 0; -+ GError *err = NULL; -+ if (!g_file_get_contents(file, &cdata, &clen, &err)) { -+ error_setg(errp, "unable to read file '%s'", file); -+ return 1; -+ } ++ char *cdata = NULL; ++ gsize clen = 0; ++ GError *err = NULL; ++ if (!g_file_get_contents(file, &cdata, &clen, &err)) { ++ error_setg(errp, "unable to read file '%s'", file); ++ return 1; ++ } + -+ char *basename = g_path_get_basename(file); -+ -+ if (format == BACKUP_FORMAT_VMA) { -+ vma_object_add_config_file(vmaobj, basename, cdata, clen, errp); -+ } else if (format == BACKUP_FORMAT_DIR) { -+ char config_path[PATH_MAX]; -+ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); -+ if (!g_file_set_contents(config_path, cdata, clen, &err)) { -+ error_setg(errp, "unable to write config file '%s'", config_path); -+ g_free(cdata); -+ g_free(basename); -+ return 1; -+ } -+ } ++ char *basename = g_path_get_basename(file); + -+ g_free(basename); -+ g_free(cdata); ++ if (format == BACKUP_FORMAT_VMA) { ++ vma_object_add_config_file(vmaobj, basename, cdata, clen, errp); ++ } else if (format == BACKUP_FORMAT_DIR) { ++ char config_path[PATH_MAX]; ++ snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); ++ if (!g_file_set_contents(config_path, cdata, clen, &err)) { ++ error_setg(errp, "unable to write config file '%s'", config_path); ++ g_free(cdata); ++ g_free(basename); ++ return 1; ++ } ++ } + -+ return 0; ++ g_free(basename); ++ g_free(cdata); ++ return 0; +} + ++void block_job_resume(BlockJob *job); +static void pvebackup_run_next_job(void) +{ -+ bool cancel = backup_state.error || backup_state.cancel; ++ qemu_mutex_lock(&backup_state.backup_mutex); ++ + GList *next = g_list_nth(backup_state.di_list, backup_state.next_job); + while (next) { + PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data; + backup_state.next_job++; + if (!di->completed && di->bs && di->bs->job) { + BlockJob *job = di->bs->job; -+ if (cancel) { -+ block_job_cancel(job); ++ AioContext *aio_context = blk_get_aio_context(job->blk); ++ aio_context_acquire(aio_context); ++ qemu_mutex_unlock(&backup_state.backup_mutex); ++ if (backup_state.error || backup_state.cancel) { ++ block_job_cancel_sync(job); + } else { + block_job_resume(job); + } ++ aio_context_release(aio_context); + return; + } + next = g_list_next(next); + } ++ qemu_mutex_unlock(&backup_state.backup_mutex); ++ ++ // no more jobs, run the cleanup + pvebackup_cleanup(); +} + @@ -701,6 +731,11 @@ index 9b6cfafd33..534c00f5da 100644 + UuidInfo *uuid_info; + BlockJob *job; + ++ if (!backup_state.backup_mutex_initialized) { ++ qemu_mutex_init(&backup_state.backup_mutex); ++ backup_state.backup_mutex_initialized = true; ++ } ++ + if (backup_state.di_list || backup_state.vmaobj) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "previous backup not finished"); @@ -831,7 +866,7 @@ index 9b6cfafd33..534c00f5da 100644 + + int flags = BDRV_O_RDWR; + bdrv_img_create(di->targetfile, "raw", NULL, NULL, NULL, -+ di->size, flags, &local_err, false); ++ di->size, flags, false, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto err; @@ -844,22 +879,22 @@ index 9b6cfafd33..534c00f5da 100644 + } + } + } else { -+ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); -+ goto err; ++ error_set(errp, ERROR_CLASS_GENERIC_ERROR, "unknown backup format"); ++ goto err; + } + + /* add configuration file to archive */ + if (has_config_file) { -+ if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -+ goto err; -+ } ++ if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { ++ goto err; ++ } + } + + /* add firewall file to archive */ + if (has_firewall_file) { -+ if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -+ goto err; -+ } ++ if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { ++ goto err; ++ } + } + /* initialize global backup_state now */ + @@ -883,6 +918,7 @@ index 9b6cfafd33..534c00f5da 100644 + memcpy(&backup_state.uuid, &uuid, sizeof(uuid)); + qemu_uuid_unparse(&uuid, backup_state.uuid_str); + ++ qemu_mutex_lock(&backup_state.backup_mutex); + backup_state.di_list = di_list; + backup_state.next_job = 0; + @@ -912,6 +948,8 @@ index 9b6cfafd33..534c00f5da 100644 + } + } + ++ qemu_mutex_unlock(&backup_state.backup_mutex); ++ + if (!backup_state.error) { + pvebackup_run_next_job(); // run one job + } @@ -1003,21 +1041,35 @@ index 9b6cfafd33..534c00f5da 100644 void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, bool has_base, const char *base, bool has_base_node, const char *base_node, +diff --git a/blockjob.c b/blockjob.c +index c1b6b6a810..2de9f8f4dd 100644 +--- a/blockjob.c ++++ b/blockjob.c +@@ -149,7 +149,8 @@ static void block_job_pause(BlockJob *job) + job->pause_count++; + } + +-static void block_job_resume(BlockJob *job) ++void block_job_resume(BlockJob *job); ++void block_job_resume(BlockJob *job) + { + assert(job->pause_count > 0); + job->pause_count--; diff --git a/configure b/configure -index be4d326ae0..841f7a8fae 100755 +index 0c6e7572db..3a28a0a092 100755 --- a/configure +++ b/configure -@@ -320,6 +320,7 @@ numa="" - tcmalloc="no" +@@ -422,6 +422,7 @@ tcmalloc="no" jemalloc="no" replication="yes" + vxhs="" +vma="" supported_cpu="no" supported_os="no" -@@ -1183,6 +1184,10 @@ for opt do +@@ -1313,6 +1314,10 @@ for opt do ;; - --enable-replication) replication="yes" + --disable-git-update) git_update=no ;; + --disable-vma) vma="no" + ;; @@ -1026,15 +1078,15 @@ index be4d326ae0..841f7a8fae 100755 *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" -@@ -1427,6 +1432,7 @@ disabled with --disable-FEATURE, default is enabled if available: - xfsctl xfsctl support - qom-cast-debug cast debugging support - tools build qemu-io, qemu-nbd and qemu-image tools +@@ -1561,6 +1566,7 @@ disabled with --disable-FEATURE, default is enabled if available: + crypto-afalg Linux AF_ALG crypto backend driver + vhost-user vhost-user support + capstone capstone disassembler support + vma VMA archive backend NOTE: The object files are built at the place where configure is launched EOF -@@ -3705,6 +3711,23 @@ EOF +@@ -3890,6 +3896,23 @@ EOF fi ########################################## @@ -1058,16 +1110,16 @@ index be4d326ae0..841f7a8fae 100755 # signalfd probe signalfd="no" cat > $TMPC << EOF -@@ -5146,6 +5169,7 @@ echo "tcmalloc support $tcmalloc" - echo "jemalloc support $jemalloc" - echo "avx2 optimization $avx2_opt" +@@ -5555,6 +5578,7 @@ echo "avx2 optimization $avx2_opt" echo "replication support $replication" + echo "VxHS block device $vxhs" + echo "capstone $capstone" +echo "VMA support $vma" if test "$sdl_too_old" = "yes"; then echo "-> Your SDL version is too old - please upgrade to have SDL support" -@@ -5703,6 +5727,12 @@ if test "$libssh2" = "yes" ; then - echo "LIBSSH2_LIBS=$libssh2_libs" >> $config_host_mak +@@ -5998,6 +6022,12 @@ if test "$libusb" = "yes" ; then + echo "LIBUSB_LIBS=$libusb_libs" >> $config_host_mak fi +if test "$vma" = "yes" ; then @@ -1076,14 +1128,14 @@ index be4d326ae0..841f7a8fae 100755 + echo "VMA_LIBS=$vma_libs" >> $config_host_mak +fi + - # USB host support - if test "$libusb" = "yes"; then - echo "HOST_USB=libusb legacy" >> $config_host_mak + if test "$usb_redir" = "yes" ; then + echo "CONFIG_USB_REDIR=y" >> $config_host_mak + echo "USB_REDIR_CFLAGS=$usb_redir_cflags" >> $config_host_mak diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx -index 5fc57a2210..3b5a0f95e4 100644 +index 3bf69a193c..7beeb29e99 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx -@@ -487,6 +487,19 @@ STEXI +@@ -493,6 +493,19 @@ STEXI Show CPU statistics. ETEXI @@ -1104,7 +1156,7 @@ index 5fc57a2210..3b5a0f95e4 100644 { .name = "usernet", diff --git a/hmp-commands.hx b/hmp-commands.hx -index 58940a762b..a2867b56f2 100644 +index b35bc6ab6c..9e50947845 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -87,6 +87,37 @@ STEXI @@ -1146,10 +1198,10 @@ index 58940a762b..a2867b56f2 100644 .name = "block_job_set_speed", .args_type = "device:B,speed:o", diff --git a/hmp.c b/hmp.c -index f725d061e6..12f1f46125 100644 +index b9ade681f0..241c2543ec 100644 --- a/hmp.c +++ b/hmp.c -@@ -151,6 +151,44 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict) +@@ -156,6 +156,44 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict) qapi_free_MouseInfoList(mice_list); } @@ -1194,7 +1246,7 @@ index f725d061e6..12f1f46125 100644 void hmp_info_migrate(Monitor *mon, const QDict *qdict) { MigrationInfo *info; -@@ -1613,6 +1651,31 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) +@@ -1848,6 +1886,31 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) hmp_handle_error(mon, &error); } @@ -1227,7 +1279,7 @@ index f725d061e6..12f1f46125 100644 { Error *error = NULL; diff --git a/hmp.h b/hmp.h -index 0497afbf65..8c1b4846b3 100644 +index 45ada581b6..635b9b4218 100644 --- a/hmp.h +++ b/hmp.h @@ -31,6 +31,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict); @@ -1238,7 +1290,7 @@ index 0497afbf65..8c1b4846b3 100644 void hmp_info_cpus(Monitor *mon, const QDict *qdict); void hmp_info_block(Monitor *mon, const QDict *qdict); void hmp_info_blockstats(Monitor *mon, const QDict *qdict); -@@ -80,6 +81,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict); +@@ -85,6 +86,8 @@ void hmp_eject(Monitor *mon, const QDict *qdict); void hmp_change(Monitor *mon, const QDict *qdict); void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict); void hmp_block_stream(Monitor *mon, const QDict *qdict); @@ -1247,12 +1299,12 @@ index 0497afbf65..8c1b4846b3 100644 void hmp_block_job_set_speed(Monitor *mon, const QDict *qdict); void hmp_block_job_cancel(Monitor *mon, const QDict *qdict); void hmp_block_job_pause(Monitor *mon, const QDict *qdict); -diff --git a/qapi-schema.json b/qapi-schema.json -index 5e82933ca1..b20020a054 100644 ---- a/qapi-schema.json -+++ b/qapi-schema.json -@@ -571,6 +571,97 @@ - { 'command': 'query-events', 'returns': ['EventInfo'] } +diff --git a/qapi/block-core.json b/qapi/block-core.json +index dd763dcf87..461fca9a3d 100644 +--- a/qapi/block-core.json ++++ b/qapi/block-core.json +@@ -615,6 +615,97 @@ + ## +# @BackupStatus: @@ -1346,24 +1398,20 @@ index 5e82933ca1..b20020a054 100644 +{ 'command': 'backup-cancel' } + +## - # @MigrationStats: + # @BlockDeviceTimedStats: # - # Detailed migration status. -diff --git a/qapi/block-core.json b/qapi/block-core.json -index 7ce90ec940..b0ffd3de4d 100644 ---- a/qapi/block-core.json -+++ b/qapi/block-core.json -@@ -2118,7 +2118,7 @@ + # Statistics of a block device during a given interval of time. +@@ -2239,7 +2330,7 @@ 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', 'ssh', -- 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] } -+ 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vma-drive' ] } +- 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ++ 'throttle', 'vdi', 'vhdx', 'vma-drive', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ## # @BlockdevOptionsFile: -@@ -2895,6 +2895,21 @@ - 'data': { '*offset': 'int', '*size': 'int' } } +@@ -3116,6 +3207,21 @@ + '*tls-creds': 'str' } } ## +# @BlockdevOptionsVMADrive: @@ -1381,19 +1429,17 @@ index 7ce90ec940..b0ffd3de4d 100644 + 'size': 'int' } } + +## - # @BlockdevOptions: + # @BlockdevOptionsThrottle: # - # Options for creating a block device. Many options are available for all -@@ -2956,7 +2971,8 @@ + # Driver specific block device options for the throttle driver +@@ -3196,6 +3302,7 @@ + 'throttle': 'BlockdevOptionsThrottle', + 'vdi': 'BlockdevOptionsGenericFormat', 'vhdx': 'BlockdevOptionsGenericFormat', ++ 'vma-drive': 'BlockdevOptionsVMADrive', 'vmdk': 'BlockdevOptionsGenericCOWFormat', 'vpc': 'BlockdevOptionsGenericFormat', -- 'vvfat': 'BlockdevOptionsVVFAT' -+ 'vvfat': 'BlockdevOptionsVVFAT', -+ 'vma-drive': 'BlockdevOptionsVMADrive' - } } - - ## + 'vvfat': 'BlockdevOptionsVVFAT', -- 2.11.0 diff --git a/debian/patches/pve/0028-adding-old-vma-files.patch b/debian/patches/pve/0027-adding-old-vma-files.patch similarity index 91% rename from debian/patches/pve/0028-adding-old-vma-files.patch rename to debian/patches/pve/0027-adding-old-vma-files.patch index 8025136..92a5b54 100644 --- a/debian/patches/pve/0028-adding-old-vma-files.patch +++ b/debian/patches/pve/0027-adding-old-vma-files.patch @@ -6,35 +6,35 @@ Subject: [PATCH] adding old vma files --- Makefile | 3 +- Makefile.objs | 1 + - block/backup.c | 132 ++++--- + block/backup.c | 130 ++++--- block/replication.c | 1 + - blockdev.c | 249 +++++++++----- - blockjob.c | 11 +- + blockdev.c | 207 +++++++---- + blockjob.c | 3 +- include/block/block_int.h | 4 + vma-reader.c | 857 ++++++++++++++++++++++++++++++++++++++++++++++ vma-writer.c | 771 +++++++++++++++++++++++++++++++++++++++++ - vma.c | 757 ++++++++++++++++++++++++++++++++++++++++ - vma.h | 149 ++++++++ - 11 files changed, 2802 insertions(+), 133 deletions(-) + vma.c | 756 ++++++++++++++++++++++++++++++++++++++++ + vma.h | 150 ++++++++ + 11 files changed, 2760 insertions(+), 123 deletions(-) create mode 100644 vma-reader.c create mode 100644 vma-writer.c create mode 100644 vma.c create mode 100644 vma.h diff --git a/Makefile b/Makefile -index 6c359b2f86..edbc8b50f0 100644 +index ab0354c153..ad28227b6c 100644 --- a/Makefile +++ b/Makefile -@@ -284,7 +284,7 @@ ifneq ($(wildcard config-host.mak),) +@@ -340,7 +340,7 @@ dummy := $(call unnest-vars,, \ + include $(SRC_PATH)/tests/Makefile.include - endif -all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules +all: $(DOCS) $(TOOLS) vma$(EXESUF) $(HELPERS-y) recurse-all modules qemu-version.h: FORCE $(call quiet-command, \ -@@ -377,6 +377,7 @@ qemu-img.o: qemu-img-cmds.h +@@ -439,6 +439,7 @@ qemu-img.o: qemu-img-cmds.h qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) @@ -43,11 +43,11 @@ index 6c359b2f86..edbc8b50f0 100644 qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) diff --git a/Makefile.objs b/Makefile.objs -index fbfbbb7f70..f5f8dbab3b 100644 +index 686247b556..34e62547d8 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -14,6 +14,7 @@ block-obj-y += block.o blockjob.o - block-obj-y += block/ + block-obj-y += block/ scsi/ block-obj-y += qemu-io-cmds.o block-obj-$(CONFIG_REPLICATION) += replication.o +block-obj-y += vma-writer.o @@ -55,7 +55,7 @@ index fbfbbb7f70..f5f8dbab3b 100644 block-obj-m = block/ diff --git a/block/backup.c b/block/backup.c -index 1ede70c061..7c5febc434 100644 +index 8c2967a8cb..0870acdae7 100644 --- a/block/backup.c +++ b/block/backup.c @@ -36,6 +36,7 @@ typedef struct BackupBlockJob { @@ -66,47 +66,48 @@ index 1ede70c061..7c5febc434 100644 BlockdevOnError on_source_error; BlockdevOnError on_target_error; CoRwlock flush_rwlock; -@@ -145,13 +146,24 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, +@@ -135,13 +136,24 @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job, goto out; } -+ int64_t start_sec = start * sectors_per_cluster; ++ if (buffer_is_zero(iov.iov_base, iov.iov_len)) { -- ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size, +- ret = blk_co_pwrite_zeroes(job->target, start, - bounce_qiov.size, BDRV_REQ_MAY_UNMAP); + if (job->dump_cb) { -+ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, NULL); ++ ret = job->dump_cb(job->common.opaque, job->target, start, bounce_qiov.size, NULL); + } + if (job->target) { -+ ret = blk_co_pwrite_zeroes(job->target, start * job->cluster_size, ++ ret = blk_co_pwrite_zeroes(job->target, start, + bounce_qiov.size, BDRV_REQ_MAY_UNMAP); + } } else { -- ret = blk_co_pwritev(job->target, start * job->cluster_size, +- ret = blk_co_pwritev(job->target, start, - bounce_qiov.size, &bounce_qiov, - job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + if (job->dump_cb) { -+ ret = job->dump_cb(job->common.opaque, job->target, start_sec, n, bounce_buffer); ++ ret = job->dump_cb(job->common.opaque, job->target, start, bounce_qiov.size, bounce_buffer); + } + if (job->target) { -+ ret = blk_co_pwritev(job->target, start * job->cluster_size, ++ ret = blk_co_pwritev(job->target, start, + bounce_qiov.size, &bounce_qiov, + job->compress ? BDRV_REQ_WRITE_COMPRESSED : 0); + } } if (ret < 0) { trace_backup_do_cow_write_fail(job, start, ret); -@@ -246,6 +258,9 @@ static void backup_abort(BlockJob *job) +@@ -234,7 +246,9 @@ static void backup_abort(BlockJob *job) static void backup_clean(BlockJob *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common); +- assert(s->target); + if (!s->target) { + return; + } - assert(s->target); blk_unref(s->target); s->target = NULL; -@@ -255,7 +270,9 @@ static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context) + } +@@ -243,7 +257,9 @@ static void backup_attached_aio_context(BlockJob *job, AioContext *aio_context) { BackupBlockJob *s = container_of(job, BackupBlockJob, common); @@ -117,7 +118,7 @@ index 1ede70c061..7c5febc434 100644 } void backup_do_checkpoint(BlockJob *job, Error **errp) -@@ -330,9 +347,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, +@@ -315,9 +331,11 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, if (read) { return block_job_error_action(&job->common, job->on_source_error, true, error); @@ -130,7 +131,7 @@ index 1ede70c061..7c5febc434 100644 } } -@@ -557,6 +576,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -538,6 +556,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_source_error, BlockdevOnError on_target_error, int creation_flags, @@ -138,7 +139,7 @@ index 1ede70c061..7c5febc434 100644 BlockCompletionFunc *cb, void *opaque, int pause_count, BlockJobTxn *txn, Error **errp) -@@ -567,7 +587,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -548,7 +567,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, int ret; assert(bs); @@ -147,7 +148,7 @@ index 1ede70c061..7c5febc434 100644 if (bs == target) { error_setg(errp, "Source and target cannot be the same"); -@@ -580,13 +600,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -561,13 +580,13 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } @@ -163,7 +164,7 @@ index 1ede70c061..7c5febc434 100644 error_setg(errp, "Compression is not supported for this drive %s", bdrv_get_device_name(target)); return NULL; -@@ -596,7 +616,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -577,7 +596,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, return NULL; } @@ -172,7 +173,7 @@ index 1ede70c061..7c5febc434 100644 return NULL; } -@@ -636,15 +656,18 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -617,15 +636,18 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, goto error; } @@ -198,7 +199,7 @@ index 1ede70c061..7c5febc434 100644 job->on_source_error = on_source_error; job->on_target_error = on_target_error; job->sync_mode = sync_mode; -@@ -652,38 +675,55 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -633,36 +655,52 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, sync_bitmap : NULL; job->compress = compress; @@ -208,12 +209,12 @@ index 1ede70c061..7c5febc434 100644 - ret = bdrv_get_info(target, &bdi); - if (ret == -ENOTSUP && !target->backing) { - /* Cluster size is not defined */ -- error_report("WARNING: The target block device doesn't provide " -- "information about the block size and it doesn't have a " -- "backing file. The default block size of %u bytes is " -- "used. If the actual block size of the target exceeds " -- "this default, the backup may be unusable", -- BACKUP_CLUSTER_SIZE_DEFAULT); +- warn_report("The target block device doesn't provide " +- "information about the block size and it doesn't have a " +- "backing file. The default block size of %u bytes is " +- "used. If the actual block size of the target exceeds " +- "this default, the backup may be unusable", +- BACKUP_CLUSTER_SIZE_DEFAULT); - job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT; - } else if (ret < 0 && !target->backing) { - error_setg_errno(errp, -ret, @@ -232,12 +233,12 @@ index 1ede70c061..7c5febc434 100644 + ret = bdrv_get_info(target, &bdi); + if (ret == -ENOTSUP && !target->backing) { + /* Cluster size is not defined */ -+ error_report("WARNING: The target block device doesn't provide " -+ "information about the block size and it doesn't have a " -+ "backing file. The default block size of %u bytes is " -+ "used. If the actual block size of the target exceeds " -+ "this default, the backup may be unusable", -+ BACKUP_CLUSTER_SIZE_DEFAULT); ++ warn_report("The target block device doesn't provide " ++ "information about the block size and it doesn't have a " ++ "backing file. The default block size of %u bytes is " ++ "used. If the actual block size of the target exceeds " ++ "this default, the backup may be unusable", ++ BACKUP_CLUSTER_SIZE_DEFAULT); + job->cluster_size = BACKUP_CLUSTER_SIZE_DEFAULT; + } else if (ret < 0 && !target->backing) { + error_setg_errno(errp, -ret, @@ -274,19 +275,15 @@ index 1ede70c061..7c5febc434 100644 + /* Required permissions are already taken with target's blk_new() */ + block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, + &error_abort); -+ } else { -+ job->common.pause_count = pause_count; + } job->common.len = len; -- job->common.pause_count = pause_count; + job->common.pause_count = pause_count; block_job_txn_add_job(txn, &job->common); - - return &job->common; diff --git a/block/replication.c b/block/replication.c -index 1c41d9e6bf..60c6524417 100644 +index 1b08b242eb..3d101ce6e6 100644 --- a/block/replication.c +++ b/block/replication.c -@@ -531,6 +531,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, +@@ -561,6 +561,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, 0, MIRROR_SYNC_MODE_NONE, NULL, false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL, @@ -295,7 +292,7 @@ index 1c41d9e6bf..60c6524417 100644 if (local_err) { error_propagate(errp, local_err); diff --git a/blockdev.c b/blockdev.c -index 534c00f5da..19a82e8774 100644 +index 3ffd064c48..4b6091afc6 100644 --- a/blockdev.c +++ b/blockdev.c @@ -31,7 +31,6 @@ @@ -314,37 +311,7 @@ index 534c00f5da..19a82e8774 100644 static QTAILQ_HEAD(, BlockDriverState) monitor_bdrv_states = QTAILQ_HEAD_INITIALIZER(monitor_bdrv_states); -@@ -2934,20 +2934,44 @@ out: - aio_context_release(aio_context); - } - -+void block_job_event_cancelled(BlockJob *job); -+void block_job_event_completed(BlockJob *job, const char *msg); -+static void block_job_cb(void *opaque, int ret) -+{ -+ /* Note that this function may be executed from another AioContext besides -+ * the QEMU main loop. If you need to access anything that assumes the -+ * QEMU global mutex, use a BH or introduce a mutex. -+ */ -+ -+ BlockDriverState *bs = opaque; -+ const char *msg = NULL; -+ -+ assert(bs->job); -+ -+ if (ret < 0) { -+ msg = strerror(-ret); -+ } -+ -+ if (block_job_is_cancelled(bs->job)) { -+ block_job_event_cancelled(bs->job); -+ } else { -+ block_job_event_completed(bs->job, msg); -+ } -+} -+ - /* PVE backup related function */ - +@@ -2970,15 +2970,14 @@ out: static struct PVEBackupState { Error *error; bool cancel; @@ -362,45 +329,37 @@ index 534c00f5da..19a82e8774 100644 size_t total; size_t transferred; size_t zero_bytes; -@@ -2957,6 +2981,7 @@ typedef struct PVEBackupDevInfo { - BlockDriverState *bs; - size_t size; - uint8_t dev_id; -+ //bool started; - bool completed; - char targetfile[PATH_MAX]; - BlockDriverState *target; -@@ -2964,13 +2989,79 @@ typedef struct PVEBackupDevInfo { +@@ -2997,6 +2996,71 @@ typedef struct PVEBackupDevInfo { static void pvebackup_run_next_job(void); +static int pvebackup_dump_cb(void *opaque, BlockBackend *target, -+ int64_t sector_num, int n_sectors, -+ unsigned char *buf) ++ uint64_t start, uint64_t bytes, ++ const void *pbuf) +{ ++ const uint64_t size = bytes; ++ const unsigned char *buf = pbuf; + PVEBackupDevInfo *di = opaque; + -+ int size = n_sectors * BDRV_SECTOR_SIZE; + if (backup_state.cancel) { + return size; // return success + } + -+ if (sector_num & 0x7f) { ++ uint64_t cluster_num = start / VMA_CLUSTER_SIZE; ++ if ((cluster_num * VMA_CLUSTER_SIZE) != start) { + if (!backup_state.error) { + error_setg(&backup_state.error, + "got unaligned write inside backup dump " -+ "callback (sector %ld)", sector_num); ++ "callback (sector %ld)", start); + } + return -1; // not aligned to cluster size + } + -+ int64_t cluster_num = sector_num >> 7; -+ + int ret = -1; + + if (backup_state.vmaw) { + size_t zero_bytes = 0; -+ int64_t remaining = n_sectors * BDRV_SECTOR_SIZE; ++ uint64_t remaining = size; + while (remaining > 0) { + ret = vma_writer_write(backup_state.vmaw, di->dev_id, cluster_num, + buf, &zero_bytes); @@ -420,10 +379,11 @@ index 534c00f5da..19a82e8774 100644 + backup_state.zero_bytes += zero_bytes; + if (remaining >= VMA_CLUSTER_SIZE) { + backup_state.transferred += VMA_CLUSTER_SIZE; ++ remaining -= VMA_CLUSTER_SIZE; + } else { + backup_state.transferred += remaining; ++ remaining = 0; + } -+ remaining -= VMA_CLUSTER_SIZE; + } + } + } else { @@ -440,6 +400,9 @@ index 534c00f5da..19a82e8774 100644 + static void pvebackup_cleanup(void) { + qemu_mutex_lock(&backup_state.backup_mutex); +@@ -3008,9 +3072,11 @@ static void pvebackup_cleanup(void) + backup_state.end_time = time(NULL); - if (backup_state.vmaobj) { @@ -452,9 +415,9 @@ index 534c00f5da..19a82e8774 100644 + backup_state.vmaw = NULL; } - if (backup_state.di_list) { -@@ -2985,6 +3076,13 @@ static void pvebackup_cleanup(void) - } + g_list_free(backup_state.di_list); +@@ -3018,6 +3084,13 @@ static void pvebackup_cleanup(void) + qemu_mutex_unlock(&backup_state.backup_mutex); } +static void coroutine_fn backup_close_vma_stream(void *opaque) @@ -466,13 +429,8 @@ index 534c00f5da..19a82e8774 100644 + static void pvebackup_complete_cb(void *opaque, int ret) { - PVEBackupDevInfo *di = opaque; -@@ -2996,14 +3094,18 @@ static void pvebackup_complete_cb(void *opaque, int ret) - ret, strerror(-ret)); - } - -+ BlockDriverState *bs = di->bs; -+ + // This always runs in the main loop +@@ -3034,9 +3107,9 @@ static void pvebackup_complete_cb(void *opaque, int ret) di->bs = NULL; di->target = NULL; @@ -484,12 +442,8 @@ index 534c00f5da..19a82e8774 100644 + qemu_coroutine_enter(co); } -+ block_job_cb(bs, ret); -+ - if (!backup_state.cancel) { - pvebackup_run_next_job(); - } -@@ -3017,14 +3119,9 @@ static void pvebackup_cancel(void *opaque) + // remove self from job queue +@@ -3064,14 +3137,9 @@ static void pvebackup_cancel(void *opaque) error_setg(&backup_state.error, "backup cancelled"); } @@ -506,7 +460,7 @@ index 534c00f5da..19a82e8774 100644 } GList *l = backup_state.di_list; -@@ -3049,19 +3146,15 @@ void qmp_backup_cancel(Error **errp) +@@ -3102,18 +3170,14 @@ void qmp_backup_cancel(Error **errp) Coroutine *co = qemu_coroutine_create(pvebackup_cancel, NULL); qemu_coroutine_enter(co); @@ -524,44 +478,33 @@ index 534c00f5da..19a82e8774 100644 static int config_to_vma(const char *file, BackupFormat format, - Object *vmaobj, - const char *backup_dir, -- Error **errp) -+ const char *backup_dir, VmaWriter *vmaw, -+ Error **errp) ++ const char *backup_dir, VmaWriter *vmaw, + Error **errp) { - char *cdata = NULL; - gsize clen = 0; -@@ -3074,12 +3167,17 @@ static int config_to_vma(const char *file, BackupFormat format, - char *basename = g_path_get_basename(file); + char *cdata = NULL; +@@ -3127,7 +3191,12 @@ static int config_to_vma(const char *file, BackupFormat format, + char *basename = g_path_get_basename(file); - if (format == BACKUP_FORMAT_VMA) { -- vma_object_add_config_file(vmaobj, basename, cdata, clen, errp); -+ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { -+ error_setg(errp, "unable to add %s config data to vma archive", file); -+ g_free(cdata); -+ g_free(basename); -+ return 1; -+ } - } else if (format == BACKUP_FORMAT_DIR) { - char config_path[PATH_MAX]; - snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); - if (!g_file_set_contents(config_path, cdata, clen, &err)) { -- error_setg(errp, "unable to write config file '%s'", config_path); -+ error_setg(errp, "unable to write config file '%s'", config_path); - g_free(cdata); - g_free(basename); - return 1; -@@ -3089,34 +3187,37 @@ static int config_to_vma(const char *file, BackupFormat format, - g_free(basename); - g_free(cdata); - -- return 0; -+ return 0; + if (format == BACKUP_FORMAT_VMA) { +- vma_object_add_config_file(vmaobj, basename, cdata, clen, errp); ++ if (vma_writer_add_config(vmaw, basename, cdata, clen) != 0) { ++ error_setg(errp, "unable to add %s config data to vma archive", file); ++ g_free(cdata); ++ g_free(basename); ++ return 1; ++ } + } else if (format == BACKUP_FORMAT_DIR) { + char config_path[PATH_MAX]; + snprintf(config_path, PATH_MAX, "%s/%s", backup_dir, basename); +@@ -3145,28 +3214,30 @@ static int config_to_vma(const char *file, BackupFormat format, } + void block_job_resume(BlockJob *job); +bool block_job_should_pause(BlockJob *job); static void pvebackup_run_next_job(void) { -- bool cancel = backup_state.error || backup_state.cancel; + qemu_mutex_lock(&backup_state.backup_mutex); + - GList *next = g_list_nth(backup_state.di_list, backup_state.next_job); - while (next) { - PVEBackupDevInfo *di = (PVEBackupDevInfo *)next->data; @@ -572,26 +515,28 @@ index 534c00f5da..19a82e8774 100644 + l = g_list_next(l); if (!di->completed && di->bs && di->bs->job) { BlockJob *job = di->bs->job; -- if (cancel) { -- block_job_cancel(job); + AioContext *aio_context = blk_get_aio_context(job->blk); + aio_context_acquire(aio_context); + qemu_mutex_unlock(&backup_state.backup_mutex); +- if (backup_state.error || backup_state.cancel) { +- block_job_cancel_sync(job); - } else { - block_job_resume(job); + if (block_job_should_pause(job)) { -+ bool cancel = backup_state.error || backup_state.cancel; -+ if (cancel) { -+ block_job_cancel(job); ++ if (backup_state.error || backup_state.cancel) { ++ block_job_cancel_sync(job); + } else { + block_job_resume(job); + } } + aio_context_release(aio_context); return; } - next = g_list_next(next); } -+ - pvebackup_cleanup(); - } + qemu_mutex_unlock(&backup_state.backup_mutex); +@@ -3177,7 +3248,7 @@ static void pvebackup_run_next_job(void) UuidInfo *qmp_backup(const char *backup_file, bool has_format, BackupFormat format, bool has_config_file, const char *config_file, @@ -600,7 +545,7 @@ index 534c00f5da..19a82e8774 100644 bool has_devlist, const char *devlist, bool has_speed, int64_t speed, Error **errp) { -@@ -3124,14 +3225,15 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, +@@ -3185,7 +3256,8 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, BlockDriverState *bs = NULL; const char *backup_dir = NULL; Error *local_err = NULL; @@ -610,15 +555,16 @@ index 534c00f5da..19a82e8774 100644 gchar **devs = NULL; GList *di_list = NULL; GList *l; - UuidInfo *uuid_info; - BlockJob *job; +@@ -3197,7 +3269,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, + backup_state.backup_mutex_initialized = true; + } - if (backup_state.di_list || backup_state.vmaobj) { + if (backup_state.di_list) { error_set(errp, ERROR_CLASS_GENERIC_ERROR, "previous backup not finished"); return NULL; -@@ -3206,40 +3308,28 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, +@@ -3272,40 +3344,28 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, total += size; } @@ -667,27 +613,24 @@ index 534c00f5da..19a82e8774 100644 goto err; } } -@@ -3280,15 +3370,15 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, +@@ -3346,14 +3406,14 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, /* add configuration file to archive */ if (has_config_file) { -- if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -- goto err; -+ if(config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) { -+ goto err; - } +- if(config_to_vma(config_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { ++ if (config_to_vma(config_file, format, backup_dir, vmaw, errp) != 0) { + goto err; + } } /* add firewall file to archive */ if (has_firewall_file) { -- if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { -- goto err; -+ if(config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) { -+ goto err; - } +- if(config_to_vma(firewall_file, format, backup_state.vmaobj, backup_dir, errp) != 0) { ++ if (config_to_vma(firewall_file, format, backup_dir, vmaw, errp) != 0) { + goto err; + } } - /* initialize global backup_state now */ -@@ -3310,11 +3400,12 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, +@@ -3376,12 +3436,13 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, } backup_state.backup_file = g_strdup(backup_file); @@ -698,12 +641,13 @@ index 534c00f5da..19a82e8774 100644 + uuid_copy(backup_state.uuid, uuid); + uuid_unparse_lower(uuid, backup_state.uuid_str); + qemu_mutex_lock(&backup_state.backup_mutex); backup_state.di_list = di_list; - backup_state.next_job = 0; backup_state.total = total; backup_state.transferred = 0; -@@ -3325,21 +3416,16 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, +@@ -3392,21 +3453,16 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, while (l) { PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; l = g_list_next(l); @@ -727,8 +671,8 @@ index 534c00f5da..19a82e8774 100644 + block_job_start(job); } - if (!backup_state.error) { -@@ -3373,9 +3459,10 @@ err: + qemu_mutex_unlock(&backup_state.backup_mutex); +@@ -3442,9 +3498,10 @@ err: g_strfreev(devs); } @@ -742,7 +686,7 @@ index 534c00f5da..19a82e8774 100644 } if (backup_dir) { -@@ -3760,7 +3847,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, +@@ -3829,7 +3886,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, bmap, backup->compress, backup->on_source_error, backup->on_target_error, @@ -751,7 +695,7 @@ index 534c00f5da..19a82e8774 100644 bdrv_unref(target_bs); if (local_err != NULL) { error_propagate(errp, local_err); -@@ -3839,7 +3926,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, +@@ -3908,7 +3965,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn, job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, backup->sync, NULL, backup->compress, backup->on_source_error, backup->on_target_error, @@ -761,22 +705,11 @@ index 534c00f5da..19a82e8774 100644 error_propagate(errp, local_err); } diff --git a/blockjob.c b/blockjob.c -index 764d41863e..cb3741f6dd 100644 +index 2de9f8f4dd..1df33bd194 100644 --- a/blockjob.c +++ b/blockjob.c -@@ -37,8 +37,8 @@ - #include "qemu/timer.h" - #include "qapi-event.h" - --static void block_job_event_cancelled(BlockJob *job); --static void block_job_event_completed(BlockJob *job, const char *msg); -+void block_job_event_cancelled(BlockJob *job); -+void block_job_event_completed(BlockJob *job, const char *msg); - - /* Transactional group of block jobs */ - struct BlockJobTxn { -@@ -473,7 +473,8 @@ void block_job_user_pause(BlockJob *job) - block_job_pause(job); +@@ -757,7 +757,8 @@ void block_job_completed(BlockJob *job, int ret) + } } -static bool block_job_should_pause(BlockJob *job) @@ -785,39 +718,21 @@ index 764d41863e..cb3741f6dd 100644 { return job->pause_count > 0; } -@@ -687,7 +688,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) - } - } - --static void block_job_event_cancelled(BlockJob *job) -+void block_job_event_cancelled(BlockJob *job) - { - if (block_job_is_internal(job)) { - return; -@@ -701,7 +702,7 @@ static void block_job_event_cancelled(BlockJob *job) - &error_abort); - } - --static void block_job_event_completed(BlockJob *job, const char *msg) -+void block_job_event_completed(BlockJob *job, const char *msg) - { - if (block_job_is_internal(job)) { - return; diff --git a/include/block/block_int.h b/include/block/block_int.h -index 19b84b027f..fc1c53a059 100644 +index 1dbbdafd31..2ed3e41437 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h -@@ -59,6 +59,9 @@ +@@ -60,6 +60,9 @@ #define BLOCK_PROBE_BUF_SIZE 512 +typedef int BackupDumpFunc(void *opaque, BlockBackend *be, -+ int64_t sector_num, int n_sectors, unsigned char *buf); ++ uint64_t offset, uint64_t bytes, const void *buf); + enum BdrvTrackedRequestType { BDRV_TRACKED_READ, BDRV_TRACKED_WRITE, -@@ -878,6 +881,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, +@@ -984,6 +987,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, BlockdevOnError on_source_error, BlockdevOnError on_target_error, int creation_flags, @@ -1690,7 +1605,7 @@ index 0000000000..2000889bd3 + diff --git a/vma-writer.c b/vma-writer.c new file mode 100644 -index 0000000000..9001cbdd2b +index 0000000000..fd9567634d --- /dev/null +++ b/vma-writer.c @@ -0,0 +1,771 @@ @@ -2279,7 +2194,7 @@ index 0000000000..9001cbdd2b + +int64_t coroutine_fn +vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, int64_t cluster_num, -+ unsigned char *buf, size_t *zero_bytes) ++ const unsigned char *buf, size_t *zero_bytes) +{ + g_assert(vmaw != NULL); + g_assert(zero_bytes != NULL); @@ -2335,7 +2250,7 @@ index 0000000000..9001cbdd2b + int i; + int bit = 1; + for (i = 0; i < 16; i++) { -+ unsigned char *vmablock = buf + (i*VMA_BLOCK_SIZE); ++ const unsigned char *vmablock = buf + (i*VMA_BLOCK_SIZE); + if (!buffer_is_zero(vmablock, VMA_BLOCK_SIZE)) { + mask |= bit; + memcpy(vmaw->outbuf + vmaw->outbuf_pos, vmablock, @@ -2467,10 +2382,10 @@ index 0000000000..9001cbdd2b +} diff --git a/vma.c b/vma.c new file mode 100644 -index 0000000000..04915427c8 +index 0000000000..1b59fd1555 --- /dev/null +++ b/vma.c -@@ -0,0 +1,757 @@ +@@ -0,0 +1,756 @@ +/* + * VMA: Virtual Machine Archive + * @@ -2492,7 +2407,6 @@ index 0000000000..04915427c8 +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qapi/qmp/qstring.h" -+#include "sysemu/char.h" /* qstring_from_str */ +#include "sysemu/block-backend.h" + +static void help(void) @@ -2772,7 +2686,7 @@ index 0000000000..04915427c8 + printf("DEVINFO %s %zd\n", devfn, di->size); + + bdrv_img_create(devfn, "raw", NULL, NULL, NULL, di->size, -+ flags, &errp, 0); ++ flags, true, &errp); + if (errp) { + g_error("can't create file %s: %s", devfn, + error_get_pretty(errp)); @@ -3230,10 +3144,10 @@ index 0000000000..04915427c8 +} diff --git a/vma.h b/vma.h new file mode 100644 -index 0000000000..fa6f4df7e7 +index 0000000000..c895c97f6d --- /dev/null +++ b/vma.h -@@ -0,0 +1,149 @@ +@@ -0,0 +1,150 @@ +/* + * VMA: Virtual Machine Archive + * @@ -3360,7 +3274,8 @@ index 0000000000..fa6f4df7e7 + size_t size); + +int64_t coroutine_fn vma_writer_write(VmaWriter *vmaw, uint8_t dev_id, -+ int64_t cluster_num, unsigned char *buf, ++ int64_t cluster_num, ++ const unsigned char *buf, + size_t *zero_bytes); + +int coroutine_fn vma_writer_close_stream(VmaWriter *vmaw, uint8_t dev_id); diff --git a/debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch b/debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch deleted file mode 100644 index 0aa7271..0000000 --- a/debian/patches/pve/0029-backup-fix-race-in-backup-stop-command.patch +++ /dev/null @@ -1,253 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Tue, 5 Dec 2017 12:12:15 +0100 -Subject: [PATCH] backup: fix race in backup-stop command - -Signed-off-by: Wolfgang Bumiller ---- - blockdev.c | 90 +++++++++++++++++++++++++++++++++----------------------------- - blockjob.c | 8 +++--- - 2 files changed, 52 insertions(+), 46 deletions(-) - -diff --git a/blockdev.c b/blockdev.c -index 19a82e8774..d3490936d8 100644 ---- a/blockdev.c -+++ b/blockdev.c -@@ -2934,31 +2934,6 @@ out: - aio_context_release(aio_context); - } - --void block_job_event_cancelled(BlockJob *job); --void block_job_event_completed(BlockJob *job, const char *msg); --static void block_job_cb(void *opaque, int ret) --{ -- /* Note that this function may be executed from another AioContext besides -- * the QEMU main loop. If you need to access anything that assumes the -- * QEMU global mutex, use a BH or introduce a mutex. -- */ -- -- BlockDriverState *bs = opaque; -- const char *msg = NULL; -- -- assert(bs->job); -- -- if (ret < 0) { -- msg = strerror(-ret); -- } -- -- if (block_job_is_cancelled(bs->job)) { -- block_job_event_cancelled(bs->job); -- } else { -- block_job_event_completed(bs->job, msg); -- } --} -- - /* PVE backup related function */ - - static struct PVEBackupState { -@@ -2975,6 +2950,8 @@ static struct PVEBackupState { - size_t total; - size_t transferred; - size_t zero_bytes; -+ QemuMutex backup_mutex; -+ bool backup_mutex_initialized; - } backup_state; - - typedef struct PVEBackupDevInfo { -@@ -3055,6 +3032,13 @@ static int pvebackup_dump_cb(void *opaque, BlockBackend *target, - - static void pvebackup_cleanup(void) - { -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ // Avoid race between block jobs and backup-cancel command: -+ if (!backup_state.vmaw) { -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ return; -+ } -+ - backup_state.end_time = time(NULL); - - if (backup_state.vmaw) { -@@ -3064,16 +3048,9 @@ static void pvebackup_cleanup(void) - backup_state.vmaw = NULL; - } - -- if (backup_state.di_list) { -- GList *l = backup_state.di_list; -- while (l) { -- PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; -- l = g_list_next(l); -- g_free(di); -- } -- g_list_free(backup_state.di_list); -- backup_state.di_list = NULL; -- } -+ g_list_free(backup_state.di_list); -+ backup_state.di_list = NULL; -+ qemu_mutex_unlock(&backup_state.backup_mutex); - } - - static void coroutine_fn backup_close_vma_stream(void *opaque) -@@ -3085,6 +3062,8 @@ static void coroutine_fn backup_close_vma_stream(void *opaque) - - static void pvebackup_complete_cb(void *opaque, int ret) - { -+ // This always runs in the main loop -+ - PVEBackupDevInfo *di = opaque; - - di->completed = true; -@@ -3094,8 +3073,6 @@ static void pvebackup_complete_cb(void *opaque, int ret) - ret, strerror(-ret)); - } - -- BlockDriverState *bs = di->bs; -- - di->bs = NULL; - di->target = NULL; - -@@ -3104,7 +3081,11 @@ static void pvebackup_complete_cb(void *opaque, int ret) - qemu_coroutine_enter(co); - } - -- block_job_cb(bs, ret); -+ // remove self from job queue -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ backup_state.di_list = g_list_remove(backup_state.di_list, di); -+ g_free(di); -+ qemu_mutex_unlock(&backup_state.backup_mutex); - - if (!backup_state.cancel) { - pvebackup_run_next_job(); -@@ -3114,6 +3095,12 @@ static void pvebackup_complete_cb(void *opaque, int ret) - static void pvebackup_cancel(void *opaque) - { - backup_state.cancel = true; -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ // Avoid race between block jobs and backup-cancel command: -+ if (!backup_state.vmaw) { -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ return; -+ } - - if (!backup_state.error) { - error_setg(&backup_state.error, "backup cancelled"); -@@ -3131,13 +3118,17 @@ static void pvebackup_cancel(void *opaque) - if (!di->completed && di->bs) { - BlockJob *job = di->bs->job; - if (job) { -+ AioContext *aio_context = blk_get_aio_context(job->blk); -+ aio_context_acquire(aio_context); - if (!di->completed) { -- block_job_cancel_sync(job); -+ block_job_cancel(job); - } -+ aio_context_release(aio_context); - } - } - } - -+ qemu_mutex_unlock(&backup_state.backup_mutex); - pvebackup_cleanup(); - } - -@@ -3193,24 +3184,31 @@ static int config_to_vma(const char *file, BackupFormat format, - bool block_job_should_pause(BlockJob *job); - static void pvebackup_run_next_job(void) - { -+ qemu_mutex_lock(&backup_state.backup_mutex); -+ - GList *l = backup_state.di_list; - while (l) { - PVEBackupDevInfo *di = (PVEBackupDevInfo *)l->data; - l = g_list_next(l); - if (!di->completed && di->bs && di->bs->job) { - BlockJob *job = di->bs->job; -+ AioContext *aio_context = blk_get_aio_context(job->blk); -+ aio_context_acquire(aio_context); -+ qemu_mutex_unlock(&backup_state.backup_mutex); - if (block_job_should_pause(job)) { -- bool cancel = backup_state.error || backup_state.cancel; -- if (cancel) { -- block_job_cancel(job); -+ if (backup_state.error || backup_state.cancel) { -+ block_job_cancel_sync(job); - } else { - block_job_resume(job); - } - } -+ aio_context_release(aio_context); - return; - } - } -+ qemu_mutex_unlock(&backup_state.backup_mutex); - -+ // no more jobs, run the cleanup - pvebackup_cleanup(); - } - -@@ -3233,6 +3231,11 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - UuidInfo *uuid_info; - BlockJob *job; - -+ if (!backup_state.backup_mutex_initialized) { -+ qemu_mutex_init(&backup_state.backup_mutex); -+ backup_state.backup_mutex_initialized = true; -+ } -+ - if (backup_state.di_list) { - error_set(errp, ERROR_CLASS_GENERIC_ERROR, - "previous backup not finished"); -@@ -3405,6 +3408,7 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - uuid_copy(backup_state.uuid, uuid); - uuid_unparse_lower(uuid, backup_state.uuid_str); - -+ qemu_mutex_lock(&backup_state.backup_mutex); - backup_state.di_list = di_list; - - backup_state.total = total; -@@ -3428,6 +3432,8 @@ UuidInfo *qmp_backup(const char *backup_file, bool has_format, - block_job_start(job); - } - -+ qemu_mutex_unlock(&backup_state.backup_mutex); -+ - if (!backup_state.error) { - pvebackup_run_next_job(); // run one job - } -diff --git a/blockjob.c b/blockjob.c -index cb3741f6dd..199d5852db 100644 ---- a/blockjob.c -+++ b/blockjob.c -@@ -37,8 +37,8 @@ - #include "qemu/timer.h" - #include "qapi-event.h" - --void block_job_event_cancelled(BlockJob *job); --void block_job_event_completed(BlockJob *job, const char *msg); -+static void block_job_event_cancelled(BlockJob *job); -+static void block_job_event_completed(BlockJob *job, const char *msg); - - /* Transactional group of block jobs */ - struct BlockJobTxn { -@@ -688,7 +688,7 @@ static void block_job_iostatus_set_err(BlockJob *job, int error) - } - } - --void block_job_event_cancelled(BlockJob *job) -+static void block_job_event_cancelled(BlockJob *job) - { - if (block_job_is_internal(job)) { - return; -@@ -702,7 +702,7 @@ void block_job_event_cancelled(BlockJob *job) - &error_abort); - } - --void block_job_event_completed(BlockJob *job, const char *msg) -+static void block_job_event_completed(BlockJob *job, const char *msg) - { - if (block_job_is_internal(job)) { - return; --- -2.11.0 - diff --git a/debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch b/debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch deleted file mode 100644 index d75114d..0000000 --- a/debian/patches/pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch +++ /dev/null @@ -1,165 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Thu, 15 Feb 2018 11:07:56 +0100 -Subject: [PATCH] vma: add throttling options to drive mapping fifo protocol - -Signed-off-by: Wolfgang Bumiller ---- - vma.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- - 1 file changed, 73 insertions(+), 9 deletions(-) - -diff --git a/vma.c b/vma.c -index 04915427c8..91ae14cdc3 100644 ---- a/vma.c -+++ b/vma.c -@@ -18,7 +18,9 @@ - #include "qemu-common.h" - #include "qemu/error-report.h" - #include "qemu/main-loop.h" -+#include "qemu/cutils.h" - #include "qapi/qmp/qstring.h" -+#include "qapi/qmp/qint.h" - #include "sysemu/char.h" /* qstring_from_str */ - #include "sysemu/block-backend.h" - -@@ -133,9 +135,39 @@ typedef struct RestoreMap { - char *devname; - char *path; - char *format; -+ uint64_t throttling_bps; -+ char *throttling_group; - bool write_zero; - } RestoreMap; - -+static bool try_parse_option(char **line, const char *optname, char **out, const char *inbuf) { -+ size_t optlen = strlen(optname); -+ if (strncmp(*line, optname, optlen) != 0 || (*line)[optlen] != '=') { -+ return false; -+ } -+ if (*out) { -+ g_error("read map failed - duplicate value for option '%s'", optname); -+ } -+ char *value = (*line) + optlen + 1; /* including a '=' */ -+ char *colon = strchr(value, ':'); -+ if (!colon) { -+ g_error("read map failed - option '%s' not terminated ('%s')", -+ optname, inbuf); -+ } -+ *line = colon+1; -+ *out = g_strndup(value, colon - value); -+ return true; -+} -+ -+static uint64_t verify_u64(const char *text) { -+ uint64_t value; -+ const char *endptr = NULL; -+ if (qemu_strtou64(text, &endptr, 0, &value) != 0 || !endptr || *endptr) { -+ g_error("read map failed - not a number: %s", text); -+ } -+ return value; -+} -+ - static int extract_content(int argc, char **argv) - { - int c, ret = 0; -@@ -209,6 +241,9 @@ static int extract_content(int argc, char **argv) - while (1) { - char inbuf[8192]; - char *line = fgets(inbuf, sizeof(inbuf), map); -+ char *format = NULL; -+ char *bps = NULL; -+ char *group = NULL; - if (!line || line[0] == '\0' || !strcmp(line, "done\n")) { - break; - } -@@ -220,15 +255,19 @@ static int extract_content(int argc, char **argv) - } - } - -- char *format = NULL; -- if (strncmp(line, "format=", sizeof("format=")-1) == 0) { -- format = line + sizeof("format=")-1; -- char *colon = strchr(format, ':'); -- if (!colon) { -- g_error("read map failed - found only a format ('%s')", inbuf); -+ while (1) { -+ if (!try_parse_option(&line, "format", &format, inbuf) && -+ !try_parse_option(&line, "throttling.bps", &bps, inbuf) && -+ !try_parse_option(&line, "throttling.group", &group, inbuf)) -+ { -+ break; - } -- format = g_strndup(format, colon - format); -- line = colon+1; -+ } -+ -+ uint64_t bps_value = 0; -+ if (bps) { -+ bps_value = verify_u64(bps); -+ g_free(bps); - } - - const char *path; -@@ -254,6 +293,8 @@ static int extract_content(int argc, char **argv) - map->devname = g_strdup(devname); - map->path = g_strdup(path); - map->format = format; -+ map->throttling_bps = bps_value; -+ map->throttling_group = group; - map->write_zero = write_zero; - - g_hash_table_insert(devmap, map->devname, map); -@@ -281,6 +322,8 @@ static int extract_content(int argc, char **argv) - } else if (di) { - char *devfn = NULL; - const char *format = NULL; -+ uint64_t throttling_bps = 0; -+ const char *throttling_group = NULL; - int flags = BDRV_O_RDWR | BDRV_O_NO_FLUSH; - bool write_zero = true; - -@@ -292,6 +335,8 @@ static int extract_content(int argc, char **argv) - } - devfn = map->path; - format = map->format; -+ throttling_bps = map->throttling_bps; -+ throttling_group = map->throttling_group; - write_zero = map->write_zero; - } else { - devfn = g_strdup_printf("%s/tmp-disk-%s.raw", -@@ -328,12 +373,31 @@ static int extract_content(int argc, char **argv) - qdict_put(options, "driver", qstring_from_str("raw")); - } - -- - if (errp || !(blk = blk_new_open(devfn, NULL, options, flags, &errp))) { - g_error("can't open file %s - %s", devfn, - error_get_pretty(errp)); - } - -+ if (throttling_group) { -+ blk_io_limits_enable(blk, throttling_group); -+ } -+ -+ if (throttling_bps) { -+ if (!throttling_group) { -+ blk_io_limits_enable(blk, devfn); -+ } -+ -+ ThrottleConfig cfg; -+ throttle_config_init(&cfg); -+ cfg.buckets[THROTTLE_BPS_WRITE].avg = throttling_bps; -+ Error *err = NULL; -+ if (!throttle_is_valid(&cfg, &err)) { -+ error_report_err(err); -+ g_error("failed to apply throttling"); -+ } -+ blk_set_io_limits(blk, &cfg); -+ } -+ - if (vma_reader_register_bs(vmar, i, blk, write_zero, &errp) < 0) { - g_error("%s", error_get_pretty(errp)); - } --- -2.11.0 - diff --git a/debian/patches/series b/debian/patches/series index c49ca96..954ea11 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -1,4 +1,4 @@ -pve/0001-fr-ca-keymap-corrections.patch +pve/0001-block-file-change-locking-default-to-off.patch pve/0002-Adjust-network-script-path-to-etc-kvm.patch pve/0003-qemu-img-return-success-on-info-without-snapshots.patch pve/0004-use-kvm-by-default.patch @@ -15,65 +15,15 @@ pve/0014-use-whitespace-between-VERSION-and-PKGVERSION.patch pve/0015-vnc-altgr-emulation.patch pve/0016-vnc-make-x509-imply-tls-again.patch pve/0017-vnc-PVE-VNC-authentication.patch -pve/0018-migrate-fix-possible-unitialised-return-value.patch -pve/0019-block-rbd-disable-rbd_cache_writethrough_until_flush.patch -pve/0020-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch -pve/0021-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch -pve/0022-glusterfs-no-default-logfile-if-daemonized.patch -pve/0023-glusterfs-allow-partial-reads.patch -pve/0024-block-add-the-zeroinit-block-driver-filter.patch -pve/0025-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch -pve/0026-backup-modify-job-api.patch -pve/0027-backup-introduce-vma-archive-format.patch -pve/0028-adding-old-vma-files.patch -pve/0029-backup-fix-race-in-backup-stop-command.patch -pve/0030-vma-add-throttling-options-to-drive-mapping-fifo-pro.patch +pve/0018-block-rbd-disable-rbd_cache_writethrough_until_flush.patch +pve/0019-block-snapshot-qmp_snapshot_drive-add-aiocontext.patch +pve/0020-block-snapshot-qmp_delete_drive_snapshot-add-aiocont.patch +pve/0021-glusterfs-no-default-logfile-if-daemonized.patch +pve/0022-glusterfs-allow-partial-reads.patch +pve/0023-block-add-the-zeroinit-block-driver-filter.patch +pve/0024-qemu-img-dd-add-osize-and-read-from-to-stdin-stdout.patch +pve/0025-backup-modify-job-api.patch +pve/0026-backup-introduce-vma-archive-format.patch +pve/0027-adding-old-vma-files.patch extra/0001-Revert-target-i386-disable-LINT0-after-reset.patch -extra/0002-virtio-serial-fix-segfault-on-disconnect.patch -extra/0003-megasas-always-store-SCSIRequest-into-MegasasCmd.patch -extra/0004-slirp-check-len-against-dhcp-options-array-end.patch -extra/0005-IDE-Do-not-flush-empty-CDROM-drives.patch -extra/0006-bitmap-add-bitmap_copy_and_clear_atomic.patch -extra/0007-memory-add-support-getting-and-using-a-dirty-bitmap-.patch -extra/0008-vga-add-vga_scanline_invalidated-helper.patch -extra/0009-vga-make-display-updates-thread-safe.patch -extra/0010-vga-fix-display-update-region-calculation.patch -extra/0011-vga-fix-display-update-region-calculation-split-scre.patch -extra/0012-vga-stop-passing-pointers-to-vga_draw_line-functions.patch -extra/0013-multiboot-validate-multiboot-header-address-values.patch -extra/0014-virtio-fix-descriptor-counting-in-virtqueue_pop.patch -extra/0015-nbd-server-CVE-2017-15119-Reject-options-larger-than.patch -extra/0016-vga-migration-Update-memory-map-in-post_load.patch -extra/0017-vga-drop-line_offset-variable.patch -extra/0018-vga-handle-cirrus-vbe-mode-wraparounds.patch -extra/0019-vga-add-ram_addr_t-cast.patch -extra/0020-vga-fix-region-checks-in-wraparound-case.patch -extra/0021-io-monitor-encoutput-buffer-size-from-websocket-GSou.patch -extra/0022-9pfs-use-g_malloc0-to-allocate-space-for-xattr.patch -extra/0023-cirrus-fix-oob-access-in-mode4and5-write-functions.patch -extra/0024-virtio-check-VirtQueue-Vring-object-is-set.patch -extra/0025-block-gluster-glfs_lseek-workaround.patch -extra/0026-gluster-add-support-for-PREALLOC_MODE_FALLOC.patch -extra/0027-target-i386-Use-host_vendor_fms-in-max_x86_cpu_initf.patch -extra/0028-target-i386-Define-CPUID_MODEL_ID_SZ-macro.patch -extra/0029-target-i386-Don-t-use-x86_cpu_load_def-on-max-CPU-mo.patch -extra/0030-i386-Change-X86CPUDefinition-model_id-to-const-char.patch -extra/0031-i386-Add-support-for-SPEC_CTRL-MSR.patch -extra/0032-i386-Add-spec-ctrl-CPUID-bit.patch -extra/0033-i386-Add-FEAT_8000_0008_EBX-CPUID-feature-word.patch -extra/0034-i386-Add-new-IBRS-versions-of-Intel-CPU-models.patch -extra/0035-ratelimit-don-t-align-wait-time-with-slices.patch -extra/0036-nbd-make-it-thread-safe-fix-qcow2-over-nbd.patch -extra/0037-nbd-strict-nbd_wr_syncv.patch -extra/0038-nbd-read_sync-and-friends-return-0-on-success.patch -extra/0039-nbd-make-nbd_drop-public.patch -extra/0040-nbd-server-get-rid-of-nbd_negotiate_read-and-friends.patch -extra/0041-nbd-client-Fix-regression-when-server-sends-garbage.patch -extra/0042-fix-build-failure-in-nbd_read_reply_entry.patch -extra/0043-nbd-client-avoid-spurious-qio_channel_yield-re-entry.patch -extra/0044-nbd-client-avoid-read_reply_co-entry-if-send-failed.patch -extra/0045-qemu-iotests-improve-nbd-fault-injector.py-startup-p.patch -extra/0046-qemu-iotests-test-NBD-over-UNIX-domain-sockets-in-08.patch -extra/0047-block-nbd-client-nbd_co_send_request-fix-return-code.patch -extra/0048-target-i386-cpu-Add-new-EPYC-CPU-model.patch -extra/0049-i386-Add-EPYC-IBPB-CPU-model.patch +extra/0002-ratelimit-don-t-align-wait-time-with-slices.patch diff --git a/keycodemapdb/LICENSE.BSD b/keycodemapdb/LICENSE.BSD new file mode 100644 index 0000000..ec1a29d --- /dev/null +++ b/keycodemapdb/LICENSE.BSD @@ -0,0 +1,27 @@ +Copyright (c) Individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of PyCA Cryptography nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/keycodemapdb/LICENSE.GPL2 b/keycodemapdb/LICENSE.GPL2 new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/keycodemapdb/LICENSE.GPL2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/keycodemapdb/README b/keycodemapdb/README new file mode 100644 index 0000000..a3c3c9f --- /dev/null +++ b/keycodemapdb/README @@ -0,0 +1,114 @@ + Key code / scan code / key symbol mapping database + ================================================== + +This module provides a database that maps between different +key code / scan code / key symbol sets: + + - Linux evdev + - OS-X + - AT Set 1 + - AT Set 2 + - AT Set 3 + - XT + - Linux XT KBD driver + - USB HID + - Win32 + - XWin XT + - XKBD XT + - Xorg Evdev + - Xorg KBD + - Xorg OS-X + - XOrg Cygwin + - RFB + +Licensing +--------- + +The contents of this package are dual licensed under the terms of: + + - GNU General Public License (version 2 or later) + - 3-clause BSD License + +The output files generated by keymap-gen may be distributed & used under +the terms of either of the above licenses. + +Data formats +------------ + +The following output formats are possible + + - Code map + + An array mapping between key code sets values + + Indexes in the array are values from the source code set. + Entries in the array are values from the target code set + + + - Code table + + An array listing all values in a key code set + + Indexes in the array are simply a numeric counter + Entries in the array are values from the key code set + + The size of the array matches the total number of entries in + the keycode database. + + + - Name map + + An array mapping between key code sets values and names + + Indexes in the array are values from the source code set + Entries in the array are names from the target code set + + + - Name table + + An array listing all names in a key code set + + Indexes in the array are simply a numeric counter + Entries in the array are values from the key code set + + The size of the array matches the total number of entries in + the keycode database. + + +Output languages +---------------- + +The tool is capable of generating data tables for the following +programming languages / environments + + - Standard C + - GLib2 (standard C, but with GLib2 data types) + - Python + - Perl + + +Usage +----- + +Map values from AT Set 1 to USB HID, generating tables for the +C programming language + + $ keymap-gen --lang stdc code-map data/keymaps.csv atset1 usb + +Generate a tables of names for Linux key codes, OS-X key codes, +in python - equivalent array indexes map between the two sets. +A variable name override is used + + $ keymap-gen --varname linux_keycodes --lang stdc \ + code-table data/keymaps.csv linux + $ keymap-gen --varname osx_keycodes --lang stdc \ + code-table data/keymaps.csv os-x + +Generate a mapping from XOrg XWin values to Win32 names + + $ keymap-gen --lang perl name-map data/keymaps.csv xorgxwin win32 + +Generate a table of names for Linux key codes in Perl + + $ keymap-gen --lang perl name-table data/keymaps.csv linux + diff --git a/keycodemapdb/data/README b/keycodemapdb/data/README new file mode 100644 index 0000000..6b56534 --- /dev/null +++ b/keycodemapdb/data/README @@ -0,0 +1,89 @@ +This directory contains the raw data for mapping between different +keyboard codes. Naming if often based on the US keyboard layout, but +does not indicate the symbol actually generated by the key. + +The columns currently in this data set are: + +Linux +----- + +Name and value of the hardware independent keycodes used by the linux +kernel and exposed through the input subsystem. + +References: linux/input.h + +macOS +----- + +Low level key codes as exposed by Mac OS X/macOS. + +References: Carbon/HIToolbox/Events.h + +PC scan code sets +----------------- + +Scan codes for the three orignal PC keyboard generations: + + Set 1: XT + Set 2: AT + Set 3: PS/2 + +The sets include codes for modern keys as well and not just the keys +present on those original keyboards. + +References: linux/drivers/input/keyboard/atkbd.c + +USB HID +------- + +Codes as specified by the HID profile in USB. + +References: linux/drivers/hid/usbhid/usbkbd.c + +Windows Virtual-key codes +------------------------- + +The low level, hardware independent "VKEYs" exposed by Windows. + +References: mingw32/winuser.h + +XWin XT +------- + +X11 keycodes generated by the XWin server. Based on the XT scan code +set. + +References: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h} + +Xfree86 KBD XT +-------------- + +X11 keycodes generated by the Xfree86 keyboard drivers. Based on the XT +scan code set. + +References: xf86-input-keyboard/src/at_scancode.c + +X11 keysyms +----------- + +Corresponding X11 keysym value(s) for a US keyboard layout. + +WARNING: These columns represent symbols, not physical keys, and should + be used with extreme care. + +References: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h + +HTML KeyboardEvent.code +----------------------- + +Key codes seen in the KeyboardEvent.code attribute as part of the +UI Events specification. + +References: https://www.w3.org/TR/uievents-code/ + +XKEYBOARD key names +------------------- + +Hardware independent key names as used in the XKEYBOARD extension. + +References: /usr/share/X11/xkb/keycodes/ diff --git a/keycodemapdb/data/keymaps.csv b/keycodemapdb/data/keymaps.csv new file mode 100644 index 0000000..0ef2ec1 --- /dev/null +++ b/keycodemapdb/data/keymaps.csv @@ -0,0 +1,539 @@ +"Linux Name","Linux Keycode","OS-X Name","OS-X Keycode","AT set1 keycode","AT set2 keycode","AT set3 keycode","USB Keycodes","Win32 Name","Win32 Keycode","Xwin XT","Xfree86 KBD XT","X11 keysym name","X11 keysym","HTML code","XKB key name","QEMU QKeyCode","Sun KBD","Apple ADB" +KEY_RESERVED,0,,0xff,,,,,,,,,,,,,unmapped,,0xff +KEY_ESC,1,Escape,0x35,0x01,0x76,0x08,41,VK_ESCAPE,0x1b,1,1,XK_Escape,0xff1b,Escape,ESC,esc,0x1d,0x35 +KEY_1,2,ANSI_1,0x12,0x02,0x16,0x16,30,VK_1,0x31,2,2,XK_1,0x0031,Digit1,AE01,1,0x1e,0x12 +KEY_1,2,ANSI_1,0x12,0x02,0x16,0x16,30,VK_1,0x31,2,2,XK_exclam,0x0021,Digit1,AE01,1,0x1e,0x12 +KEY_2,3,ANSI_2,0x13,0x03,0x1e,0x1e,31,VK_2,0x32,3,3,XK_2,0x0032,Digit2,AE02,2,0x1f,0x13 +KEY_2,3,ANSI_2,0x13,0x03,0x1e,0x1e,31,VK_2,0x32,3,3,XK_at,0x0040,Digit2,AE02,2,0x1f,0x13 +KEY_3,4,ANSI_3,0x14,0x04,0x26,0x26,32,VK_3,0x33,4,4,XK_3,0x0033,Digit3,AE03,3,0x20,0x14 +KEY_3,4,ANSI_3,0x14,0x04,0x26,0x26,32,VK_3,0x33,4,4,XK_numbersign,0x0023,Digit3,AE03,3,0x20,0x14 +KEY_4,5,ANSI_4,0x15,0x05,0x25,0x25,33,VK_4,0x34,5,5,XK_4,0x0034,Digit4,AE04,4,0x21,0x15 +KEY_4,5,ANSI_4,0x15,0x05,0x25,0x25,33,VK_4,0x34,5,5,XK_dollar,0x0024,Digit4,AE04,4,0x21,0x15 +KEY_5,6,ANSI_5,0x17,0x06,0x2e,0x2e,34,VK_5,0x35,6,6,XK_5,0x0035,Digit5,AE05,5,0x22,0x17 +KEY_5,6,ANSI_5,0x17,0x06,0x2e,0x2e,34,VK_5,0x35,6,6,XK_percent,0x0025,Digit5,AE05,5,0x22,0x17 +KEY_6,7,ANSI_6,0x16,0x07,0x36,0x36,35,VK_6,0x36,7,7,XK_6,0x0036,Digit6,AE06,6,0x23,0x16 +KEY_6,7,ANSI_6,0x16,0x07,0x36,0x36,35,VK_6,0x36,7,7,XK_asciicircum,0x005e,Digit6,AE06,6,0x23,0x16 +KEY_7,8,ANSI_7,0x1a,0x08,0x3d,0x3d,36,VK_7,0x37,8,8,XK_7,0x0037,Digit7,AE07,7,0x24,0x1a +KEY_7,8,ANSI_7,0x1a,0x08,0x3d,0x3d,36,VK_7,0x37,8,8,XK_ampersand,0x0026,Digit7,AE07,7,0x24,0x1a +KEY_8,9,ANSI_8,0x1c,0x09,0x3e,0x3e,37,VK_8,0x38,9,9,XK_8,0x0038,Digit8,AE08,8,0x25,0x1c +KEY_8,9,ANSI_8,0x1c,0x09,0x3e,0x3e,37,VK_8,0x38,9,9,XK_asterisk,0x002a,Digit8,AE08,8,0x25,0x1c +KEY_9,10,ANSI_9,0x19,0x0a,0x46,0x46,38,VK_9,0x39,10,10,XK_9,0x0039,Digit9,AE09,9,0x26,0x19 +KEY_9,10,ANSI_9,0x19,0x0a,0x46,0x46,38,VK_9,0x39,10,10,XK_parenleft,0x0028,Digit9,AE09,9,0x26,0x19 +KEY_0,11,ANSI_0,0x1d,0x0b,0x45,0x45,39,VK_0,0x30,11,11,XK_0,0x0030,Digit0,AE10,0,0x27,0x1d +KEY_0,11,ANSI_0,0x1d,0x0b,0x45,0x45,39,VK_0,0x30,11,11,XK_parenright,0x0029,Digit0,AE10,0,0x27,0x1d +KEY_MINUS,12,ANSI_Minus,0x1b,0x0c,0x4e,0x4e,45,VK_OEM_MINUS,0xbd,12,12,XK_minus,0x002d,Minus,AE11,minus,0x28,0x1b +KEY_MINUS,12,ANSI_Minus,0x1b,0x0c,0x4e,0x4e,45,VK_OEM_MINUS,0xbd,12,12,XK_underscore,0x005f,Minus,AE11,minus,0x28,0x1b +KEY_EQUAL,13,ANSI_Equal,0x18,0x0d,0x55,0x55,46,VK_OEM_PLUS,0xbb,13,13,XK_equal,0x003d,Equal,AE12,equal,0x29,0x18 +KEY_EQUAL,13,ANSI_Equal,0x18,0x0d,0x55,0x55,46,VK_OEM_PLUS,0xbb,13,13,XK_plus,0x002b,Equal,AE12,equal,0x29,0x18 +KEY_BACKSPACE,14,Delete,0x33,0x0e,0x66,0x66,42,VK_BACK,0x08,14,14,XK_BackSpace,0xff08,Backspace,BKSP,backspace,0x2b,0x33 +KEY_TAB,15,Tab,0x30,0x0f,0x0d,0x0d,43,VK_TAB,0x09,15,15,XK_Tab,0xff09,Tab,TAB,tab,0x35,0x30 +KEY_Q,16,ANSI_Q,0xc,0x10,0x15,0x15,20,VK_Q,0x51,16,16,XK_Q,0x0051,KeyQ,AD01,q,0x36,0xc +KEY_Q,16,ANSI_Q,0xc,0x10,0x15,0x15,20,VK_Q,0x51,16,16,XK_q,0x0071,KeyQ,AD01,q,0x36,0xc +KEY_W,17,ANSI_W,0xd,0x11,0x1d,0x1d,26,VK_W,0x57,17,17,XK_W,0x0057,KeyW,AD02,w,0x37,0xd +KEY_W,17,ANSI_W,0xd,0x11,0x1d,0x1d,26,VK_W,0x57,17,17,XK_w,0x0077,KeyW,AD02,w,0x37,0xd +KEY_E,18,ANSI_E,0xe,0x12,0x24,0x24,8,VK_E,0x45,18,18,XK_E,0x0045,KeyE,AD03,e,0x38,0xe +KEY_E,18,ANSI_E,0xe,0x12,0x24,0x24,8,VK_E,0x45,18,18,XK_e,0x0065,KeyE,AD03,e,0x38,0xe +KEY_R,19,ANSI_R,0xf,0x13,0x2d,0x2d,21,VK_R,0x52,19,19,XK_R,0x0052,KeyR,AD04,r,0x39,0xf +KEY_R,19,ANSI_R,0xf,0x13,0x2d,0x2d,21,VK_R,0x52,19,19,XK_r,0x0072,KeyR,AD04,r,0x39,0xf +KEY_T,20,ANSI_T,0x11,0x14,0x2c,0x2c,23,VK_T,0x54,20,20,XK_T,0x0054,KeyT,AD05,t,0x3a,0x11 +KEY_T,20,ANSI_T,0x11,0x14,0x2c,0x2c,23,VK_T,0x54,20,20,XK_t,0x0074,KeyT,AD05,t,0x3a,0x11 +KEY_Y,21,ANSI_Y,0x10,0x15,0x35,0x35,28,VK_Y,0x59,21,21,XK_Y,0x0059,KeyY,AD06,y,0x3b,0x10 +KEY_Y,21,ANSI_Y,0x10,0x15,0x35,0x35,28,VK_Y,0x59,21,21,XK_y,0x0079,KeyY,AD06,y,0x3b,0x10 +KEY_U,22,ANSI_U,0x20,0x16,0x3c,0x3c,24,VK_U,0x55,22,22,XK_U,0x0055,KeyU,AD07,u,0x3c,0x20 +KEY_U,22,ANSI_U,0x20,0x16,0x3c,0x3c,24,VK_U,0x55,22,22,XK_u,0x0075,KeyU,AD07,u,0x3c,0x20 +KEY_I,23,ANSI_I,0x22,0x17,0x43,0x43,12,VK_I,0x49,23,23,XK_I,0x0049,KeyI,AD08,i,0x3d,0x22 +KEY_I,23,ANSI_I,0x22,0x17,0x43,0x43,12,VK_I,0x49,23,23,XK_i,0x0069,KeyI,AD08,i,0x3d,0x22 +KEY_O,24,ANSI_O,0x1f,0x18,0x44,0x44,18,VK_O,0x4f,24,24,XK_O,0x004f,KeyO,AD09,o,0x3e,0x1f +KEY_O,24,ANSI_O,0x1f,0x18,0x44,0x44,18,VK_O,0x4f,24,24,XK_o,0x006f,KeyO,AD09,o,0x3e,0x1f +KEY_P,25,ANSI_P,0x23,0x19,0x4d,0x4d,19,VK_P,0x50,25,25,XK_P,0x0050,KeyP,AD10,p,0x3f,0x23 +KEY_P,25,ANSI_P,0x23,0x19,0x4d,0x4d,19,VK_P,0x50,25,25,XK_p,0x0070,KeyP,AD10,p,0x3f,0x23 +KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,0x1a,0x54,0x54,47,VK_OEM_4,0xdb,26,26,XK_bracketleft,0x005b,BracketLeft,AD11,bracket_left,0x40,0x21 +KEY_LEFTBRACE,26,ANSI_LeftBracket,0x21,0x1a,0x54,0x54,47,VK_OEM_4,0xdb,26,26,XK_braceleft,0x007b,BracketLeft,AD11,bracket_left,0x40,0x21 +KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,0x1b,0x5b,0x5b,48,VK_OEM_6,0xdd,27,27,XK_bracketright,0x005d,BracketRight,AD12,bracket_right,0x41,0x1e +KEY_RIGHTBRACE,27,ANSI_RightBracket,0x1e,0x1b,0x5b,0x5b,48,VK_OEM_6,0xdd,27,27,XK_braceright,0x007d,BracketRight,AD12,bracket_right,0x41,0x1e +KEY_ENTER,28,Return,0x24,0x1c,0x5a,0x5a,40,VK_RETURN,0x0d,28,28,XK_Return,0xff0d,Enter,RTRN,ret,0x59,0x24 +KEY_LEFTCTRL,29,Control,0x3b,0x1d,0x14,0x11,224,VK_LCONTROL,0xa2,29,29,XK_Control_L,0xffe3,ControlLeft,LCTL,ctrl,0x4c,0x36 +KEY_LEFTCTRL,29,Control,0x3b,0x1d,0x14,0x11,224,VK_CONTROL,0x11,29,29,XK_Control_L,0xffe3,ControlLeft,LCTL,ctrl,0x4c,0x36 +KEY_A,30,ANSI_A,0x0,0x1e,0x1c,0x1c,4,VK_A,0x41,30,30,XK_A,0x0041,KeyA,AC01,a,0x4d,0x0 +KEY_A,30,ANSI_A,0x0,0x1e,0x1c,0x1c,4,VK_A,0x41,30,30,XK_a,0x0061,KeyA,AC01,a,0x4d,0x0 +KEY_S,31,ANSI_S,0x1,0x1f,0x1b,0x1b,22,VK_S,0x53,31,31,XK_S,0x0053,KeyS,AC02,s,0x4e,0x1 +KEY_S,31,ANSI_S,0x1,0x1f,0x1b,0x1b,22,VK_S,0x53,31,31,XK_s,0x0073,KeyS,AC02,s,0x4e,0x1 +KEY_D,32,ANSI_D,0x2,0x20,0x23,0x23,7,VK_D,0x44,32,32,XK_D,0x0044,KeyD,AC03,d,0x4f,0x2 +KEY_D,32,ANSI_D,0x2,0x20,0x23,0x23,7,VK_D,0x44,32,32,XK_d,0x0064,KeyD,AC03,d,0x4f,0x2 +KEY_F,33,ANSI_F,0x3,0x21,0x2b,0x2b,9,VK_F,0x46,33,33,XK_F,0x0046,KeyF,AC04,f,0x50,0x3 +KEY_F,33,ANSI_F,0x3,0x21,0x2b,0x2b,9,VK_F,0x46,33,33,XK_f,0x0066,KeyF,AC04,f,0x50,0x3 +KEY_G,34,ANSI_G,0x5,0x22,0x34,0x34,10,VK_G,0x47,34,34,XK_G,0x0047,KeyG,AC05,g,0x51,0x5 +KEY_G,34,ANSI_G,0x5,0x22,0x34,0x34,10,VK_G,0x47,34,34,XK_g,0x0067,KeyG,AC05,g,0x51,0x5 +KEY_H,35,ANSI_H,0x4,0x23,0x33,0x33,11,VK_H,0x48,35,35,XK_H,0x0048,KeyH,AC06,h,0x52,0x4 +KEY_H,35,ANSI_H,0x4,0x23,0x33,0x33,11,VK_H,0x48,35,35,XK_h,0x0068,KeyH,AC06,h,0x52,0x4 +KEY_J,36,ANSI_J,0x26,0x24,0x3b,0x3b,13,VK_J,0x4a,36,36,XK_J,0x004a,KeyJ,AC07,j,0x53,0x26 +KEY_J,36,ANSI_J,0x26,0x24,0x3b,0x3b,13,VK_J,0x4a,36,36,XK_j,0x006a,KeyJ,AC07,j,0x53,0x26 +KEY_K,37,ANSI_K,0x28,0x25,0x42,0x42,14,VK_K,0x4b,37,37,XK_K,0x004b,KeyK,AC08,k,0x54,0x28 +KEY_K,37,ANSI_K,0x28,0x25,0x42,0x42,14,VK_K,0x4b,37,37,XK_k,0x006b,KeyK,AC08,k,0x54,0x28 +KEY_L,38,ANSI_L,0x25,0x26,0x4b,0x4b,15,VK_L,0x4c,38,38,XK_L,0x004c,KeyL,AC09,l,0x55,0x25 +KEY_L,38,ANSI_L,0x25,0x26,0x4b,0x4b,15,VK_L,0x4c,38,38,XK_l,0x006c,KeyL,AC09,l,0x55,0x25 +KEY_SEMICOLON,39,ANSI_Semicolon,0x29,0x27,0x4c,0x4c,51,VK_OEM_1,0xba,39,39,XK_semicolon,0x003b,Semicolon,AC10,semicolon,0x56,0x29 +KEY_SEMICOLON,39,ANSI_Semicolon,0x29,0x27,0x4c,0x4c,51,VK_OEM_1,0xba,39,39,XK_colon,0x003a,Semicolon,AC10,semicolon,0x56,0x29 +KEY_APOSTROPHE,40,ANSI_Quote,0x27,0x28,0x52,0x52,52,VK_OEM_7,0xde,40,40,XK_apostrophe,0x0027,Quote,AC11,apostrophe,0x57,0x27 +KEY_APOSTROPHE,40,ANSI_Quote,0x27,0x28,0x52,0x52,52,VK_OEM_7,0xde,40,40,XK_quotedbl,0x0022,Quote,AC11,apostrophe,0x57,0x27 +KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_grave,0x0060,Backquote,TLDE,grave_accent,0x2a,0x32 +KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_grave,0x0060,Backquote,AB00,grave_accent,0x2a,0x32 +KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_asciitilde,0x007e,Backquote,TLDE,grave_accent,0x2a,0x32 +KEY_GRAVE,41,ANSI_Grave,0x32,0x29,0x0e,0x0e,53,VK_OEM_3,0xc0,41,41,XK_asciitilde,0x007e,Backquote,AB00,grave_accent,0x2a,0x32 +KEY_SHIFT,42,Shift,0x38,0x2a,0x12,0x12,225,VK_SHIFT,0x10,42,42,XK_Shift_L,0xffe1,ShiftLeft,LFSH,shift,0x63,0x38 +KEY_LEFTSHIFT,42,Shift,0x38,0x2a,0x12,0x12,225,VK_LSHIFT,0xa0,42,42,XK_Shift_L,0xffe1,ShiftLeft,LFSH,shift,0x63,0x38 +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,BKSL,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,AC12,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,BKSL,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,49,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,AC12,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,BKSL,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_backslash,0x005c,Backslash,AC12,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,BKSL,backslash,0x58,0x2a +KEY_BACKSLASH,43,ANSI_Backslash,0x2a,0x2b,0x5d,0x5c,50,VK_OEM_5,0xdc,43,43,XK_bar,0x007c,Backslash,AC12,backslash,0x58,0x2a +KEY_Z,44,ANSI_Z,0x6,0x2c,0x1a,0x1a,29,VK_Z,0x5a,44,44,XK_Z,0x005a,KeyZ,AB01,z,0x64,0x6 +KEY_Z,44,ANSI_Z,0x6,0x2c,0x1a,0x1a,29,VK_Z,0x5a,44,44,XK_z,0x007a,KeyZ,AB01,z,0x64,0x6 +KEY_X,45,ANSI_X,0x7,0x2d,0x22,0x22,27,VK_X,0x58,45,45,XK_X,0x0058,KeyX,AB02,x,0x65,0x7 +KEY_X,45,ANSI_X,0x7,0x2d,0x22,0x22,27,VK_X,0x58,45,45,XK_x,0x0078,KeyX,AB02,x,0x65,0x7 +KEY_C,46,ANSI_C,0x8,0x2e,0x21,0x21,6,VK_C,0x43,46,46,XK_C,0x0043,KeyC,AB03,c,0x66,0x8 +KEY_C,46,ANSI_C,0x8,0x2e,0x21,0x21,6,VK_C,0x43,46,46,XK_c,0x0063,KeyC,AB03,c,0x66,0x8 +KEY_V,47,ANSI_V,0x9,0x2f,0x2a,0x2a,25,VK_V,0x56,47,47,XK_V,0x0056,KeyV,AB04,v,0x67,0x9 +KEY_V,47,ANSI_V,0x9,0x2f,0x2a,0x2a,25,VK_V,0x56,47,47,XK_v,0x0076,KeyV,AB04,v,0x67,0x9 +KEY_B,48,ANSI_B,0xb,0x30,0x32,0x32,5,VK_B,0x42,48,48,XK_B,0x0042,KeyB,AB05,b,0x68,0xb +KEY_B,48,ANSI_B,0xb,0x30,0x32,0x32,5,VK_B,0x42,48,48,XK_b,0x0062,KeyB,AB05,b,0x68,0xb +KEY_N,49,ANSI_N,0x2d,0x31,0x31,0x31,17,VK_N,0x4e,49,49,XK_N,0x004e,KeyN,AB06,n,0x69,0x2d +KEY_N,49,ANSI_N,0x2d,0x31,0x31,0x31,17,VK_N,0x4e,49,49,XK_n,0x006e,KeyN,AB06,n,0x69,0x2d +KEY_M,50,ANSI_M,0x2e,0x32,0x3a,0x3a,16,VK_M,0x4d,50,50,XK_M,0x004d,KeyM,AB07,m,0x6a,0x2e +KEY_M,50,ANSI_M,0x2e,0x32,0x3a,0x3a,16,VK_M,0x4d,50,50,XK_m,0x006d,KeyM,AB07,m,0x6a,0x2e +KEY_COMMA,51,ANSI_Comma,0x2b,0x33,0x41,0x41,54,VK_OEM_COMMA,0xbc,51,51,XK_comma,0x002c,Comma,AB08,comma,0x6b,0x2b +KEY_COMMA,51,ANSI_Comma,0x2b,0x33,0x41,0x41,54,VK_OEM_COMMA,0xbc,51,51,XK_less,0x003c,Comma,AB08,comma,0x6b,0x2b +KEY_DOT,52,ANSI_Period,0x2f,0x34,0x49,0x49,55,VK_OEM_PERIOD,0xbe,52,52,XK_period,0x002e,Period,AB09,dot,0x6c,0x2f +KEY_DOT,52,ANSI_Period,0x2f,0x34,0x49,0x49,55,VK_OEM_PERIOD,0xbe,52,52,XK_greater,0x003e,Period,AB09,dot,0x6c,0x2f +KEY_SLASH,53,ANSI_Slash,0x2c,0x35,0x4a,0x4a,56,VK_OEM_2,0xbf,53,53,XK_slash,0x002f,Slash,AB10,slash,0x6d,0x2c +KEY_SLASH,53,ANSI_Slash,0x2c,0x35,0x4a,0x4a,56,VK_OEM_2,0xbf,53,53,XK_question,0x003f,Slash,AB10,slash,0x6d,0x2c +KEY_RIGHTSHIFT,54,RightShift,0x3c,0x36,0x59,0x59,229,VK_RSHIFT,0xa1,54,54,XK_Shift_R,0xffe2,ShiftRight,RTSH,shift_r,0x6e,0x7b +KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,0x37,0x7c,0x7e,85,VK_MULTIPLY,0x6a,55,55,XK_multiply,0x00d7,NumpadMultiply,KPMU,asterisk,0x2f,0x43 +KEY_KPASTERISK,55,ANSI_KeypadMultiply,0x43,0x37,0x7c,0x7e,85,VK_MULTIPLY,0x6a,55,55,XK_multiply,0x00d7,NumpadMultiply,KPMU,kp_multiply,0x2f,0x43 +KEY_LEFTALT,56,Option,0x3a,0x38,0x11,0x19,226,VK_LMENU,0xa4,56,56,XK_Alt_L,0xffe9,AltLeft,LALT,alt,0x13,0x3a +KEY_LEFTALT,56,Option,0x3a,0x38,0x11,0x19,226,VK_MENU,0x12,56,56,XK_Alt_L,0xffe9,AltLeft,LALT,alt,0x13,0x3a +KEY_SPACE,57,Space,0x31,0x39,0x29,0x29,44,VK_SPACE,0x20,57,57,XK_space,0x0020,Space,SPCE,spc,0x79,0x31 +KEY_CAPSLOCK,58,CapsLock,0x39,0x3a,0x58,0x14,57,VK_CAPITAL,0x14,58,58,XK_Caps_Lock,0xffe5,CapsLock,CAPS,caps_lock,0x77,0x39 +KEY_F1,59,F1,0x7a,0x3b,0x05,0x07,58,VK_F1,0x70,59,59,XK_F1,0xffbe,F1,FK01,f1,0x05,0x7a +KEY_F2,60,F2,0x78,0x3c,0x06,0x0f,59,VK_F2,0x71,60,60,XK_F2,0xffbf,F2,FK02,f2,0x06,0x78 +KEY_F3,61,F3,0x63,0x3d,0x04,0x17,60,VK_F3,0x72,61,61,XK_F3,0xffc0,F3,FK03,f3,0x08,0x63 +KEY_F4,62,F4,0x76,0x3e,0x0c,0x1f,61,VK_F4,0x73,62,62,XK_F4,0xffc1,F4,FK04,f4,0x0a,0x76 +KEY_F5,63,F5,0x60,0x3f,0x03,0x27,62,VK_F5,0x74,63,63,XK_F5,0xffc2,F5,FK05,f5,0x0c,0x60 +KEY_F6,64,F6,0x61,0x40,0x0b,0x2f,63,VK_F6,0x75,64,64,XK_F6,0xffc3,F6,FK06,f6,0x0e,0x61 +KEY_F7,65,F7,0x62,0x41,0x83,0x37,64,VK_F7,0x76,65,65,XK_F7,0xffc4,F7,FK07,f7,0x10,0x62 +KEY_F8,66,F8,0x64,0x42,0x0a,0x3f,65,VK_F8,0x77,66,66,XK_F8,0xffc5,F8,FK08,f8,0x11,0x64 +KEY_F9,67,F9,0x65,0x43,0x01,0x47,66,VK_F9,0x78,67,67,XK_F9,0xffc6,F9,FK09,f9,0x12,0x65 +KEY_F10,68,F10,0x6d,0x44,0x09,0x4f,67,VK_F10,0x79,68,68,XK_F10,0xffc7,F10,FK10,f10,0x07,0x6d +KEY_NUMLOCK,69,ANSI_KeypadClear,0x47,0x45,0x77,0x76,83,VK_NUMLOCK,0x90,69,69,XK_Num_Lock,0xff7f,NumLock,NMLK,num_lock,0x62,0x47 +KEY_SCROLLLOCK,70,,,0x46,0x7e,0x5f,71,VK_SCROLL,0x91,70,70,XK_Scroll_Lock,0xff14,ScrollLock,SCLK,scroll_lock,0x17,0x6b +KEY_KP7,71,ANSI_Keypad7,0x59,0x47,0x6c,0x6c,95,VK_NUMPAD7,0x67,71,71,XK_KP_7,0xffb7,Numpad7,KP7,kp_7,0x44,0x59 +KEY_KP8,72,ANSI_Keypad8,0x5b,0x48,0x75,0x75,96,VK_NUMPAD8,0x68,72,72,XK_KP_8,0xffb8,Numpad8,KP8,kp_8,0x45,0x5b +KEY_KP9,73,ANSI_Keypad9,0x5c,0x49,0x7d,0x7d,97,VK_NUMPAD9,0x69,73,73,XK_KP_9,0xffb9,Numpad9,KP9,kp_9,0x46,0x5c +KEY_KPMINUS,74,ANSI_KeypadMinus,0x4e,0x4a,0x7b,0x4e,86,VK_SUBTRACT,0x6d,74,74,XK_KP_Subtract,0xffad,NumpadSubtract,KPSU,kp_subtract,0x47,0x4e +KEY_KP4,75,ANSI_Keypad4,0x56,0x4b,0x6b,0x6b,92,VK_NUMPAD4,0x64,75,75,XK_KP_4,0xffb4,Numpad4,KP4,kp_4,0x5b,0x56 +KEY_KP5,76,ANSI_Keypad5,0x57,0x4c,0x73,0x73,93,VK_NUMPAD5,0x65,76,76,XK_KP_5,0xffb5,Numpad5,KP5,kp_5,0x5c,0x57 +KEY_KP6,77,ANSI_Keypad6,0x58,0x4d,0x74,0x74,94,VK_NUMPAD6,0x66,77,77,XK_KP_6,0xffb6,Numpad6,KP6,kp_6,0x5d,0x58 +KEY_KPPLUS,78,ANSI_KeypadPlus,0x45,0x4e,0x79,0x7c,87,VK_ADD,0x6b,78,78,XK_KP_Add,0xffab,NumpadAdd,KPAD,kp_add,0x7d,0x45 +KEY_KP1,79,ANSI_Keypad1,0x53,0x4f,0x69,0x69,89,VK_NUMPAD1,0x61,79,79,XK_KP_1,0xffb1,Numpad1,KP1,kp_1,0x70,0x53 +KEY_KP2,80,ANSI_Keypad2,0x54,0x50,0x72,0x72,90,VK_NUMPAD2,0x62,80,80,XK_KP_2,0xffb2,Numpad2,KP2,kp_2,0x71,0x54 +KEY_KP3,81,ANSI_Keypad3,0x55,0x51,0x7a,0x7a,91,VK_NUMPAD3,0x63,81,81,XK_KP_3,0xffb3,Numpad3,KP3,kp_3,0x72,0x55 +KEY_KP0,82,ANSI_Keypad0,0x52,0x52,0x70,0x70,98,VK_NUMPAD0,0x60,82,82,XK_KP_0,0xffb0,Numpad0,KP0,kp_0,0x5e,0x52 +KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,0x53,0x71,0x71,99,VK_DECIMAL,0x6e,83,83,XK_KP_Decimal,0xffae,NumpadDecimal,KPDL,kp_decimal,0x32,0x41 +KEY_KPDOT,83,ANSI_KeypadDecimal,0x41,0x53,0x71,0x71,99,VK_DECIMAL,0x6e,83,83,XK_KP_Decimal,0xffae,NumpadDecimal,KPDC,kp_decimal,0x32,0x41 +,84,,,0x54,,,,,,,,,,,,,, +KEY_ZENKAKUHANKAKU,85,,,0x76,0x5f,,148,,,,,,,Lang5,HZTG,,, +KEY_102ND,86,,,0x56,0x61,0x13,100,VK_OEM_102,0xe1,86,86,,,IntlBackslash,LSGT,less,0x7c, +KEY_F11,87,F11,0x67,0x57,0x78,0x56,68,VK_F11,0x7a,87,87,XK_F11,0xffc8,F11,FK11,f11,0x09,0x67 +KEY_F12,88,F12,0x6f,0x58,0x07,0x5e,69,VK_F12,0x7b,88,88,XK_F12,0xffc9,F12,FK12,f12,0x0b,0x6f +KEY_RO,89,,,0x73,0x51,,135,,,,,,,IntlRo,AB11,ro,, +KEY_KATAKANA,90,JIS_Kana,0x68,0x78,0x63,,146,VK_KANA,0x15,,,,,Katakana,KATA,,, +KEY_KATAKANA,90,JIS_Kana,0x68,0x78,0x63,,146,VK_KANA,0x15,,,,,Lang3,KATA,,, +KEY_HIRAGANA,91,,,0x77,0x62,0x87,147,,,,,,,Hiragana,HIRA,hiragana,, +KEY_HIRAGANA,91,,,0x77,0x62,0x87,147,,,,,,,Lang4,HIRA,hiragana,, +KEY_HENKAN,92,,,0x79,0x64,0x86,138,,,,,,,Convert,HENK,henkan,, +KEY_KATAKANAHIRAGANA,93,,,0x70,0x13,0x87,136,,,0xc8,0xc8,,,KanaMode,HKTG,,, +KEY_MUHENKAN,94,,,0x7b,0x67,0x85,139,,,,,,,NonConvert,NFER,,, +KEY_MUHENKAN,94,,,0x7b,0x67,0x85,139,,,,,,,NonConvert,MUHE,,, +KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,0x5c,0x27,,140,,,,,XK_KP_Separator,0xffac,,KPSP,,, +KEY_KPJPCOMMA,95,JIS_KeypadComma,0x5f,0x5c,0x27,,140,,,,,XK_KP_Separator,0xffac,,JPCM,,, +KEY_KPENTER,96,ANSI_KeypadEnter,0x4c,0xe01c,0xe05a,0x79,88,,,0x64,0x64,XK_KP_Enter,0xff8d,NumpadEnter,KPEN,kp_enter,0x5a,0x4c +KEY_RIGHTCTRL,97,RightControl,0x3e,0xe01d,0xe014,0x58,228,VK_RCONTROL,0xa3,0x65,0x65,XK_Control_R,0xffe4,ControlRight,RCTL,ctrl_r,0x4c,0x7d +KEY_KPSLASH,98,ANSI_KeypadDivide,0x4b,0xe035,0xe04a,0x4a,84,VK_DIVIDE,0x6f,0x68,0x68,XK_KP_Divide,0xffaf,NumpadDivide,KPDV,kp_divide,0x2e,0x4b +KEY_SYSRQ,99,,,0x54,0x7f,0x57,70,VK_SNAPSHOT,0x2c,0x67,0x67,XK_Sys_Req,0xff15,PrintScreen,PRSC,print,0x16,0x69 +KEY_SYSRQ,99,,,0x54,0x7f,0x57,70,VK_SNAPSHOT,0x2c,0x67,0x67,XK_Sys_Req,0xff15,PrintScreen,SYRQ,sysrq,0x16,0x69 +KEY_RIGHTALT,100,RightOption,0x3d,0xe038,0xe011,0x39,230,VK_RMENU,0xa5,0x69,0x69,XK_Alt_R,0xffea,AltRight,ALGR,alt_r,0x0d,0x7c +KEY_RIGHTALT,100,RightOption,0x3d,0xe038,0xe011,0x39,230,VK_RMENU,0xa5,0x69,0x69,XK_Alt_R,0xffea,AltRight,RALT,alt_r,0x0d,0x7c +KEY_LINEFEED,101,,,0x5b,,,,,,,,,,,LNFD,lf,0x6f, +KEY_HOME,102,Home,0x73,0xe047,0xe06c,0x6e,74,VK_HOME,0x24,0x59,0x59,XK_Home,0xff50,Home,HOME,home,0x34,0x73 +KEY_UP,103,UpArrow,0x7e,0xe048,0xe075,0x63,82,VK_UP,0x26,0x5a,0x5a,XK_Up,0xff52,ArrowUp,UP,up,0x14,0x3e +KEY_PAGEUP,104,PageUp,0x74,0xe049,0xe07d,0x6f,75,VK_PRIOR,0x21,0x5b,0x5b,XK_Page_Up,0xff55,PageUp,PGUP,pgup,0x60,0x74 +KEY_LEFT,105,LeftArrow,0x7b,0xe04b,0xe06b,0x61,80,VK_LEFT,0x25,0x5c,0x5c,XK_Left,0xff51,ArrowLeft,LEFT,left,0x18,0x3b +KEY_RIGHT,106,RightArrow,0x7c,0xe04d,0xe074,0x6a,79,VK_RIGHT,0x27,0x5e,0x5e,XK_Right,0xff53,ArrowRight,RGHT,right,0x1c,0x3c +KEY_END,107,End,0x77,0xe04f,0xe069,0x65,77,VK_END,0x23,0x5f,0x5f,XK_End,0xff57,End,END,end,0x4a,0x77 +KEY_DOWN,108,DownArrow,0x7d,0xe050,0xe072,0x60,81,VK_DOWN,0x28,0x60,0x60,XK_Down,0xff54,ArrowDown,DOWN,down,0x1b,0x3d +KEY_PAGEDOWN,109,PageDown,0x79,0xe051,0xe07a,0x6d,78,VK_NEXT,0x22,0x61,0x61,XK_Page_Down,0xff56,PageDown,PGDN,pgdn,0x7b,0x79 +KEY_INSERT,110,,,0xe052,0xe070,0x67,73,VK_INSERT,0x2d,0x62,0x62,XK_Insert,0xff63,Insert,INS,insert,0x2c,0x72 +KEY_DELETE,111,ForwardDelete,0x75,0xe053,0xe071,0x64,76,VK_DELETE,0x2e,0x63,0x63,XK_Delete,0xffff,Delete,DEL,delete,0x42,0x75 +KEY_DELETE,111,ForwardDelete,0x75,0xe053,0xe071,0x64,76,VK_DELETE,0x2e,0x63,0x63,XK_Delete,0xffff,Delete,DELE,,0x42,0x75 +KEY_MACRO,112,,,0xe06f,0xe06f,0x8e,,,,,,,,,I120,,, +KEY_MUTE,113,Mute,0x4a,0xe020,0xe023,0x9c,127,VK_VOLUME_MUTE,0xad,,,,,AudioVolumeMute,MUTE,audiomute,, +KEY_MUTE,113,Mute,0x4a,0xe020,0xe023,0x9c,239,VK_VOLUME_MUTE,0xad,,,,,AudioVolumeMute,MUTE,audiomute,, +KEY_VOLUMEDOWN,114,VolumeDown,0x49,0xe02e,0xe021,0x9d,129,VK_VOLUME_DOWN,0xae,,,,,AudioVolumeDown,VOL-,volumedown,, +KEY_VOLUMEDOWN,114,VolumeDown,0x49,0xe02e,0xe021,0x9d,238,VK_VOLUME_DOWN,0xae,,,,,AudioVolumeDown,VOL-,volumedown,, +KEY_VOLUMEUP,115,VolumeUp,0x48,0xe030,0xe032,0x95,128,VK_VOLUME_UP,0xaf,,,,,AudioVolumeUp,VOL+,volumeup,, +KEY_VOLUMEUP,115,VolumeUp,0x48,0xe030,0xe032,0x95,237,VK_VOLUME_UP,0xaf,,,,,AudioVolumeUp,VOL+,volumeup,, +KEY_POWER,116,,,0xe05e,0xe037,,102,,,,,,,Power,POWR,power,,0x7f7f +KEY_KPEQUAL,117,ANSI_KeypadEquals,0x51,0x59,0x0f,,103,,,0x76,0x76,XK_KP_Equal,0xffbd,NumpadEqual,KPEQ,kp_equals,0x2d,0x51 +KEY_KPPLUSMINUS,118,,,0xe04e,0xe079,,,,,,,,,,I126,,, +KEY_PAUSE,119,,,0xe046,0xe077,0x62,72,VK_PAUSE,0x013,0x66,0x66,XK_Pause,0xff13,Pause,PAUS,pause,0x15,0x71 +KEY_SCALE,120,,,0xe00b,,,,,,,,,,,I128,,, +KEY_KPCOMMA,121,,,0x7e,0x6d,,133,VK_SEPARATOR??,0x6c,,,,,NumpadComma,KPCO,kp_comma,, +KEY_KPCOMMA,121,,,0x7e,0x6d,,133,VK_SEPARATOR??,0x6c,,,,,NumpadComma,I129,,, +KEY_HANGEUL,122,,,,,,144,VK_HANGEUL,0x15,,,,,,HNGL,,, +KEY_HANJA,123,,,0xe00d,,,145,VK_HANJA,0x19,,,,,,HJCV,,, +KEY_YEN,124,JIS_Yen,0x5d,0x7d,0x6a,0x5d,137,,,0x7d,0x7d,,,IntlYen,AE13,yen,, +KEY_LEFTMETA,125,Command,0x37,0xe05b,0xe01f,0x8b,227,VK_LWIN,0x5b,0x6b,0x6b,XK_Meta_L,0xffe7,MetaLeft,LMTA,meta_l,0x78,0x37 +KEY_LEFTMETA,125,Command,0x37,0xe05b,0xe01f,0x8b,227,VK_LWIN,0x5b,0x6b,0x6b,XK_Meta_L,0xffe7,MetaLeft,LWIN,meta_l,0x78,0x37 +KEY_RIGHTMETA,126,RightCommand,0x36,0xe05c,0xe027,0x8c,231,VK_RWIN,0x5c,0x6c,0x6c,XK_Meta_R,0xffe8,MetaRight,RMTA,meta_r,0x7a,0x37 +KEY_RIGHTMETA,126,RightCommand,0x36,0xe05c,0xe027,0x8c,231,VK_RWIN,0x5c,0x6c,0x6c,XK_Meta_R,0xffe8,MetaRight,RWIN,meta_r,0x7a,0x37 +KEY_COMPOSE,127,,0x6e,0xe05d,0xe02f,0x8d,101,VK_APPS,0x5d,0x6d,0x6d,,,ContextMenu,MENU,compose,0x43, +KEY_COMPOSE,127,,0x6e,0xe05d,0xe02f,0x8d,101,VK_APPS,0x5d,0x6d,0x6d,,,ContextMenu,COMP,compose,0x43, +KEY_STOP,128,,,0xe068,0xe028,0x0a,120,VK_BROWSER_STOP,0xa9,,,,,BrowserStop,STOP,stop,0x01, +KEY_STOP,128,,,0xe068,0xe028,0x0a,243,VK_BROWSER_STOP,0xa9,,,,,BrowserStop,STOP,stop,0x01, +KEY_AGAIN,129,,,0xe005,,0x0b,121,,,,,,,Again,AGAI,again,0x03, +KEY_PROPS,130,,,0xe006,,0x0c,,,,,,,,Props,PROP,props,0x19, +KEY_UNDO,131,,,0xe007,,0x10,122,,,,,,,Undo,UNDO,undo,0x1a, +KEY_FRONT,132,,,0xe00c,,,119,,,,,,,,FRNT,front,0x31, +KEY_COPY,133,,,0xe078,,0x18,124,,,,,,,Copy,COPY,copy,0x33, +KEY_OPEN,134,,,0x64,,0x20,116,,,,,,,Open,OPEN,open,0x48, +KEY_PASTE,135,,,0x65,,0x28,125,,,,,,,Paste,PAST,paste,0x49, +KEY_FIND,136,,,0xe041,,0x30,126,,,,,,,Find,FIND,find,0x5f, +KEY_FIND,136,,,0xe041,,0x30,244,,,,,,,Find,FIND,find,0x5f, +KEY_CUT,137,,,0xe03c,,0x38,123,,,,,,,Cut,CUT,cut,0x61, +KEY_HELP,138,Help,0x72,0xe075,,0x09,117,VK_HELP,0x2f,,,XK_Help,0xff6a,Help,HELP,help,0x76, +KEY_MENU,139,,,0xe01e,,0x91,118,,,,,,,,I147,menu,, +KEY_CALC,140,,,0xe021,0xe02b,0xa3,251,,,,,,,LaunchApp2,I148,calculator,, +KEY_SETUP,141,,,0x66,,,,,,,,,,,I149,,, +KEY_SLEEP,142,,,0xe05f,0xe03f,,248,VK_SLEEP,0x5f,,,,,Sleep,I150,sleep,, +KEY_WAKEUP,143,,,0xe063,0xe05e,,,,,,,,,WakeUp,I151,wake,, +KEY_FILE,144,,,0x67,,,,,,,,,,,I152,,, +KEY_SENDFILE,145,,,0x68,,,,,,,,,,,I153,,, +KEY_DELETEFILE,146,,,0x69,,,,,,,,,,,I154,,, +KEY_XFER,147,,,0xe013,,0xa2,,,,,,,,,XFER,,, +KEY_XFER,147,,,0xe013,,0xa2,,,,,,,,,I155,,, +KEY_PROG1,148,,,0xe01f,,0xa0,,,,,,,,,I156,,, +KEY_PROG2,149,,,0xe017,,0xa1,,,,,,,,,I157,,, +KEY_WWW,150,,,0xe002,,,240,,,,,,,,I158,,, +KEY_MSDOS,151,,,0x6a,,,,,,,,,,,I159,,, +KEY_SCREENLOCK,152,,,0xe012,,0x96,249,,,,,,,,I160,,, +KEY_DIRECTION,153,,,0x6b,,,,,,,,,,,I161,,, +KEY_CYCLEWINDOWS,154,,,0xe026,,0x9b,,,,,,,,,I162,,, +KEY_MAIL,155,,,0xe06c,0xe048,,,,,,,,,LaunchMail,I163,mail,, +KEY_BOOKMARKS,156,,,0xe066,0xe018,,,,,,,,,BrowserFavorites,I164,ac_bookmarks,, +KEY_COMPUTER,157,,,0xe06b,0xe040,,,,,,,,,LaunchApp1,I165,computer,, +KEY_BACK,158,,,0xe06a,0xe038,,241,VK_BROWSER_BACK,0xa6,,,,,BrowserBack,I166,ac_back,, +KEY_FORWARD,159,,,0xe069,0xe030,,242,VK_BROWSER_FORWARD,0xa7,,,,,BrowserForward,I167,ac_forward,, +KEY_CLOSECD,160,,,0xe023,,0x9a,,,,,,,,,I168,,, +KEY_EJECTCD,161,,,0x6c,,,236,,,,,,,,I169,,, +KEY_EJECTCLOSECD,162,,,0xe07d,,,,,,,,,,Eject,I170,,, +KEY_NEXTSONG,163,,,0xe019,0xe04d,0x93,235,VK_MEDIA_NEXT_TRACK,0xb0,,,,,MediaTrackNext,I171,audionext,, +KEY_PLAYPAUSE,164,,,0xe022,0xe034,,232,VK_MEDIA_PLAY_PAUSE,0xb3,,,,,MediaPlayPause,I172,audioplay,, +KEY_PREVIOUSSONG,165,,,0xe010,0xe015,0x94,234,VK_MEDIA_PREV_TRACK,0xb1,,,,,MediaTrackPrevious,I173,audioprev,, +KEY_STOPCD,166,,,0xe024,0xe03b,0x98,233,VK_MEDIA_STOP,0xb2,,,,,MediaStop,I174,audiostop,, +KEY_RECORD,167,,,0xe031,,0x9e,,,,,,,,,I175,,, +KEY_REWIND,168,,,0xe018,,0x9f,,,,,,,,,I176,,, +KEY_PHONE,169,,,0x63,,,,,,,,,,,I177,,, +KEY_ISO,170,ISO_Section,0xa,0x70,,,,,,,,,,,I178,,, +KEY_CONFIG,171,,,0xe001,,,,,,,,,,,I179,,, +KEY_HOMEPAGE,172,,,0xe032,0xe03a,0x97,,VK_BROWSER_HOME,0xac,,,,,BrowserHome,I180,ac_home,, +KEY_REFRESH,173,,,0xe067,0xe020,,250,VK_BROWSER_REFRESH,0xa8,,,,,BrowserRefresh,I181,ac_refresh,, +KEY_EXIT,174,,,0x71,,,,,,,,,,,I182,,, +KEY_MOVE,175,,,0x72,,,,,,,,,,,I183,,, +KEY_EDIT,176,,,0xe008,,,247,,,,,,,,I184,,, +KEY_SCROLLUP,177,,,0x75,,,245,,,,,,,,I185,,, +KEY_SCROLLDOWN,178,,,0xe00f,,,246,,,,,,,,I186,,, +KEY_KPLEFTPAREN,179,,,0xe076,,,182,,,,,,,NumpadParenLeft,I187,,, +KEY_KPRIGHTPAREN,180,,,0xe07b,,,183,,,,,,,NumpadParenRight,I188,,, +KEY_NEW,181,,,0xe009,,,,,,,,,,,I189,,, +KEY_REDO,182,,,0xe00a,,,,,,,,,,,I190,,, +KEY_F13,183,F13,0x69,0x5d,0x2f,0x7f,104,VK_F13,0x7c,0x6e,0x6e,,,F13,FK13,,,0x69 +KEY_F14,184,F14,0x6b,0x5e,0x37,0x80,105,VK_F14,0x7d,0x6f,0x6f,,,F14,FK14,,,0x6b +KEY_F15,185,F15,0x71,0x5f,0x3f,0x81,106,VK_F15,0x7e,0x70,0x70,,,F15,FK15,,,0x71 +KEY_F16,186,F16,0x6a,0x55,,0x82,107,VK_F16,0x7f,0x71,0x71,,,F16,FK16,,, +KEY_F17,187,F17,0x40,0xe003,,0x83,108,VK_F17,0x80,0x72,0x72,,,F17,FK17,,, +KEY_F18,188,F18,0x4f,0xe077,,,109,VK_F18,0x81,,,,,F18,FK18,,, +KEY_F19,189,F19,0x50,0xe004,,,110,VK_F19,0x82,,,,,F19,FK19,,, +KEY_F20,190,F20,0x5a,0x5a,,,111,VK_F20,0x83,,,,,F20,FK20,,, +KEY_F21,191,,,0x74,,,112,VK_F21,0x84,,,,,F21,FK21,,, +KEY_F22,192,,,0xe079,,,113,VK_F22,0x85,,,,,F22,FK22,,, +KEY_F23,193,,,0x6d,,,114,VK_F23,0x86,,,,,F23,FK23,,, +KEY_F24,194,,,0x6f,,,115,VK_F24,0x87,,,,,F24,FK24,,, +,195,,,0xe015,,,,,,,,,,,,,, +,196,,,0xe016,,,,,,,,,,,,,, +,197,,,0xe01a,,,,,,,,,,,,,, +,198,,,0xe01b,,,,,,,,,,,,,, +,199,,,0xe027,,,,,,,,,,,,,, +KEY_PLAYCD,200,,,0xe028,,,,,,,,,,,I208,,, +KEY_PAUSECD,201,,,0xe029,,,,,,,,,,,I209,,, +KEY_PROG3,202,,,0xe02b,,,,,,,,,,,I210,,, +KEY_PROG4,203,,,0xe02c,,,,,,,,,,,I211,,, +KEY_DASHBOARD,204,,,0xe02d,,,,,,,,,,,I212,,, +KEY_SUSPEND,205,,,0xe025,,,,,,,,,,Suspend,I213,,, +KEY_CLOSE,206,,,0xe02f,,,,,,,,,,,I214,,, +KEY_PLAY,207,,,0xe033,,,,VK_PLAY,0xfa,,,,,,I215,,, +KEY_FASTFORWARD,208,,,0xe034,,,,,,,,,,,I216,,, +KEY_BASSBOOST,209,,,0xe036,,,,,,,,,,,I217,,, +KEY_PRINT,210,,,0xe039,,,,VK_PRINT,0x2a,,,,,,I218,,, +KEY_HP,211,,,0xe03a,,,,,,,,,,,I219,,, +KEY_CAMERA,212,,,0xe03b,,,,,,,,,,,I220,,, +KEY_SOUND,213,,,0xe03d,,,,,,,,,,,I221,,, +KEY_QUESTION,214,,,0xe03e,,,,,,,,,,,I222,,, +KEY_EMAIL,215,,,0xe03f,,,,VK_LAUNCH_MAIL,0xb4,,,,,,I223,,, +KEY_CHAT,216,,,0xe040,,,,,,,,,,,I224,,, +KEY_SEARCH,217,,,0xe065,0xe010,,,VK_BROWSER_SEARCH,0xaa,,,,,BrowserSearch,I225,,, +KEY_CONNECT,218,,,0xe042,,,,,,,,,,,I226,,, +KEY_FINANCE,219,,,0xe043,,,,,,,,,,,I227,,, +KEY_SPORT,220,,,0xe044,,,,,,,,,,,I228,,, +KEY_SHOP,221,,,0xe045,,,,,,,,,,,I229,,, +KEY_ALTERASE,222,,,0xe014,,,,,,,,,,,I230,,, +KEY_CANCEL,223,,,0xe04a,,,,,,,,,,,I231,,, +KEY_BRIGHTNESSDOWN,224,,,0xe04c,,,,,,,,,,,I232,,, +KEY_BRIGHTNESSUP,225,,,0xe054,,,,,,,,,,,I233,,, +KEY_MEDIA,226,,,0xe06d,0xe050,,,,,,,,,MediaSelect,I234,mediaselect,, +KEY_SWITCHVIDEOMODE,227,,,0xe056,,,,,,,,,,,I235,,, +KEY_KBDILLUMTOGGLE,228,,,0xe057,,,,,,,,,,,I236,,, +KEY_KBDILLUMDOWN,229,,,0xe058,,,,,,,,,,,I237,,, +KEY_KBDILLUMUP,230,,,0xe059,,,,,,,,,,,I238,,, +KEY_SEND,231,,,0xe05a,,,,,,,,,,,I239,,, +KEY_REPLY,232,,,0xe064,,,,,,,,,,,I240,,, +KEY_FORWARDMAIL,233,,,0xe00e,,,,,,,,,,,I241,,, +KEY_SAVE,234,,,0xe055,,,,,,,,,,,I242,,, +KEY_DOCUMENTS,235,,,0xe070,,,,,,,,,,,I243,,, +KEY_BATTERY,236,,,0xe071,,,,,,,,,,,I244,,, +KEY_BLUETOOTH,237,,,0xe072,,,,,,,,,,,I245,,, +KEY_WLAN,238,,,0xe073,,,,,,,,,,,I246,,, +KEY_UWB,239,,,0xe074,,,,,,,,,,,I247,,, +KEY_UNKNOWN,240,,,,,,,,,,,,,,I248,,, +KEY_VIDEO_NEXT,241,,,,,,,,,,,,,,I249,,, +KEY_VIDEO_PREV,242,,,,,,,,,,,,,,I250,,, +KEY_BRIGHTNESS_CYCLE,243,,,,,,,,,,,,,,I251,,, +KEY_BRIGHTNESS_ZERO,244,,,,,,,,,,,,,,I252,,, +KEY_DISPLAY_OFF,245,,,,,,,,,,,,,,I253,,, +KEY_WIMAX,246,,,,,,,,,,,,,,,,, +,247,,,,,,,,,,,,,,,,, +,248,,,,,,,,,,,,,,,,, +,249,,,,,,,,,,,,,,,,, +,250,,,,,,,,,,,,,,,,, +,251,,,,,,,,,,,,,,,,, +,252,,,,,,,,,,,,,,,,, +,253,,,,,,,,,,,,,,,,, +,254,,,,,,,,,,,,,,,,, +,255,,,,0xe012,,,,,,,,,,,,, +BTN_MISC,0x100,,,,,,,,,,,,,,,,, +BTN_0,0x100,,,,,,,VK_LBUTTON,0x01,,,,,,,,, +BTN_1,0x101,,,,,,,VK_RBUTTON,0x02,,,,,,,,, +BTN_2,0x102,,,,,,,VK_MBUTTON,0x04,,,,,,,,, +BTN_3,0x103,,,,,,,VK_XBUTTON1,0x05,,,,,,,,, +BTN_4,0x104,,,,,,,VK_XBUTTON2,0x06,,,,,,,,, +BTN_5,0x105,,,,,,,,,,,,,,,,, +BTN_6,0x106,,,,,,,,,,,,,,,,, +BTN_7,0x107,,,,,,,,,,,,,,,,, +BTN_8,0x108,,,,,,,,,,,,,,,,, +BTN_9,0x109,,,,,,,,,,,,,,,,, +BTN_MOUSE,0x110,,,,,,,,,,,,,,,,, +BTN_LEFT,0x110,,,,,,,,,,,,,,,,, +BTN_RIGHT,0x111,,,,,,,,,,,,,,,,, +BTN_MIDDLE,0x112,,,,,,,,,,,,,,,,, +BTN_SIDE,0x113,,,,,,,,,,,,,,,,, +BTN_EXTRA,0x114,,,,,,,,,,,,,,,,, +BTN_FORWARD,0x115,,,,,,,,,,,,,,,,, +BTN_BACK,0x116,,,,,,,,,,,,,,,,, +BTN_TASK,0x117,,,,,,,,,,,,,,,,, +BTN_JOYSTICK,0x120,,,,,,,,,,,,,,,,, +BTN_TRIGGER,0x120,,,,,,,,,,,,,,,,, +BTN_THUMB,0x121,,,,,,,,,,,,,,,,, +BTN_THUMB2,0x122,,,,,,,,,,,,,,,,, +BTN_TOP,0x123,,,,,,,,,,,,,,,,, +BTN_TOP2,0x124,,,,,,,,,,,,,,,,, +BTN_PINKIE,0x125,,,,,,,,,,,,,,,,, +BTN_BASE,0x126,,,,,,,,,,,,,,,,, +BTN_BASE2,0x127,,,,,,,,,,,,,,,,, +BTN_BASE3,0x128,,,,,,,,,,,,,,,,, +BTN_BASE4,0x129,,,,,,,,,,,,,,,,, +BTN_BASE5,0x12a,,,,,,,,,,,,,,,,, +BTN_BASE6,0x12b,,,,,,,,,,,,,,,,, +BTN_DEAD,0x12f,,,,,,,,,,,,,,,,, +BTN_GAMEPAD,0x130,,,,,,,,,,,,,,,,, +BTN_A,0x130,,,,,,,,,,,,,,,,, +BTN_B,0x131,,,,,,,,,,,,,,,,, +BTN_C,0x132,,,,,,,,,,,,,,,,, +BTN_X,0x133,,,,,,,,,,,,,,,,, +BTN_Y,0x134,,,,,,,,,,,,,,,,, +BTN_Z,0x135,,,,,,,,,,,,,,,,, +BTN_TL,0x136,,,,,,,,,,,,,,,,, +BTN_TR,0x137,,,,,,,,,,,,,,,,, +BTN_TL2,0x138,,,,,,,,,,,,,,,,, +BTN_TR2,0x139,,,,,,,,,,,,,,,,, +BTN_SELECT,0x13a,,,,,,,,,,,,,,,,, +BTN_START,0x13b,,,,,,,,,,,,,,,,, +BTN_MODE,0x13c,,,,,,,,,,,,,,,,, +BTN_THUMBL,0x13d,,,,,,,,,,,,,,,,, +BTN_THUMBR,0x13e,,,,,,,,,,,,,,,,, +BTN_DIGI,0x140,,,,,,,,,,,,,,,,, +BTN_TOOL_PEN,0x140,,,,,,,,,,,,,,,,, +BTN_TOOL_RUBBER,0x141,,,,,,,,,,,,,,,,, +BTN_TOOL_BRUSH,0x142,,,,,,,,,,,,,,,,, +BTN_TOOL_PENCIL,0x143,,,,,,,,,,,,,,,,, +BTN_TOOL_AIRBRUSH,0x144,,,,,,,,,,,,,,,,, +BTN_TOOL_FINGER,0x145,,,,,,,,,,,,,,,,, +BTN_TOOL_MOUSE,0x146,,,,,,,,,,,,,,,,, +BTN_TOOL_LENS,0x147,,,,,,,,,,,,,,,,, +BTN_TOUCH,0x14a,,,,,,,,,,,,,,,,, +BTN_STYLUS,0x14b,,,,,,,,,,,,,,,,, +BTN_STYLUS2,0x14c,,,,,,,,,,,,,,,,, +BTN_TOOL_DOUBLETAP,0x14d,,,,,,,,,,,,,,,,, +BTN_TOOL_TRIPLETAP,0x14e,,,,,,,,,,,,,,,,, +BTN_TOOL_QUADTAP,0x14f,,,,,,,,,,,,,,,,, +BTN_WHEEL,0x150,,,,,,,,,,,,,,,,, +BTN_GEAR_DOWN,0x150,,,,,,,,,,,,,,,,, +BTN_GEAR_UP,0x151,,,,,,,,,,,,,,,,, +KEY_OK,0x160,,,,,,,,,,,,,,,,, +KEY_SELECT,0x161,,,,,,,VK_SELECT,0x29,,,XK_Select,0xff60,Select,SELE,,, +KEY_GOTO,0x162,,,,,,,,,,,,,,,,, +KEY_CLEAR,0x163,,,,,,,,,,,,,NumpadClear,CLR,,, +KEY_POWER2,0x164,,,,,,,,,,,,,,,,, +KEY_OPTION,0x165,,,,,,,,,,,,,,,,, +KEY_INFO,0x166,,,,,,,,,,,,,,,,, +KEY_TIME,0x167,,,,,,,,,,,,,,,,, +KEY_VENDOR,0x168,,,,,,,,,,,,,,,,, +KEY_ARCHIVE,0x169,,,,,,,,,,,,,,,,, +KEY_PROGRAM,0x16a,,,,,,,,,,,,,,,,, +KEY_CHANNEL,0x16b,,,,,,,,,,,,,,,,, +KEY_FAVORITES,0x16c,,,,,,,VK_BROWSER_FAVOURITES,0xab,,,,,,,,, +KEY_EPG,0x16d,,,,,,,,,,,,,,,,, +KEY_PVR,0x16e,,,,,,,,,,,,,,,,, +KEY_MHP,0x16f,,,,,,,,,,,,,,,,, +KEY_LANGUAGE,0x170,,,,,,,,,,,,,,,,, +KEY_TITLE,0x171,,,,,,,,,,,,,,,,, +KEY_SUBTITLE,0x172,,,,,,,,,,,,,,,,, +KEY_ANGLE,0x173,,,,,,,,,,,,,,,,, +KEY_ZOOM,0x174,,,,,,,VK_ZOOM,0xfb,,,,,,,,, +KEY_MODE,0x175,,,,,,,,,,,,,,,,, +KEY_KEYBOARD,0x176,,,,,,,,,,,,,,,,, +KEY_SCREEN,0x177,,,,,,,,,,,,,,,,, +KEY_PC,0x178,,,,,,,,,,,,,,,,, +KEY_TV,0x179,,,,,,,,,,,,,,,,, +KEY_TV2,0x17a,,,,,,,,,,,,,,,,, +KEY_VCR,0x17b,,,,,,,,,,,,,,,,, +KEY_VCR2,0x17c,,,,,,,,,,,,,,,,, +KEY_SAT,0x17d,,,,,,,,,,,,,,,,, +KEY_SAT2,0x17e,,,,,,,,,,,,,,,,, +KEY_CD,0x17f,,,,,,,,,,,,,,,,, +KEY_TAPE,0x180,,,,,,,,,,,,,,,,, +KEY_RADIO,0x181,,,,,,,,,,,,,,,,, +KEY_TUNER,0x182,,,,,,,,,,,,,,,,, +KEY_PLAYER,0x183,,,,,,,,,,,,,,,,, +KEY_TEXT,0x184,,,,,,,,,,,,,,,,, +KEY_DVD,0x185,,,,,,,,,,,,,,,,, +KEY_AUX,0x186,,,,,,,,,,,,,,,,, +KEY_MP3,0x187,,,,,,,,,,,,,,,,, +KEY_AUDIO,0x188,,,,,,,,,,,,,,,,, +KEY_VIDEO,0x189,,,,,,,,,,,,,,,,, +KEY_DIRECTORY,0x18a,,,,,,,,,,,,,,,,, +KEY_LIST,0x18b,,,,,,,,,,,,,,,,, +KEY_MEMO,0x18c,,,,,,,,,,,,,,,,, +KEY_CALENDAR,0x18d,,,,,,,,,,,,,,,,, +KEY_RED,0x18e,,,,,,,,,,,,,,,,, +KEY_GREEN,0x18f,,,,,,,,,,,,,,,,, +KEY_YELLOW,0x190,,,,,,,,,,,,,,,,, +KEY_BLUE,0x191,,,,,,,,,,,,,,,,, +KEY_CHANNELUP,0x192,,,,,,,,,,,,,,,,, +KEY_CHANNELDOWN,0x193,,,,,,,,,,,,,,,,, +KEY_FIRST,0x194,,,,,,,,,,,,,,,,, +KEY_LAST,0x195,,,,,,,,,,,,,,,,, +KEY_AB,0x196,,,,,,,,,,,,,,,,, +KEY_NEXT,0x197,,,,,,,,,,,,,,,,, +KEY_RESTART,0x198,,,,,,,,,,,,,,,,, +KEY_SLOW,0x199,,,,,,,,,,,,,,,,, +KEY_SHUFFLE,0x19a,,,,,,,,,,,,,,,,, +KEY_BREAK,0x19b,,,,,,,,,,,,,,BREA,,, +KEY_BREAK,0x19b,,,,,,,,,,,,,,BRK,,, +KEY_PREVIOUS,0x19c,,,,,,,,,,,,,,,,, +KEY_DIGITS,0x19d,,,,,,,,,,,,,,,,, +KEY_TEEN,0x19e,,,,,,,,,,,,,,,,, +KEY_TWEN,0x19f,,,,,,,,,,,,,,,,, +KEY_VIDEOPHONE,0x1a0,,,,,,,,,,,,,,,,, +KEY_GAMES,0x1a1,,,,,,,,,,,,,,,,, +KEY_ZOOMIN,0x1a2,,,,,,,,,,,,,,,,, +KEY_ZOOMOUT,0x1a3,,,,,,,,,,,,,,,,, +KEY_ZOOMRESET,0x1a4,,,,,,,,,,,,,,,,, +KEY_WORDPROCESSOR,0x1a5,,,,,,,,,,,,,,,,, +KEY_EDITOR,0x1a6,,,,,,,,,,,,,,,,, +KEY_SPREADSHEET,0x1a7,,,,,,,,,,,,,,,,, +KEY_GRAPHICSEDITOR,0x1a8,,,,,,,,,,,,,,,,, +KEY_PRESENTATION,0x1a9,,,,,,,,,,,,,,,,, +KEY_DATABASE,0x1aa,,,,,,,,,,,,,,,,, +KEY_NEWS,0x1ab,,,,,,,,,,,,,,,,, +KEY_VOICEMAIL,0x1ac,,,,,,,,,,,,,,,,, +KEY_ADDRESSBOOK,0x1ad,,,,,,,,,,,,,,,,, +KEY_MESSENGER,0x1ae,,,,,,,,,,,,,,,,, +KEY_DISPLAYTOGGLE,0x1af,,,,,,,,,,,,,,,,, +KEY_SPELLCHECK,0x1b0,,,,,,,,,,,,,,,,, +KEY_LOGOFF,0x1b1,,,,,,,,,,,,,,,,, +KEY_DOLLAR,0x1b2,,,,,,,,,,,,,,,,, +KEY_EURO,0x1b3,,,,,,,,,,,,,,,,, +KEY_FRAMEBACK,0x1b4,,,,,,,,,,,,,,,,, +KEY_FRAMEFORWARD,0x1b5,,,,,,,,,,,,,,,,, +KEY_CONTEXT_MENU,0x1b6,,,,,,,,,,,,,,,,, +KEY_MEDIA_REPEAT,0x1b7,,,,,,,,,,,,,,,,, +KEY_DEL_EOL,0x1c0,,,,,,,,,,,,,,,,, +KEY_DEL_EOS,0x1c1,,,,,,,,,,,,,,,,, +KEY_INS_LINE,0x1c2,,,,,,,,,,,,,,,,, +KEY_DEL_LINE,0x1c3,,,,,,,,,,,,,,,,, +KEY_FN,0x1d0,Function,0x3f,,,,,,,,,,,Fn,,,, +KEY_FN_ESC,0x1d1,,,,,,,,,,,,,,,,, +KEY_FN_F1,0x1d2,,,,,,,,,,,,,,,,, +KEY_FN_F2,0x1d3,,,,,,,,,,,,,,,,, +KEY_FN_F3,0x1d4,,,,,,,,,,,,,,,,, +KEY_FN_F4,0x1d5,,,,,,,,,,,,,,,,, +KEY_FN_F5,0x1d6,,,,,,,,,,,,,,,,, +KEY_FN_F6,0x1d7,,,,,,,,,,,,,,,,, +KEY_FN_F7,0x1d8,,,,,,,,,,,,,,,,, +KEY_FN_F8,0x1d9,,,,,,,,,,,,,,,,, +KEY_FN_F9,0x1da,,,,,,,,,,,,,,,,, +KEY_FN_F10,0x1db,,,,,,,,,,,,,,,,, +KEY_FN_F11,0x1dc,,,,,,,,,,,,,,,,, +KEY_FN_F12,0x1dd,,,,,,,,,,,,,,,,, +KEY_FN_1,0x1de,,,,,,,,,,,,,,,,, +KEY_FN_2,0x1df,,,,,,,,,,,,,,,,, +KEY_FN_D,0x1e0,,,,,,,,,,,,,,,,, +KEY_FN_E,0x1e1,,,,,,,,,,,,,,,,, +KEY_FN_F,0x1e2,,,,,,,,,,,,,,,,, +KEY_FN_S,0x1e3,,,,,,,,,,,,,,,,, +KEY_FN_B,0x1e4,,,,,,,,,,,,,,,,, +KEY_BRL_DOT1,0x1f1,,,,,,,,,,,,,,,,, +KEY_BRL_DOT2,0x1f2,,,,,,,,,,,,,,,,, +KEY_BRL_DOT3,0x1f3,,,,,,,,,,,,,,,,, +KEY_BRL_DOT4,0x1f4,,,,,,,,,,,,,,,,, +KEY_BRL_DOT5,0x1f5,,,,,,,,,,,,,,,,, +KEY_BRL_DOT6,0x1f6,,,,,,,,,,,,,,,,, +KEY_BRL_DOT7,0x1f7,,,,,,,,,,,,,,,,, +KEY_BRL_DOT8,0x1f8,,,,,,,,,,,,,,,,, +KEY_BRL_DOT9,0x1f9,,,,,,,,,,,,,,,,, +KEY_BRL_DOT10,0x1fa,,,,,,,,,,,,,,,,, +KEY_NUMERIC_0,0x200,,,,,,,,,,,,,,,,, +KEY_NUMERIC_1,0x201,,,,,,,,,,,,,,,,, +KEY_NUMERIC_2,0x202,,,,,,,,,,,,,,,,, +KEY_NUMERIC_3,0x203,,,,,,,,,,,,,,,,, +KEY_NUMERIC_4,0x204,,,,,,,,,,,,,,,,, +KEY_NUMERIC_5,0x205,,,,,,,,,,,,,,,,, +KEY_NUMERIC_6,0x206,,,,,,,,,,,,,,,,, +KEY_NUMERIC_7,0x207,,,,,,,,,,,,,,,,, +KEY_NUMERIC_8,0x208,,,,,,,,,,,,,,,,, +KEY_NUMERIC_9,0x209,,,,,,,,,,,,,,,,, +KEY_NUMERIC_STAR,0x20a,,,,,,,,,,,,,NumpadStar,,,, +KEY_NUMERIC_POUND,0x20b,,,,,,,,,,,,,NumpadHash,,,, +KEY_RFKILL,0x20c,,,,,,,,,,,,,,,,, diff --git a/keycodemapdb/tests/.gitignore b/keycodemapdb/tests/.gitignore new file mode 100644 index 0000000..0562305 --- /dev/null +++ b/keycodemapdb/tests/.gitignore @@ -0,0 +1,11 @@ +osx2win32.* +osx2win32_name.* +osx2xkb.* +osx2xkb_name.* +html2win32.* +html2win32_name.* +osx.* +osx_name.* +stdc +stdc++ +node_modules/ diff --git a/keycodemapdb/tests/Makefile b/keycodemapdb/tests/Makefile new file mode 100644 index 0000000..b25c77c --- /dev/null +++ b/keycodemapdb/tests/Makefile @@ -0,0 +1,118 @@ +TESTS := stdc stdc++ python2 python3 javascript + +check: $(TESTS) + @set -e; for fn in $(TESTS); do \ + ./$$fn; \ + echo $$fn: OK; \ + done + @echo Done. + +GEN := ../tools/keymap-gen +DATA := ../data/keymaps.csv +SOURCES := $(GEN) $(DATA) + +.DELETE_ON_ERROR: + +stdc: stdc.c osx2win32.h osx2win32_name.h \ + osx2xkb.h osx2xkb_name.h \ + html2win32.h html2win32_name.h \ + osx.h osx_name.h + $(CC) -Wall -o $@ $^ +osx2win32.h: $(SOURCES) + $(GEN) --lang stdc code-map $(DATA) osx win32 > $@ +osx2win32_name.h: $(SOURCES) + $(GEN) --lang stdc name-map $(DATA) osx win32 > $@ +osx2xkb.h: $(SOURCES) + $(GEN) --lang stdc code-map $(DATA) osx xkb > $@ +osx2xkb_name.h: $(SOURCES) + $(GEN) --lang stdc name-map $(DATA) osx xkb > $@ +html2win32.h: $(SOURCES) + $(GEN) --lang stdc code-map $(DATA) html win32 > $@ +html2win32_name.h: $(SOURCES) + $(GEN) --lang stdc name-map $(DATA) html win32 > $@ +osx.h: $(SOURCES) + $(GEN) --lang stdc code-table $(DATA) osx > $@ +osx_name.h: $(SOURCES) + $(GEN) --lang stdc name-table $(DATA) osx > $@ + +stdc++: stdc++.cc osx2win32.hh osx2win32_name.hh \ + osx2xkb.hh osx2xkb_name.hh \ + html2win32.hh html2win32_name.hh \ + osx.hh osx_name.hh + $(CXX) -Wall -std=c++11 -o $@ $^ +osx2win32.hh: $(SOURCES) + $(GEN) --lang stdc++ code-map $(DATA) osx win32 > $@ +osx2win32_name.hh: $(SOURCES) + $(GEN) --lang stdc++ name-map $(DATA) osx win32 > $@ +osx2xkb.hh: $(SOURCES) + $(GEN) --lang stdc++ code-map $(DATA) osx xkb > $@ +osx2xkb_name.hh: $(SOURCES) + $(GEN) --lang stdc++ name-map $(DATA) osx xkb > $@ +html2win32.hh: $(SOURCES) + $(GEN) --lang stdc++ code-map $(DATA) html win32 > $@ +html2win32_name.hh: $(SOURCES) + $(GEN) --lang stdc++ name-map $(DATA) html win32 > $@ +osx.hh: $(SOURCES) + $(GEN) --lang stdc++ code-table $(DATA) osx > $@ +osx_name.hh: $(SOURCES) + $(GEN) --lang stdc++ name-table $(DATA) osx > $@ + +python2: osx2win32.py osx2win32_name.py \ + osx2xkb.py osx2xkb_name.py \ + html2win32.py html2win32_name.py \ + osx.py osx_name.py +osx2win32.py: $(SOURCES) + $(GEN) --lang python2 code-map $(DATA) osx win32 > $@ +osx2win32_name.py: $(SOURCES) + $(GEN) --lang python2 name-map $(DATA) osx win32 > $@ +osx2xkb.py: $(SOURCES) + $(GEN) --lang python2 code-map $(DATA) osx xkb > $@ +osx2xkb_name.py: $(SOURCES) + $(GEN) --lang python2 name-map $(DATA) osx xkb > $@ +html2win32.py: $(SOURCES) + $(GEN) --lang python2 code-map $(DATA) html win32 > $@ +html2win32_name.py: $(SOURCES) + $(GEN) --lang python2 name-map $(DATA) html win32 > $@ +osx.py: $(SOURCES) + $(GEN) --lang python2 code-table $(DATA) osx > $@ +osx_name.py: $(SOURCES) + $(GEN) --lang python2 name-table $(DATA) osx > $@ + +javascript: node_modules/babel-core \ + node_modules/babel-plugin-transform-es2015-modules-commonjs \ + osx2win32.js osx2win32_name.js \ + osx2xkb.js osx2xkb_name.js \ + html2win32.js html2win32_name.js \ + osx.js osx_name.js +node_modules/babel-core: + npm install babel-core +node_modules/babel-plugin-transform-es2015-modules-commonjs: + npm install babel-plugin-transform-es2015-modules-commonjs +osx2win32.js: $(SOURCES) + $(GEN) --lang js code-map $(DATA) osx win32 > $@ +osx2win32_name.js: $(SOURCES) + $(GEN) --lang js name-map $(DATA) osx win32 > $@ +osx2xkb.js: $(SOURCES) + $(GEN) --lang js code-map $(DATA) osx xkb > $@ +osx2xkb_name.js: $(SOURCES) + $(GEN) --lang js name-map $(DATA) osx xkb > $@ +html2win32.js: $(SOURCES) + $(GEN) --lang js code-map $(DATA) html win32 > $@ +html2win32_name.js: $(SOURCES) + $(GEN) --lang js name-map $(DATA) html win32 > $@ +osx.js: $(SOURCES) + $(GEN) --lang js code-table $(DATA) osx > $@ +osx_name.js: $(SOURCES) + $(GEN) --lang js name-table $(DATA) osx > $@ + +clean: + rm -rf node_modules + rm -f osx2win32.* + rm -f osx2win32_name.* + rm -f osx2xkb.* + rm -f osx2xkb_name.* + rm -f html2win32.* + rm -f html2win32_name.* + rm -f osx.* + rm -f osx_name.* + rm -f stdc stdc++ diff --git a/keycodemapdb/tests/javascript b/keycodemapdb/tests/javascript new file mode 100755 index 0000000..5179db2 --- /dev/null +++ b/keycodemapdb/tests/javascript @@ -0,0 +1,53 @@ +#!/usr/bin/env node +/* + * Keycode Map Generator JavaScript Tests + * + * Copyright 2017 Pierre Ossman for Cendio AB + * + * This file is dual license under the terms of the GPLv2 or later + * and 3-clause BSD licenses. + */ + +"use strict"; + +var assert = require('assert'); +var babel = require('babel-core'); +var fs = require('fs'); + +function include(fn) { + var options = { + plugins: ["transform-es2015-modules-commonjs"] + }; + + var code = babel.transformFileSync(fn, options).code; + fs.writeFileSync("." + fn + "_nodejs.js", code); + var imp = require("./." + fn + "_nodejs.js"); + fs.unlinkSync("./." + fn + "_nodejs.js"); + + return imp +} + +var code_map_osx_to_win32 = include("osx2win32.js").default; +var name_map_osx_to_win32 = include("osx2win32_name.js").default; + +var code_map_osx_to_xkb = include("osx2xkb.js").default; +var name_map_osx_to_xkb = include("osx2xkb_name.js").default; + +var code_map_html_to_win32 = include("html2win32.js").default; +var name_map_html_to_win32 = include("html2win32_name.js").default; + +var code_table_osx = include("osx.js").default; +var name_table_osx = include("osx_name.js").default; + +assert.equal(code_map_osx_to_win32[0x1d], 0x30); +assert.equal(name_map_osx_to_win32[0x1d], "VK_0"); + +assert.equal(code_map_osx_to_xkb[0x1d], "AE10"); +assert.equal(name_map_osx_to_xkb[0x1d], "AE10"); + +assert.equal(code_map_html_to_win32["ControlLeft"], 0x11); +assert.equal(name_map_html_to_win32["ControlLeft"], "VK_CONTROL"); + +assert.equal(code_table_osx[0x1d], 0x3b); +assert.equal(name_table_osx[0x1d], "Control"); + diff --git a/keycodemapdb/tests/python2 b/keycodemapdb/tests/python2 new file mode 100755 index 0000000..28a5b03 --- /dev/null +++ b/keycodemapdb/tests/python2 @@ -0,0 +1,3 @@ +#!/bin/sh + +python ./test.py diff --git a/keycodemapdb/tests/python3 b/keycodemapdb/tests/python3 new file mode 100755 index 0000000..ded1f68 --- /dev/null +++ b/keycodemapdb/tests/python3 @@ -0,0 +1,3 @@ +#!/bin/sh + +python3 ./test.py diff --git a/keycodemapdb/tests/stdc++.cc b/keycodemapdb/tests/stdc++.cc new file mode 100644 index 0000000..5e3e8f5 --- /dev/null +++ b/keycodemapdb/tests/stdc++.cc @@ -0,0 +1,40 @@ +/* + * Keycode Map Generator C++ Tests + * + * Copyright 2017 Pierre Ossman for Cendio AB + * + * This file is dual license under the terms of the GPLv2 or later + * and 3-clause BSD licenses. + */ + +#include +#include + +#include "osx2win32.hh" +#include "osx2win32_name.hh" + +#include "osx2xkb.hh" +#include "osx2xkb_name.hh" + +#include "html2win32.hh" +#include "html2win32_name.hh" + +#include "osx.hh" +#include "osx_name.hh" + +int main(int argc, char** argv) +{ + assert(code_map_osx_to_win32[0x1d] == 0x30); + assert(strcmp(name_map_osx_to_win32[0x1d], "VK_0") == 0); + + assert(strcmp(code_map_osx_to_xkb[0x1d], "AE10") == 0); + assert(strcmp(name_map_osx_to_xkb[0x1d], "AE10") == 0); + + assert(code_map_html_to_win32.at("ControlLeft") == 0x11); + assert(strcmp(name_map_html_to_win32.at("ControlLeft"), "VK_CONTROL") == 0); + + assert(code_table_osx[0x1d] == 0x3b); + assert(strcmp(name_table_osx[0x1d], "Control") == 0); + + return 0; +} diff --git a/keycodemapdb/tests/stdc.c b/keycodemapdb/tests/stdc.c new file mode 100644 index 0000000..e4946fa --- /dev/null +++ b/keycodemapdb/tests/stdc.c @@ -0,0 +1,64 @@ +/* + * Keycode Map Generator C Tests + * + * Copyright 2017 Pierre Ossman for Cendio AB + * + * This file is dual license under the terms of the GPLv2 or later + * and 3-clause BSD licenses. + */ + +#include +#include + +#include "osx2win32.h" +#include "osx2win32_name.h" + +#include "osx2xkb.h" +#include "osx2xkb_name.h" + +#include "html2win32.h" +#include "html2win32_name.h" + +#include "osx.h" +#include "osx_name.h" + +#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0])) + +int main(int argc, char** argv) +{ + unsigned i; + + assert(code_map_osx_to_win32_len == ARRAY_SIZE(code_map_osx_to_win32)); + assert(code_map_osx_to_win32[0x1d] == 0x30); + assert(name_map_osx_to_win32_len == ARRAY_SIZE(name_map_osx_to_win32)); + assert(strcmp(name_map_osx_to_win32[0x1d], "VK_0") == 0); + + assert(code_map_osx_to_xkb_len == ARRAY_SIZE(code_map_osx_to_xkb)); + assert(strcmp(code_map_osx_to_xkb[0x1d], "AE10") == 0); + assert(name_map_osx_to_xkb_len == ARRAY_SIZE(name_map_osx_to_xkb)); + assert(strcmp(name_map_osx_to_xkb[0x1d], "AE10") == 0); + + assert(code_map_html_to_win32_len == ARRAY_SIZE(code_map_html_to_win32)); + for (i = 0;i < code_map_html_to_win32_len;i++) { + if (strcmp(code_map_html_to_win32[i].from, "ControlLeft") == 0) { + assert(code_map_html_to_win32[i].to == 0x11); + break; + } + } + assert(i != code_map_html_to_win32_len); + assert(name_map_html_to_win32_len == ARRAY_SIZE(name_map_html_to_win32)); + for (i = 0;i < name_map_html_to_win32_len;i++) { + if (strcmp(name_map_html_to_win32[i].from, "ControlLeft") == 0) { + assert(strcmp(name_map_html_to_win32[i].to, "VK_CONTROL") == 0); + break; + } + } + assert(i != name_map_html_to_win32_len); + + assert(code_table_osx_len == ARRAY_SIZE(code_table_osx)); + assert(code_table_osx[0x1d] == 0x3b); + assert(name_table_osx_len == ARRAY_SIZE(name_table_osx)); + assert(strcmp(name_table_osx[0x1d], "Control") == 0); + + return 0; +} diff --git a/keycodemapdb/tests/test.py b/keycodemapdb/tests/test.py new file mode 100644 index 0000000..f265145 --- /dev/null +++ b/keycodemapdb/tests/test.py @@ -0,0 +1,30 @@ +# Keycode Map Generator Python Tests +# +# Copyright 2017 Pierre Ossman for Cendio AB +# +# This file is dual license under the terms of the GPLv2 or later +# and 3-clause BSD licenses. + +import osx2win32 +import osx2win32_name + +import osx2xkb +import osx2xkb_name + +import html2win32 +import html2win32_name + +import osx +import osx_name + +assert osx2win32.code_map_osx_to_win32[0x1d] == 0x30 +assert osx2win32_name.name_map_osx_to_win32[0x1d] == "VK_0" + +assert osx2xkb.code_map_osx_to_xkb[0x1d] == "AE10" +assert osx2xkb_name.name_map_osx_to_xkb[0x1d] == "AE10" + +assert html2win32.code_map_html_to_win32["ControlLeft"] == 0x11 +assert html2win32_name.name_map_html_to_win32["ControlLeft"] == "VK_CONTROL" + +assert osx.code_table_osx[0x1d] == 0x3b; +assert osx_name.name_table_osx[0x1d] == "Control"; diff --git a/keycodemapdb/thirdparty/LICENSE-argparse.txt b/keycodemapdb/thirdparty/LICENSE-argparse.txt new file mode 100644 index 0000000..640bc78 --- /dev/null +++ b/keycodemapdb/thirdparty/LICENSE-argparse.txt @@ -0,0 +1,20 @@ +argparse is (c) 2006-2009 Steven J. Bethard . + +The argparse module was contributed to Python as of Python 2.7 and thus +was licensed under the Python license. Same license applies to all files in +the argparse package project. + +For details about the Python License, please see doc/Python-License.txt. + +History +------- + +Before (and including) argparse 1.1, the argparse package was licensed under +Apache License v2.0. + +After argparse 1.1, all project files from the argparse project were deleted +due to license compatibility issues between Apache License 2.0 and GNU GPL v2. + +The project repository then had a clean start with some files taken from +Python 2.7.1, so definitely all files are under Python License now. + diff --git a/keycodemapdb/thirdparty/__init__.py b/keycodemapdb/thirdparty/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/keycodemapdb/thirdparty/argparse.py b/keycodemapdb/thirdparty/argparse.py new file mode 100644 index 0000000..70a77cc --- /dev/null +++ b/keycodemapdb/thirdparty/argparse.py @@ -0,0 +1,2392 @@ +# Author: Steven J. Bethard . +# Maintainer: Thomas Waldmann + +"""Command-line parsing library + +This module is an optparse-inspired command-line parsing library that: + + - handles both optional and positional arguments + - produces highly informative usage messages + - supports parsers that dispatch to sub-parsers + +The following is a simple usage example that sums integers from the +command-line and writes the result to a file:: + + parser = argparse.ArgumentParser( + description='sum the integers at the command line') + parser.add_argument( + 'integers', metavar='int', nargs='+', type=int, + help='an integer to be summed') + parser.add_argument( + '--log', default=sys.stdout, type=argparse.FileType('w'), + help='the file where the sum should be written') + args = parser.parse_args() + args.log.write('%s' % sum(args.integers)) + args.log.close() + +The module contains the following public classes: + + - ArgumentParser -- The main entry point for command-line parsing. As the + example above shows, the add_argument() method is used to populate + the parser with actions for optional and positional arguments. Then + the parse_args() method is invoked to convert the args at the + command-line into an object with attributes. + + - ArgumentError -- The exception raised by ArgumentParser objects when + there are errors with the parser's actions. Errors raised while + parsing the command-line are caught by ArgumentParser and emitted + as command-line messages. + + - FileType -- A factory for defining types of files to be created. As the + example above shows, instances of FileType are typically passed as + the type= argument of add_argument() calls. + + - Action -- The base class for parser actions. Typically actions are + selected by passing strings like 'store_true' or 'append_const' to + the action= argument of add_argument(). However, for greater + customization of ArgumentParser actions, subclasses of Action may + be defined and passed as the action= argument. + + - HelpFormatter, RawDescriptionHelpFormatter, RawTextHelpFormatter, + ArgumentDefaultsHelpFormatter -- Formatter classes which + may be passed as the formatter_class= argument to the + ArgumentParser constructor. HelpFormatter is the default, + RawDescriptionHelpFormatter and RawTextHelpFormatter tell the parser + not to change the formatting for help text, and + ArgumentDefaultsHelpFormatter adds information about argument defaults + to the help. + +All other classes in this module are considered implementation details. +(Also note that HelpFormatter and RawDescriptionHelpFormatter are only +considered public as object names -- the API of the formatter objects is +still considered an implementation detail.) +""" + +__version__ = '1.4.0' # we use our own version number independant of the + # one in stdlib and we release this on pypi. + +__external_lib__ = True # to make sure the tests really test THIS lib, + # not the builtin one in Python stdlib + +__all__ = [ + 'ArgumentParser', + 'ArgumentError', + 'ArgumentTypeError', + 'FileType', + 'HelpFormatter', + 'ArgumentDefaultsHelpFormatter', + 'RawDescriptionHelpFormatter', + 'RawTextHelpFormatter', + 'Namespace', + 'Action', + 'ONE_OR_MORE', + 'OPTIONAL', + 'PARSER', + 'REMAINDER', + 'SUPPRESS', + 'ZERO_OR_MORE', +] + + +import copy as _copy +import os as _os +import re as _re +import sys as _sys +import textwrap as _textwrap + +from gettext import gettext as _ + +try: + set +except NameError: + # for python < 2.4 compatibility (sets module is there since 2.3): + from sets import Set as set + +try: + basestring +except NameError: + basestring = str + +try: + sorted +except NameError: + # for python < 2.4 compatibility: + def sorted(iterable, reverse=False): + result = list(iterable) + result.sort() + if reverse: + result.reverse() + return result + + +def _callable(obj): + return hasattr(obj, '__call__') or hasattr(obj, '__bases__') + + +SUPPRESS = '==SUPPRESS==' + +OPTIONAL = '?' +ZERO_OR_MORE = '*' +ONE_OR_MORE = '+' +PARSER = 'A...' +REMAINDER = '...' +_UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args' + +# ============================= +# Utility functions and classes +# ============================= + +class _AttributeHolder(object): + """Abstract base class that provides __repr__. + + The __repr__ method returns a string in the format:: + ClassName(attr=name, attr=name, ...) + The attributes are determined either by a class-level attribute, + '_kwarg_names', or by inspecting the instance __dict__. + """ + + def __repr__(self): + type_name = type(self).__name__ + arg_strings = [] + for arg in self._get_args(): + arg_strings.append(repr(arg)) + for name, value in self._get_kwargs(): + arg_strings.append('%s=%r' % (name, value)) + return '%s(%s)' % (type_name, ', '.join(arg_strings)) + + def _get_kwargs(self): + return sorted(self.__dict__.items()) + + def _get_args(self): + return [] + + +def _ensure_value(namespace, name, value): + if getattr(namespace, name, None) is None: + setattr(namespace, name, value) + return getattr(namespace, name) + + +# =============== +# Formatting Help +# =============== + +class HelpFormatter(object): + """Formatter for generating usage messages and argument help strings. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def __init__(self, + prog, + indent_increment=2, + max_help_position=24, + width=None): + + # default setting for width + if width is None: + try: + width = int(_os.environ['COLUMNS']) + except (KeyError, ValueError): + width = 80 + width -= 2 + + self._prog = prog + self._indent_increment = indent_increment + self._max_help_position = max_help_position + self._width = width + + self._current_indent = 0 + self._level = 0 + self._action_max_length = 0 + + self._root_section = self._Section(self, None) + self._current_section = self._root_section + + self._whitespace_matcher = _re.compile(r'\s+') + self._long_break_matcher = _re.compile(r'\n\n\n+') + + # =============================== + # Section and indentation methods + # =============================== + def _indent(self): + self._current_indent += self._indent_increment + self._level += 1 + + def _dedent(self): + self._current_indent -= self._indent_increment + assert self._current_indent >= 0, 'Indent decreased below 0.' + self._level -= 1 + + class _Section(object): + + def __init__(self, formatter, parent, heading=None): + self.formatter = formatter + self.parent = parent + self.heading = heading + self.items = [] + + def format_help(self): + # format the indented section + if self.parent is not None: + self.formatter._indent() + join = self.formatter._join_parts + for func, args in self.items: + func(*args) + item_help = join([func(*args) for func, args in self.items]) + if self.parent is not None: + self.formatter._dedent() + + # return nothing if the section was empty + if not item_help: + return '' + + # add the heading if the section was non-empty + if self.heading is not SUPPRESS and self.heading is not None: + current_indent = self.formatter._current_indent + heading = '%*s%s:\n' % (current_indent, '', self.heading) + else: + heading = '' + + # join the section-initial newline, the heading and the help + return join(['\n', heading, item_help, '\n']) + + def _add_item(self, func, args): + self._current_section.items.append((func, args)) + + # ======================== + # Message building methods + # ======================== + def start_section(self, heading): + self._indent() + section = self._Section(self, self._current_section, heading) + self._add_item(section.format_help, []) + self._current_section = section + + def end_section(self): + self._current_section = self._current_section.parent + self._dedent() + + def add_text(self, text): + if text is not SUPPRESS and text is not None: + self._add_item(self._format_text, [text]) + + def add_usage(self, usage, actions, groups, prefix=None): + if usage is not SUPPRESS: + args = usage, actions, groups, prefix + self._add_item(self._format_usage, args) + + def add_argument(self, action): + if action.help is not SUPPRESS: + + # find all invocations + get_invocation = self._format_action_invocation + invocations = [get_invocation(action)] + for subaction in self._iter_indented_subactions(action): + invocations.append(get_invocation(subaction)) + + # update the maximum item length + invocation_length = max([len(s) for s in invocations]) + action_length = invocation_length + self._current_indent + self._action_max_length = max(self._action_max_length, + action_length) + + # add the item to the list + self._add_item(self._format_action, [action]) + + def add_arguments(self, actions): + for action in actions: + self.add_argument(action) + + # ======================= + # Help-formatting methods + # ======================= + def format_help(self): + help = self._root_section.format_help() + if help: + help = self._long_break_matcher.sub('\n\n', help) + help = help.strip('\n') + '\n' + return help + + def _join_parts(self, part_strings): + return ''.join([part + for part in part_strings + if part and part is not SUPPRESS]) + + def _format_usage(self, usage, actions, groups, prefix): + if prefix is None: + prefix = _('usage: ') + + # if usage is specified, use that + if usage is not None: + usage = usage % dict(prog=self._prog) + + # if no optionals or positionals are available, usage is just prog + elif usage is None and not actions: + usage = '%(prog)s' % dict(prog=self._prog) + + # if optionals and positionals are available, calculate usage + elif usage is None: + prog = '%(prog)s' % dict(prog=self._prog) + + # split optionals from positionals + optionals = [] + positionals = [] + for action in actions: + if action.option_strings: + optionals.append(action) + else: + positionals.append(action) + + # build full usage string + format = self._format_actions_usage + action_usage = format(optionals + positionals, groups) + usage = ' '.join([s for s in [prog, action_usage] if s]) + + # wrap the usage parts if it's too long + text_width = self._width - self._current_indent + if len(prefix) + len(usage) > text_width: + + # break usage into wrappable parts + part_regexp = r'\(.*?\)+|\[.*?\]+|\S+' + opt_usage = format(optionals, groups) + pos_usage = format(positionals, groups) + opt_parts = _re.findall(part_regexp, opt_usage) + pos_parts = _re.findall(part_regexp, pos_usage) + assert ' '.join(opt_parts) == opt_usage + assert ' '.join(pos_parts) == pos_usage + + # helper for wrapping lines + def get_lines(parts, indent, prefix=None): + lines = [] + line = [] + if prefix is not None: + line_len = len(prefix) - 1 + else: + line_len = len(indent) - 1 + for part in parts: + if line_len + 1 + len(part) > text_width: + lines.append(indent + ' '.join(line)) + line = [] + line_len = len(indent) - 1 + line.append(part) + line_len += len(part) + 1 + if line: + lines.append(indent + ' '.join(line)) + if prefix is not None: + lines[0] = lines[0][len(indent):] + return lines + + # if prog is short, follow it with optionals or positionals + if len(prefix) + len(prog) <= 0.75 * text_width: + indent = ' ' * (len(prefix) + len(prog) + 1) + if opt_parts: + lines = get_lines([prog] + opt_parts, indent, prefix) + lines.extend(get_lines(pos_parts, indent)) + elif pos_parts: + lines = get_lines([prog] + pos_parts, indent, prefix) + else: + lines = [prog] + + # if prog is long, put it on its own line + else: + indent = ' ' * len(prefix) + parts = opt_parts + pos_parts + lines = get_lines(parts, indent) + if len(lines) > 1: + lines = [] + lines.extend(get_lines(opt_parts, indent)) + lines.extend(get_lines(pos_parts, indent)) + lines = [prog] + lines + + # join lines into usage + usage = '\n'.join(lines) + + # prefix with 'usage:' + return '%s%s\n\n' % (prefix, usage) + + def _format_actions_usage(self, actions, groups): + # find group indices and identify actions in groups + group_actions = set() + inserts = {} + for group in groups: + try: + start = actions.index(group._group_actions[0]) + except ValueError: + continue + else: + end = start + len(group._group_actions) + if actions[start:end] == group._group_actions: + for action in group._group_actions: + group_actions.add(action) + if not group.required: + if start in inserts: + inserts[start] += ' [' + else: + inserts[start] = '[' + inserts[end] = ']' + else: + if start in inserts: + inserts[start] += ' (' + else: + inserts[start] = '(' + inserts[end] = ')' + for i in range(start + 1, end): + inserts[i] = '|' + + # collect all actions format strings + parts = [] + for i, action in enumerate(actions): + + # suppressed arguments are marked with None + # remove | separators for suppressed arguments + if action.help is SUPPRESS: + parts.append(None) + if inserts.get(i) == '|': + inserts.pop(i) + elif inserts.get(i + 1) == '|': + inserts.pop(i + 1) + + # produce all arg strings + elif not action.option_strings: + part = self._format_args(action, action.dest) + + # if it's in a group, strip the outer [] + if action in group_actions: + if part[0] == '[' and part[-1] == ']': + part = part[1:-1] + + # add the action string to the list + parts.append(part) + + # produce the first way to invoke the option in brackets + else: + option_string = action.option_strings[0] + + # if the Optional doesn't take a value, format is: + # -s or --long + if action.nargs == 0: + part = '%s' % option_string + + # if the Optional takes a value, format is: + # -s ARGS or --long ARGS + else: + default = action.dest.upper() + args_string = self._format_args(action, default) + part = '%s %s' % (option_string, args_string) + + # make it look optional if it's not required or in a group + if not action.required and action not in group_actions: + part = '[%s]' % part + + # add the action string to the list + parts.append(part) + + # insert things at the necessary indices + for i in sorted(inserts, reverse=True): + parts[i:i] = [inserts[i]] + + # join all the action items with spaces + text = ' '.join([item for item in parts if item is not None]) + + # clean up separators for mutually exclusive groups + open = r'[\[(]' + close = r'[\])]' + text = _re.sub(r'(%s) ' % open, r'\1', text) + text = _re.sub(r' (%s)' % close, r'\1', text) + text = _re.sub(r'%s *%s' % (open, close), r'', text) + text = _re.sub(r'\(([^|]*)\)', r'\1', text) + text = text.strip() + + # return the text + return text + + def _format_text(self, text): + if '%(prog)' in text: + text = text % dict(prog=self._prog) + text_width = self._width - self._current_indent + indent = ' ' * self._current_indent + return self._fill_text(text, text_width, indent) + '\n\n' + + def _format_action(self, action): + # determine the required width and the entry label + help_position = min(self._action_max_length + 2, + self._max_help_position) + help_width = self._width - help_position + action_width = help_position - self._current_indent - 2 + action_header = self._format_action_invocation(action) + + # ho nelp; start on same line and add a final newline + if not action.help: + tup = self._current_indent, '', action_header + action_header = '%*s%s\n' % tup + + # short action name; start on the same line and pad two spaces + elif len(action_header) <= action_width: + tup = self._current_indent, '', action_width, action_header + action_header = '%*s%-*s ' % tup + indent_first = 0 + + # long action name; start on the next line + else: + tup = self._current_indent, '', action_header + action_header = '%*s%s\n' % tup + indent_first = help_position + + # collect the pieces of the action help + parts = [action_header] + + # if there was help for the action, add lines of help text + if action.help: + help_text = self._expand_help(action) + help_lines = self._split_lines(help_text, help_width) + parts.append('%*s%s\n' % (indent_first, '', help_lines[0])) + for line in help_lines[1:]: + parts.append('%*s%s\n' % (help_position, '', line)) + + # or add a newline if the description doesn't end with one + elif not action_header.endswith('\n'): + parts.append('\n') + + # if there are any sub-actions, add their help as well + for subaction in self._iter_indented_subactions(action): + parts.append(self._format_action(subaction)) + + # return a single string + return self._join_parts(parts) + + def _format_action_invocation(self, action): + if not action.option_strings: + metavar, = self._metavar_formatter(action, action.dest)(1) + return metavar + + else: + parts = [] + + # if the Optional doesn't take a value, format is: + # -s, --long + if action.nargs == 0: + parts.extend(action.option_strings) + + # if the Optional takes a value, format is: + # -s ARGS, --long ARGS + else: + default = action.dest.upper() + args_string = self._format_args(action, default) + for option_string in action.option_strings: + parts.append('%s %s' % (option_string, args_string)) + + return ', '.join(parts) + + def _metavar_formatter(self, action, default_metavar): + if action.metavar is not None: + result = action.metavar + elif action.choices is not None: + choice_strs = [str(choice) for choice in action.choices] + result = '{%s}' % ','.join(choice_strs) + else: + result = default_metavar + + def format(tuple_size): + if isinstance(result, tuple): + return result + else: + return (result, ) * tuple_size + return format + + def _format_args(self, action, default_metavar): + get_metavar = self._metavar_formatter(action, default_metavar) + if action.nargs is None: + result = '%s' % get_metavar(1) + elif action.nargs == OPTIONAL: + result = '[%s]' % get_metavar(1) + elif action.nargs == ZERO_OR_MORE: + result = '[%s [%s ...]]' % get_metavar(2) + elif action.nargs == ONE_OR_MORE: + result = '%s [%s ...]' % get_metavar(2) + elif action.nargs == REMAINDER: + result = '...' + elif action.nargs == PARSER: + result = '%s ...' % get_metavar(1) + else: + formats = ['%s' for _ in range(action.nargs)] + result = ' '.join(formats) % get_metavar(action.nargs) + return result + + def _expand_help(self, action): + params = dict(vars(action), prog=self._prog) + for name in list(params): + if params[name] is SUPPRESS: + del params[name] + for name in list(params): + if hasattr(params[name], '__name__'): + params[name] = params[name].__name__ + if params.get('choices') is not None: + choices_str = ', '.join([str(c) for c in params['choices']]) + params['choices'] = choices_str + return self._get_help_string(action) % params + + def _iter_indented_subactions(self, action): + try: + get_subactions = action._get_subactions + except AttributeError: + pass + else: + self._indent() + for subaction in get_subactions(): + yield subaction + self._dedent() + + def _split_lines(self, text, width): + text = self._whitespace_matcher.sub(' ', text).strip() + return _textwrap.wrap(text, width) + + def _fill_text(self, text, width, indent): + text = self._whitespace_matcher.sub(' ', text).strip() + return _textwrap.fill(text, width, initial_indent=indent, + subsequent_indent=indent) + + def _get_help_string(self, action): + return action.help + + +class RawDescriptionHelpFormatter(HelpFormatter): + """Help message formatter which retains any formatting in descriptions. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def _fill_text(self, text, width, indent): + return ''.join([indent + line for line in text.splitlines(True)]) + + +class RawTextHelpFormatter(RawDescriptionHelpFormatter): + """Help message formatter which retains formatting of all help text. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def _split_lines(self, text, width): + return text.splitlines() + + +class ArgumentDefaultsHelpFormatter(HelpFormatter): + """Help message formatter which adds default values to argument help. + + Only the name of this class is considered a public API. All the methods + provided by the class are considered an implementation detail. + """ + + def _get_help_string(self, action): + help = action.help + if '%(default)' not in action.help: + if action.default is not SUPPRESS: + defaulting_nargs = [OPTIONAL, ZERO_OR_MORE] + if action.option_strings or action.nargs in defaulting_nargs: + help += ' (default: %(default)s)' + return help + + +# ===================== +# Options and Arguments +# ===================== + +def _get_action_name(argument): + if argument is None: + return None + elif argument.option_strings: + return '/'.join(argument.option_strings) + elif argument.metavar not in (None, SUPPRESS): + return argument.metavar + elif argument.dest not in (None, SUPPRESS): + return argument.dest + else: + return None + + +class ArgumentError(Exception): + """An error from creating or using an argument (optional or positional). + + The string value of this exception is the message, augmented with + information about the argument that caused it. + """ + + def __init__(self, argument, message): + self.argument_name = _get_action_name(argument) + self.message = message + + def __str__(self): + if self.argument_name is None: + format = '%(message)s' + else: + format = 'argument %(argument_name)s: %(message)s' + return format % dict(message=self.message, + argument_name=self.argument_name) + + +class ArgumentTypeError(Exception): + """An error from trying to convert a command line string to a type.""" + pass + + +# ============== +# Action classes +# ============== + +class Action(_AttributeHolder): + """Information about how to convert command line strings to Python objects. + + Action objects are used by an ArgumentParser to represent the information + needed to parse a single argument from one or more strings from the + command line. The keyword arguments to the Action constructor are also + all attributes of Action instances. + + Keyword Arguments: + + - option_strings -- A list of command-line option strings which + should be associated with this action. + + - dest -- The name of the attribute to hold the created object(s) + + - nargs -- The number of command-line arguments that should be + consumed. By default, one argument will be consumed and a single + value will be produced. Other values include: + - N (an integer) consumes N arguments (and produces a list) + - '?' consumes zero or one arguments + - '*' consumes zero or more arguments (and produces a list) + - '+' consumes one or more arguments (and produces a list) + Note that the difference between the default and nargs=1 is that + with the default, a single value will be produced, while with + nargs=1, a list containing a single value will be produced. + + - const -- The value to be produced if the option is specified and the + option uses an action that takes no values. + + - default -- The value to be produced if the option is not specified. + + - type -- The type which the command-line arguments should be converted + to, should be one of 'string', 'int', 'float', 'complex' or a + callable object that accepts a single string argument. If None, + 'string' is assumed. + + - choices -- A container of values that should be allowed. If not None, + after a command-line argument has been converted to the appropriate + type, an exception will be raised if it is not a member of this + collection. + + - required -- True if the action must always be specified at the + command line. This is only meaningful for optional command-line + arguments. + + - help -- The help string describing the argument. + + - metavar -- The name to be used for the option's argument with the + help string. If None, the 'dest' value will be used as the name. + """ + + def __init__(self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None): + self.option_strings = option_strings + self.dest = dest + self.nargs = nargs + self.const = const + self.default = default + self.type = type + self.choices = choices + self.required = required + self.help = help + self.metavar = metavar + + def _get_kwargs(self): + names = [ + 'option_strings', + 'dest', + 'nargs', + 'const', + 'default', + 'type', + 'choices', + 'help', + 'metavar', + ] + return [(name, getattr(self, name)) for name in names] + + def __call__(self, parser, namespace, values, option_string=None): + raise NotImplementedError(_('.__call__() not defined')) + + +class _StoreAction(Action): + + def __init__(self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None): + if nargs == 0: + raise ValueError('nargs for store actions must be > 0; if you ' + 'have nothing to store, actions such as store ' + 'true or store const may be more appropriate') + if const is not None and nargs != OPTIONAL: + raise ValueError('nargs must be %r to supply const' % OPTIONAL) + super(_StoreAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=nargs, + const=const, + default=default, + type=type, + choices=choices, + required=required, + help=help, + metavar=metavar) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, values) + + +class _StoreConstAction(Action): + + def __init__(self, + option_strings, + dest, + const, + default=None, + required=False, + help=None, + metavar=None): + super(_StoreConstAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=const, + default=default, + required=required, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, self.const) + + +class _StoreTrueAction(_StoreConstAction): + + def __init__(self, + option_strings, + dest, + default=False, + required=False, + help=None): + super(_StoreTrueAction, self).__init__( + option_strings=option_strings, + dest=dest, + const=True, + default=default, + required=required, + help=help) + + +class _StoreFalseAction(_StoreConstAction): + + def __init__(self, + option_strings, + dest, + default=True, + required=False, + help=None): + super(_StoreFalseAction, self).__init__( + option_strings=option_strings, + dest=dest, + const=False, + default=default, + required=required, + help=help) + + +class _AppendAction(Action): + + def __init__(self, + option_strings, + dest, + nargs=None, + const=None, + default=None, + type=None, + choices=None, + required=False, + help=None, + metavar=None): + if nargs == 0: + raise ValueError('nargs for append actions must be > 0; if arg ' + 'strings are not supplying the value to append, ' + 'the append const action may be more appropriate') + if const is not None and nargs != OPTIONAL: + raise ValueError('nargs must be %r to supply const' % OPTIONAL) + super(_AppendAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=nargs, + const=const, + default=default, + type=type, + choices=choices, + required=required, + help=help, + metavar=metavar) + + def __call__(self, parser, namespace, values, option_string=None): + items = _copy.copy(_ensure_value(namespace, self.dest, [])) + items.append(values) + setattr(namespace, self.dest, items) + + +class _AppendConstAction(Action): + + def __init__(self, + option_strings, + dest, + const, + default=None, + required=False, + help=None, + metavar=None): + super(_AppendConstAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + const=const, + default=default, + required=required, + help=help, + metavar=metavar) + + def __call__(self, parser, namespace, values, option_string=None): + items = _copy.copy(_ensure_value(namespace, self.dest, [])) + items.append(self.const) + setattr(namespace, self.dest, items) + + +class _CountAction(Action): + + def __init__(self, + option_strings, + dest, + default=None, + required=False, + help=None): + super(_CountAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=0, + default=default, + required=required, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + new_count = _ensure_value(namespace, self.dest, 0) + 1 + setattr(namespace, self.dest, new_count) + + +class _HelpAction(Action): + + def __init__(self, + option_strings, + dest=SUPPRESS, + default=SUPPRESS, + help=None): + super(_HelpAction, self).__init__( + option_strings=option_strings, + dest=dest, + default=default, + nargs=0, + help=help) + + def __call__(self, parser, namespace, values, option_string=None): + parser.print_help() + parser.exit() + + +class _VersionAction(Action): + + def __init__(self, + option_strings, + version=None, + dest=SUPPRESS, + default=SUPPRESS, + help="show program's version number and exit"): + super(_VersionAction, self).__init__( + option_strings=option_strings, + dest=dest, + default=default, + nargs=0, + help=help) + self.version = version + + def __call__(self, parser, namespace, values, option_string=None): + version = self.version + if version is None: + version = parser.version + formatter = parser._get_formatter() + formatter.add_text(version) + parser.exit(message=formatter.format_help()) + + +class _SubParsersAction(Action): + + class _ChoicesPseudoAction(Action): + + def __init__(self, name, aliases, help): + metavar = dest = name + if aliases: + metavar += ' (%s)' % ', '.join(aliases) + sup = super(_SubParsersAction._ChoicesPseudoAction, self) + sup.__init__(option_strings=[], dest=dest, help=help, + metavar=metavar) + + def __init__(self, + option_strings, + prog, + parser_class, + dest=SUPPRESS, + help=None, + metavar=None): + + self._prog_prefix = prog + self._parser_class = parser_class + self._name_parser_map = {} + self._choices_actions = [] + + super(_SubParsersAction, self).__init__( + option_strings=option_strings, + dest=dest, + nargs=PARSER, + choices=self._name_parser_map, + help=help, + metavar=metavar) + + def add_parser(self, name, **kwargs): + # set prog from the existing prefix + if kwargs.get('prog') is None: + kwargs['prog'] = '%s %s' % (self._prog_prefix, name) + + aliases = kwargs.pop('aliases', ()) + + # create a pseudo-action to hold the choice help + if 'help' in kwargs: + help = kwargs.pop('help') + choice_action = self._ChoicesPseudoAction(name, aliases, help) + self._choices_actions.append(choice_action) + + # create the parser and add it to the map + parser = self._parser_class(**kwargs) + self._name_parser_map[name] = parser + + # make parser available under aliases also + for alias in aliases: + self._name_parser_map[alias] = parser + + return parser + + def _get_subactions(self): + return self._choices_actions + + def __call__(self, parser, namespace, values, option_string=None): + parser_name = values[0] + arg_strings = values[1:] + + # set the parser name if requested + if self.dest is not SUPPRESS: + setattr(namespace, self.dest, parser_name) + + # select the parser + try: + parser = self._name_parser_map[parser_name] + except KeyError: + tup = parser_name, ', '.join(self._name_parser_map) + msg = _('unknown parser %r (choices: %s)' % tup) + raise ArgumentError(self, msg) + + # parse all the remaining options into the namespace + # store any unrecognized options on the object, so that the top + # level parser can decide what to do with them + namespace, arg_strings = parser.parse_known_args(arg_strings, namespace) + if arg_strings: + vars(namespace).setdefault(_UNRECOGNIZED_ARGS_ATTR, []) + getattr(namespace, _UNRECOGNIZED_ARGS_ATTR).extend(arg_strings) + + +# ============== +# Type classes +# ============== + +class FileType(object): + """Factory for creating file object types + + Instances of FileType are typically passed as type= arguments to the + ArgumentParser add_argument() method. + + Keyword Arguments: + - mode -- A string indicating how the file is to be opened. Accepts the + same values as the builtin open() function. + - bufsize -- The file's desired buffer size. Accepts the same values as + the builtin open() function. + """ + + def __init__(self, mode='r', bufsize=None): + self._mode = mode + self._bufsize = bufsize + + def __call__(self, string): + # the special argument "-" means sys.std{in,out} + if string == '-': + if 'r' in self._mode: + return _sys.stdin + elif 'w' in self._mode: + return _sys.stdout + else: + msg = _('argument "-" with mode %r' % self._mode) + raise ValueError(msg) + + try: + # all other arguments are used as file names + if self._bufsize: + return open(string, self._mode, self._bufsize) + else: + return open(string, self._mode) + except IOError: + err = _sys.exc_info()[1] + message = _("can't open '%s': %s") + raise ArgumentTypeError(message % (string, err)) + + def __repr__(self): + args = [self._mode, self._bufsize] + args_str = ', '.join([repr(arg) for arg in args if arg is not None]) + return '%s(%s)' % (type(self).__name__, args_str) + +# =========================== +# Optional and Positional Parsing +# =========================== + +class Namespace(_AttributeHolder): + """Simple object for storing attributes. + + Implements equality by attribute names and values, and provides a simple + string representation. + """ + + def __init__(self, **kwargs): + for name in kwargs: + setattr(self, name, kwargs[name]) + + __hash__ = None + + def __eq__(self, other): + return vars(self) == vars(other) + + def __ne__(self, other): + return not (self == other) + + def __contains__(self, key): + return key in self.__dict__ + + +class _ActionsContainer(object): + + def __init__(self, + description, + prefix_chars, + argument_default, + conflict_handler): + super(_ActionsContainer, self).__init__() + + self.description = description + self.argument_default = argument_default + self.prefix_chars = prefix_chars + self.conflict_handler = conflict_handler + + # set up registries + self._registries = {} + + # register actions + self.register('action', None, _StoreAction) + self.register('action', 'store', _StoreAction) + self.register('action', 'store_const', _StoreConstAction) + self.register('action', 'store_true', _StoreTrueAction) + self.register('action', 'store_false', _StoreFalseAction) + self.register('action', 'append', _AppendAction) + self.register('action', 'append_const', _AppendConstAction) + self.register('action', 'count', _CountAction) + self.register('action', 'help', _HelpAction) + self.register('action', 'version', _VersionAction) + self.register('action', 'parsers', _SubParsersAction) + + # raise an exception if the conflict handler is invalid + self._get_handler() + + # action storage + self._actions = [] + self._option_string_actions = {} + + # groups + self._action_groups = [] + self._mutually_exclusive_groups = [] + + # defaults storage + self._defaults = {} + + # determines whether an "option" looks like a negative number + self._negative_number_matcher = _re.compile(r'^-\d+$|^-\d*\.\d+$') + + # whether or not there are any optionals that look like negative + # numbers -- uses a list so it can be shared and edited + self._has_negative_number_optionals = [] + + # ==================== + # Registration methods + # ==================== + def register(self, registry_name, value, object): + registry = self._registries.setdefault(registry_name, {}) + registry[value] = object + + def _registry_get(self, registry_name, value, default=None): + return self._registries[registry_name].get(value, default) + + # ================================== + # Namespace default accessor methods + # ================================== + def set_defaults(self, **kwargs): + self._defaults.update(kwargs) + + # if these defaults match any existing arguments, replace + # the previous default on the object with the new one + for action in self._actions: + if action.dest in kwargs: + action.default = kwargs[action.dest] + + def get_default(self, dest): + for action in self._actions: + if action.dest == dest and action.default is not None: + return action.default + return self._defaults.get(dest, None) + + + # ======================= + # Adding argument actions + # ======================= + def add_argument(self, *args, **kwargs): + """ + add_argument(dest, ..., name=value, ...) + add_argument(option_string, option_string, ..., name=value, ...) + """ + + # if no positional args are supplied or only one is supplied and + # it doesn't look like an option string, parse a positional + # argument + chars = self.prefix_chars + if not args or len(args) == 1 and args[0][0] not in chars: + if args and 'dest' in kwargs: + raise ValueError('dest supplied twice for positional argument') + kwargs = self._get_positional_kwargs(*args, **kwargs) + + # otherwise, we're adding an optional argument + else: + kwargs = self._get_optional_kwargs(*args, **kwargs) + + # if no default was supplied, use the parser-level default + if 'default' not in kwargs: + dest = kwargs['dest'] + if dest in self._defaults: + kwargs['default'] = self._defaults[dest] + elif self.argument_default is not None: + kwargs['default'] = self.argument_default + + # create the action object, and add it to the parser + action_class = self._pop_action_class(kwargs) + if not _callable(action_class): + raise ValueError('unknown action "%s"' % action_class) + action = action_class(**kwargs) + + # raise an error if the action type is not callable + type_func = self._registry_get('type', action.type, action.type) + if not _callable(type_func): + raise ValueError('%r is not callable' % type_func) + + return self._add_action(action) + + def add_argument_group(self, *args, **kwargs): + group = _ArgumentGroup(self, *args, **kwargs) + self._action_groups.append(group) + return group + + def add_mutually_exclusive_group(self, **kwargs): + group = _MutuallyExclusiveGroup(self, **kwargs) + self._mutually_exclusive_groups.append(group) + return group + + def _add_action(self, action): + # resolve any conflicts + self._check_conflict(action) + + # add to actions list + self._actions.append(action) + action.container = self + + # index the action by any option strings it has + for option_string in action.option_strings: + self._option_string_actions[option_string] = action + + # set the flag if any option strings look like negative numbers + for option_string in action.option_strings: + if self._negative_number_matcher.match(option_string): + if not self._has_negative_number_optionals: + self._has_negative_number_optionals.append(True) + + # return the created action + return action + + def _remove_action(self, action): + self._actions.remove(action) + + def _add_container_actions(self, container): + # collect groups by titles + title_group_map = {} + for group in self._action_groups: + if group.title in title_group_map: + msg = _('cannot merge actions - two groups are named %r') + raise ValueError(msg % (group.title)) + title_group_map[group.title] = group + + # map each action to its group + group_map = {} + for group in container._action_groups: + + # if a group with the title exists, use that, otherwise + # create a new group matching the container's group + if group.title not in title_group_map: + title_group_map[group.title] = self.add_argument_group( + title=group.title, + description=group.description, + conflict_handler=group.conflict_handler) + + # map the actions to their new group + for action in group._group_actions: + group_map[action] = title_group_map[group.title] + + # add container's mutually exclusive groups + # NOTE: if add_mutually_exclusive_group ever gains title= and + # description= then this code will need to be expanded as above + for group in container._mutually_exclusive_groups: + mutex_group = self.add_mutually_exclusive_group( + required=group.required) + + # map the actions to their new mutex group + for action in group._group_actions: + group_map[action] = mutex_group + + # add all actions to this container or their group + for action in container._actions: + group_map.get(action, self)._add_action(action) + + def _get_positional_kwargs(self, dest, **kwargs): + # make sure required is not specified + if 'required' in kwargs: + msg = _("'required' is an invalid argument for positionals") + raise TypeError(msg) + + # mark positional arguments as required if at least one is + # always required + if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]: + kwargs['required'] = True + if kwargs.get('nargs') == ZERO_OR_MORE and 'default' not in kwargs: + kwargs['required'] = True + + # return the keyword arguments with no option strings + return dict(kwargs, dest=dest, option_strings=[]) + + def _get_optional_kwargs(self, *args, **kwargs): + # determine short and long option strings + option_strings = [] + long_option_strings = [] + for option_string in args: + # error on strings that don't start with an appropriate prefix + if not option_string[0] in self.prefix_chars: + msg = _('invalid option string %r: ' + 'must start with a character %r') + tup = option_string, self.prefix_chars + raise ValueError(msg % tup) + + # strings starting with two prefix characters are long options + option_strings.append(option_string) + if option_string[0] in self.prefix_chars: + if len(option_string) > 1: + if option_string[1] in self.prefix_chars: + long_option_strings.append(option_string) + + # infer destination, '--foo-bar' -> 'foo_bar' and '-x' -> 'x' + dest = kwargs.pop('dest', None) + if dest is None: + if long_option_strings: + dest_option_string = long_option_strings[0] + else: + dest_option_string = option_strings[0] + dest = dest_option_string.lstrip(self.prefix_chars) + if not dest: + msg = _('dest= is required for options like %r') + raise ValueError(msg % option_string) + dest = dest.replace('-', '_') + + # return the updated keyword arguments + return dict(kwargs, dest=dest, option_strings=option_strings) + + def _pop_action_class(self, kwargs, default=None): + action = kwargs.pop('action', default) + return self._registry_get('action', action, action) + + def _get_handler(self): + # determine function from conflict handler string + handler_func_name = '_handle_conflict_%s' % self.conflict_handler + try: + return getattr(self, handler_func_name) + except AttributeError: + msg = _('invalid conflict_resolution value: %r') + raise ValueError(msg % self.conflict_handler) + + def _check_conflict(self, action): + + # find all options that conflict with this option + confl_optionals = [] + for option_string in action.option_strings: + if option_string in self._option_string_actions: + confl_optional = self._option_string_actions[option_string] + confl_optionals.append((option_string, confl_optional)) + + # resolve any conflicts + if confl_optionals: + conflict_handler = self._get_handler() + conflict_handler(action, confl_optionals) + + def _handle_conflict_error(self, action, conflicting_actions): + message = _('conflicting option string(s): %s') + conflict_string = ', '.join([option_string + for option_string, action + in conflicting_actions]) + raise ArgumentError(action, message % conflict_string) + + def _handle_conflict_resolve(self, action, conflicting_actions): + + # remove all conflicting options + for option_string, action in conflicting_actions: + + # remove the conflicting option + action.option_strings.remove(option_string) + self._option_string_actions.pop(option_string, None) + + # if the option now has no option string, remove it from the + # container holding it + if not action.option_strings: + action.container._remove_action(action) + + +class _ArgumentGroup(_ActionsContainer): + + def __init__(self, container, title=None, description=None, **kwargs): + # add any missing keyword arguments by checking the container + update = kwargs.setdefault + update('conflict_handler', container.conflict_handler) + update('prefix_chars', container.prefix_chars) + update('argument_default', container.argument_default) + super_init = super(_ArgumentGroup, self).__init__ + super_init(description=description, **kwargs) + + # group attributes + self.title = title + self._group_actions = [] + + # share most attributes with the container + self._registries = container._registries + self._actions = container._actions + self._option_string_actions = container._option_string_actions + self._defaults = container._defaults + self._has_negative_number_optionals = \ + container._has_negative_number_optionals + + def _add_action(self, action): + action = super(_ArgumentGroup, self)._add_action(action) + self._group_actions.append(action) + return action + + def _remove_action(self, action): + super(_ArgumentGroup, self)._remove_action(action) + self._group_actions.remove(action) + + +class _MutuallyExclusiveGroup(_ArgumentGroup): + + def __init__(self, container, required=False): + super(_MutuallyExclusiveGroup, self).__init__(container) + self.required = required + self._container = container + + def _add_action(self, action): + if action.required: + msg = _('mutually exclusive arguments must be optional') + raise ValueError(msg) + action = self._container._add_action(action) + self._group_actions.append(action) + return action + + def _remove_action(self, action): + self._container._remove_action(action) + self._group_actions.remove(action) + + +class ArgumentParser(_AttributeHolder, _ActionsContainer): + """Object for parsing command line strings into Python objects. + + Keyword Arguments: + - prog -- The name of the program (default: sys.argv[0]) + - usage -- A usage message (default: auto-generated from arguments) + - description -- A description of what the program does + - epilog -- Text following the argument descriptions + - parents -- Parsers whose arguments should be copied into this one + - formatter_class -- HelpFormatter class for printing help messages + - prefix_chars -- Characters that prefix optional arguments + - fromfile_prefix_chars -- Characters that prefix files containing + additional arguments + - argument_default -- The default value for all arguments + - conflict_handler -- String indicating how to handle conflicts + - add_help -- Add a -h/-help option + """ + + def __init__(self, + prog=None, + usage=None, + description=None, + epilog=None, + version=None, + parents=[], + formatter_class=HelpFormatter, + prefix_chars='-', + fromfile_prefix_chars=None, + argument_default=None, + conflict_handler='error', + add_help=True): + + if version is not None: + import warnings + warnings.warn( + """The "version" argument to ArgumentParser is deprecated. """ + """Please use """ + """"add_argument(..., action='version', version="N", ...)" """ + """instead""", DeprecationWarning) + + superinit = super(ArgumentParser, self).__init__ + superinit(description=description, + prefix_chars=prefix_chars, + argument_default=argument_default, + conflict_handler=conflict_handler) + + # default setting for prog + if prog is None: + prog = _os.path.basename(_sys.argv[0]) + + self.prog = prog + self.usage = usage + self.epilog = epilog + self.version = version + self.formatter_class = formatter_class + self.fromfile_prefix_chars = fromfile_prefix_chars + self.add_help = add_help + + add_group = self.add_argument_group + self._positionals = add_group(_('positional arguments')) + self._optionals = add_group(_('optional arguments')) + self._subparsers = None + + # register types + def identity(string): + return string + self.register('type', None, identity) + + # add help and version arguments if necessary + # (using explicit default to override global argument_default) + if '-' in prefix_chars: + default_prefix = '-' + else: + default_prefix = prefix_chars[0] + if self.add_help: + self.add_argument( + default_prefix+'h', default_prefix*2+'help', + action='help', default=SUPPRESS, + help=_('show this help message and exit')) + if self.version: + self.add_argument( + default_prefix+'v', default_prefix*2+'version', + action='version', default=SUPPRESS, + version=self.version, + help=_("show program's version number and exit")) + + # add parent arguments and defaults + for parent in parents: + self._add_container_actions(parent) + try: + defaults = parent._defaults + except AttributeError: + pass + else: + self._defaults.update(defaults) + + # ======================= + # Pretty __repr__ methods + # ======================= + def _get_kwargs(self): + names = [ + 'prog', + 'usage', + 'description', + 'version', + 'formatter_class', + 'conflict_handler', + 'add_help', + ] + return [(name, getattr(self, name)) for name in names] + + # ================================== + # Optional/Positional adding methods + # ================================== + def add_subparsers(self, **kwargs): + if self._subparsers is not None: + self.error(_('cannot have multiple subparser arguments')) + + # add the parser class to the arguments if it's not present + kwargs.setdefault('parser_class', type(self)) + + if 'title' in kwargs or 'description' in kwargs: + title = _(kwargs.pop('title', 'subcommands')) + description = _(kwargs.pop('description', None)) + self._subparsers = self.add_argument_group(title, description) + else: + self._subparsers = self._positionals + + # prog defaults to the usage message of this parser, skipping + # optional arguments and with no "usage:" prefix + if kwargs.get('prog') is None: + formatter = self._get_formatter() + positionals = self._get_positional_actions() + groups = self._mutually_exclusive_groups + formatter.add_usage(self.usage, positionals, groups, '') + kwargs['prog'] = formatter.format_help().strip() + + # create the parsers action and add it to the positionals list + parsers_class = self._pop_action_class(kwargs, 'parsers') + action = parsers_class(option_strings=[], **kwargs) + self._subparsers._add_action(action) + + # return the created parsers action + return action + + def _add_action(self, action): + if action.option_strings: + self._optionals._add_action(action) + else: + self._positionals._add_action(action) + return action + + def _get_optional_actions(self): + return [action + for action in self._actions + if action.option_strings] + + def _get_positional_actions(self): + return [action + for action in self._actions + if not action.option_strings] + + # ===================================== + # Command line argument parsing methods + # ===================================== + def parse_args(self, args=None, namespace=None): + args, argv = self.parse_known_args(args, namespace) + if argv: + msg = _('unrecognized arguments: %s') + self.error(msg % ' '.join(argv)) + return args + + def parse_known_args(self, args=None, namespace=None): + # args default to the system args + if args is None: + args = _sys.argv[1:] + + # default Namespace built from parser defaults + if namespace is None: + namespace = Namespace() + + # add any action defaults that aren't present + for action in self._actions: + if action.dest is not SUPPRESS: + if not hasattr(namespace, action.dest): + if action.default is not SUPPRESS: + setattr(namespace, action.dest, action.default) + + # add any parser defaults that aren't present + for dest in self._defaults: + if not hasattr(namespace, dest): + setattr(namespace, dest, self._defaults[dest]) + + # parse the arguments and exit if there are any errors + try: + namespace, args = self._parse_known_args(args, namespace) + if hasattr(namespace, _UNRECOGNIZED_ARGS_ATTR): + args.extend(getattr(namespace, _UNRECOGNIZED_ARGS_ATTR)) + delattr(namespace, _UNRECOGNIZED_ARGS_ATTR) + return namespace, args + except ArgumentError: + err = _sys.exc_info()[1] + self.error(str(err)) + + def _parse_known_args(self, arg_strings, namespace): + # replace arg strings that are file references + if self.fromfile_prefix_chars is not None: + arg_strings = self._read_args_from_files(arg_strings) + + # map all mutually exclusive arguments to the other arguments + # they can't occur with + action_conflicts = {} + for mutex_group in self._mutually_exclusive_groups: + group_actions = mutex_group._group_actions + for i, mutex_action in enumerate(mutex_group._group_actions): + conflicts = action_conflicts.setdefault(mutex_action, []) + conflicts.extend(group_actions[:i]) + conflicts.extend(group_actions[i + 1:]) + + # find all option indices, and determine the arg_string_pattern + # which has an 'O' if there is an option at an index, + # an 'A' if there is an argument, or a '-' if there is a '--' + option_string_indices = {} + arg_string_pattern_parts = [] + arg_strings_iter = iter(arg_strings) + for i, arg_string in enumerate(arg_strings_iter): + + # all args after -- are non-options + if arg_string == '--': + arg_string_pattern_parts.append('-') + for arg_string in arg_strings_iter: + arg_string_pattern_parts.append('A') + + # otherwise, add the arg to the arg strings + # and note the index if it was an option + else: + option_tuple = self._parse_optional(arg_string) + if option_tuple is None: + pattern = 'A' + else: + option_string_indices[i] = option_tuple + pattern = 'O' + arg_string_pattern_parts.append(pattern) + + # join the pieces together to form the pattern + arg_strings_pattern = ''.join(arg_string_pattern_parts) + + # converts arg strings to the appropriate and then takes the action + seen_actions = set() + seen_non_default_actions = set() + + def take_action(action, argument_strings, option_string=None): + seen_actions.add(action) + argument_values = self._get_values(action, argument_strings) + + # error if this argument is not allowed with other previously + # seen arguments, assuming that actions that use the default + # value don't really count as "present" + if argument_values is not action.default: + seen_non_default_actions.add(action) + for conflict_action in action_conflicts.get(action, []): + if conflict_action in seen_non_default_actions: + msg = _('not allowed with argument %s') + action_name = _get_action_name(conflict_action) + raise ArgumentError(action, msg % action_name) + + # take the action if we didn't receive a SUPPRESS value + # (e.g. from a default) + if argument_values is not SUPPRESS: + action(self, namespace, argument_values, option_string) + + # function to convert arg_strings into an optional action + def consume_optional(start_index): + + # get the optional identified at this index + option_tuple = option_string_indices[start_index] + action, option_string, explicit_arg = option_tuple + + # identify additional optionals in the same arg string + # (e.g. -xyz is the same as -x -y -z if no args are required) + match_argument = self._match_argument + action_tuples = [] + while True: + + # if we found no optional action, skip it + if action is None: + extras.append(arg_strings[start_index]) + return start_index + 1 + + # if there is an explicit argument, try to match the + # optional's string arguments to only this + if explicit_arg is not None: + arg_count = match_argument(action, 'A') + + # if the action is a single-dash option and takes no + # arguments, try to parse more single-dash options out + # of the tail of the option string + chars = self.prefix_chars + if arg_count == 0 and option_string[1] not in chars: + action_tuples.append((action, [], option_string)) + char = option_string[0] + option_string = char + explicit_arg[0] + new_explicit_arg = explicit_arg[1:] or None + optionals_map = self._option_string_actions + if option_string in optionals_map: + action = optionals_map[option_string] + explicit_arg = new_explicit_arg + else: + msg = _('ignored explicit argument %r') + raise ArgumentError(action, msg % explicit_arg) + + # if the action expect exactly one argument, we've + # successfully matched the option; exit the loop + elif arg_count == 1: + stop = start_index + 1 + args = [explicit_arg] + action_tuples.append((action, args, option_string)) + break + + # error if a double-dash option did not use the + # explicit argument + else: + msg = _('ignored explicit argument %r') + raise ArgumentError(action, msg % explicit_arg) + + # if there is no explicit argument, try to match the + # optional's string arguments with the following strings + # if successful, exit the loop + else: + start = start_index + 1 + selected_patterns = arg_strings_pattern[start:] + arg_count = match_argument(action, selected_patterns) + stop = start + arg_count + args = arg_strings[start:stop] + action_tuples.append((action, args, option_string)) + break + + # add the Optional to the list and return the index at which + # the Optional's string args stopped + assert action_tuples + for action, args, option_string in action_tuples: + take_action(action, args, option_string) + return stop + + # the list of Positionals left to be parsed; this is modified + # by consume_positionals() + positionals = self._get_positional_actions() + + # function to convert arg_strings into positional actions + def consume_positionals(start_index): + # match as many Positionals as possible + match_partial = self._match_arguments_partial + selected_pattern = arg_strings_pattern[start_index:] + arg_counts = match_partial(positionals, selected_pattern) + + # slice off the appropriate arg strings for each Positional + # and add the Positional and its args to the list + for action, arg_count in zip(positionals, arg_counts): + args = arg_strings[start_index: start_index + arg_count] + start_index += arg_count + take_action(action, args) + + # slice off the Positionals that we just parsed and return the + # index at which the Positionals' string args stopped + positionals[:] = positionals[len(arg_counts):] + return start_index + + # consume Positionals and Optionals alternately, until we have + # passed the last option string + extras = [] + start_index = 0 + if option_string_indices: + max_option_string_index = max(option_string_indices) + else: + max_option_string_index = -1 + while start_index <= max_option_string_index: + + # consume any Positionals preceding the next option + next_option_string_index = min([ + index + for index in option_string_indices + if index >= start_index]) + if start_index != next_option_string_index: + positionals_end_index = consume_positionals(start_index) + + # only try to parse the next optional if we didn't consume + # the option string during the positionals parsing + if positionals_end_index > start_index: + start_index = positionals_end_index + continue + else: + start_index = positionals_end_index + + # if we consumed all the positionals we could and we're not + # at the index of an option string, there were extra arguments + if start_index not in option_string_indices: + strings = arg_strings[start_index:next_option_string_index] + extras.extend(strings) + start_index = next_option_string_index + + # consume the next optional and any arguments for it + start_index = consume_optional(start_index) + + # consume any positionals following the last Optional + stop_index = consume_positionals(start_index) + + # if we didn't consume all the argument strings, there were extras + extras.extend(arg_strings[stop_index:]) + + # if we didn't use all the Positional objects, there were too few + # arg strings supplied. + if positionals: + self.error(_('too few arguments')) + + # make sure all required actions were present, and convert defaults. + for action in self._actions: + if action not in seen_actions: + if action.required: + name = _get_action_name(action) + self.error(_('argument %s is required') % name) + else: + # Convert action default now instead of doing it before + # parsing arguments to avoid calling convert functions + # twice (which may fail) if the argument was given, but + # only if it was defined already in the namespace + if (action.default is not None and + isinstance(action.default, basestring) and + hasattr(namespace, action.dest) and + action.default is getattr(namespace, action.dest)): + setattr(namespace, action.dest, + self._get_value(action, action.default)) + + # make sure all required groups had one option present + for group in self._mutually_exclusive_groups: + if group.required: + for action in group._group_actions: + if action in seen_non_default_actions: + break + + # if no actions were used, report the error + else: + names = [_get_action_name(action) + for action in group._group_actions + if action.help is not SUPPRESS] + msg = _('one of the arguments %s is required') + self.error(msg % ' '.join(names)) + + # return the updated namespace and the extra arguments + return namespace, extras + + def _read_args_from_files(self, arg_strings): + # expand arguments referencing files + new_arg_strings = [] + for arg_string in arg_strings: + + # for regular arguments, just add them back into the list + if arg_string[0] not in self.fromfile_prefix_chars: + new_arg_strings.append(arg_string) + + # replace arguments referencing files with the file content + else: + try: + args_file = open(arg_string[1:]) + try: + arg_strings = [] + for arg_line in args_file.read().splitlines(): + for arg in self.convert_arg_line_to_args(arg_line): + arg_strings.append(arg) + arg_strings = self._read_args_from_files(arg_strings) + new_arg_strings.extend(arg_strings) + finally: + args_file.close() + except IOError: + err = _sys.exc_info()[1] + self.error(str(err)) + + # return the modified argument list + return new_arg_strings + + def convert_arg_line_to_args(self, arg_line): + return [arg_line] + + def _match_argument(self, action, arg_strings_pattern): + # match the pattern for this action to the arg strings + nargs_pattern = self._get_nargs_pattern(action) + match = _re.match(nargs_pattern, arg_strings_pattern) + + # raise an exception if we weren't able to find a match + if match is None: + nargs_errors = { + None: _('expected one argument'), + OPTIONAL: _('expected at most one argument'), + ONE_OR_MORE: _('expected at least one argument'), + } + default = _('expected %s argument(s)') % action.nargs + msg = nargs_errors.get(action.nargs, default) + raise ArgumentError(action, msg) + + # return the number of arguments matched + return len(match.group(1)) + + def _match_arguments_partial(self, actions, arg_strings_pattern): + # progressively shorten the actions list by slicing off the + # final actions until we find a match + result = [] + for i in range(len(actions), 0, -1): + actions_slice = actions[:i] + pattern = ''.join([self._get_nargs_pattern(action) + for action in actions_slice]) + match = _re.match(pattern, arg_strings_pattern) + if match is not None: + result.extend([len(string) for string in match.groups()]) + break + + # return the list of arg string counts + return result + + def _parse_optional(self, arg_string): + # if it's an empty string, it was meant to be a positional + if not arg_string: + return None + + # if it doesn't start with a prefix, it was meant to be positional + if not arg_string[0] in self.prefix_chars: + return None + + # if the option string is present in the parser, return the action + if arg_string in self._option_string_actions: + action = self._option_string_actions[arg_string] + return action, arg_string, None + + # if it's just a single character, it was meant to be positional + if len(arg_string) == 1: + return None + + # if the option string before the "=" is present, return the action + if '=' in arg_string: + option_string, explicit_arg = arg_string.split('=', 1) + if option_string in self._option_string_actions: + action = self._option_string_actions[option_string] + return action, option_string, explicit_arg + + # search through all possible prefixes of the option string + # and all actions in the parser for possible interpretations + option_tuples = self._get_option_tuples(arg_string) + + # if multiple actions match, the option string was ambiguous + if len(option_tuples) > 1: + options = ', '.join([option_string + for action, option_string, explicit_arg in option_tuples]) + tup = arg_string, options + self.error(_('ambiguous option: %s could match %s') % tup) + + # if exactly one action matched, this segmentation is good, + # so return the parsed action + elif len(option_tuples) == 1: + option_tuple, = option_tuples + return option_tuple + + # if it was not found as an option, but it looks like a negative + # number, it was meant to be positional + # unless there are negative-number-like options + if self._negative_number_matcher.match(arg_string): + if not self._has_negative_number_optionals: + return None + + # if it contains a space, it was meant to be a positional + if ' ' in arg_string: + return None + + # it was meant to be an optional but there is no such option + # in this parser (though it might be a valid option in a subparser) + return None, arg_string, None + + def _get_option_tuples(self, option_string): + result = [] + + # option strings starting with two prefix characters are only + # split at the '=' + chars = self.prefix_chars + if option_string[0] in chars and option_string[1] in chars: + if '=' in option_string: + option_prefix, explicit_arg = option_string.split('=', 1) + else: + option_prefix = option_string + explicit_arg = None + for option_string in self._option_string_actions: + if option_string.startswith(option_prefix): + action = self._option_string_actions[option_string] + tup = action, option_string, explicit_arg + result.append(tup) + + # single character options can be concatenated with their arguments + # but multiple character options always have to have their argument + # separate + elif option_string[0] in chars and option_string[1] not in chars: + option_prefix = option_string + explicit_arg = None + short_option_prefix = option_string[:2] + short_explicit_arg = option_string[2:] + + for option_string in self._option_string_actions: + if option_string == short_option_prefix: + action = self._option_string_actions[option_string] + tup = action, option_string, short_explicit_arg + result.append(tup) + elif option_string.startswith(option_prefix): + action = self._option_string_actions[option_string] + tup = action, option_string, explicit_arg + result.append(tup) + + # shouldn't ever get here + else: + self.error(_('unexpected option string: %s') % option_string) + + # return the collected option tuples + return result + + def _get_nargs_pattern(self, action): + # in all examples below, we have to allow for '--' args + # which are represented as '-' in the pattern + nargs = action.nargs + + # the default (None) is assumed to be a single argument + if nargs is None: + nargs_pattern = '(-*A-*)' + + # allow zero or one arguments + elif nargs == OPTIONAL: + nargs_pattern = '(-*A?-*)' + + # allow zero or more arguments + elif nargs == ZERO_OR_MORE: + nargs_pattern = '(-*[A-]*)' + + # allow one or more arguments + elif nargs == ONE_OR_MORE: + nargs_pattern = '(-*A[A-]*)' + + # allow any number of options or arguments + elif nargs == REMAINDER: + nargs_pattern = '([-AO]*)' + + # allow one argument followed by any number of options or arguments + elif nargs == PARSER: + nargs_pattern = '(-*A[-AO]*)' + + # all others should be integers + else: + nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs) + + # if this is an optional action, -- is not allowed + if action.option_strings: + nargs_pattern = nargs_pattern.replace('-*', '') + nargs_pattern = nargs_pattern.replace('-', '') + + # return the pattern + return nargs_pattern + + # ======================== + # Value conversion methods + # ======================== + def _get_values(self, action, arg_strings): + # for everything but PARSER args, strip out '--' + if action.nargs not in [PARSER, REMAINDER]: + arg_strings = [s for s in arg_strings if s != '--'] + + # optional argument produces a default when not present + if not arg_strings and action.nargs == OPTIONAL: + if action.option_strings: + value = action.const + else: + value = action.default + if isinstance(value, basestring): + value = self._get_value(action, value) + self._check_value(action, value) + + # when nargs='*' on a positional, if there were no command-line + # args, use the default if it is anything other than None + elif (not arg_strings and action.nargs == ZERO_OR_MORE and + not action.option_strings): + if action.default is not None: + value = action.default + else: + value = arg_strings + self._check_value(action, value) + + # single argument or optional argument produces a single value + elif len(arg_strings) == 1 and action.nargs in [None, OPTIONAL]: + arg_string, = arg_strings + value = self._get_value(action, arg_string) + self._check_value(action, value) + + # REMAINDER arguments convert all values, checking none + elif action.nargs == REMAINDER: + value = [self._get_value(action, v) for v in arg_strings] + + # PARSER arguments convert all values, but check only the first + elif action.nargs == PARSER: + value = [self._get_value(action, v) for v in arg_strings] + self._check_value(action, value[0]) + + # all other types of nargs produce a list + else: + value = [self._get_value(action, v) for v in arg_strings] + for v in value: + self._check_value(action, v) + + # return the converted value + return value + + def _get_value(self, action, arg_string): + type_func = self._registry_get('type', action.type, action.type) + if not _callable(type_func): + msg = _('%r is not callable') + raise ArgumentError(action, msg % type_func) + + # convert the value to the appropriate type + try: + result = type_func(arg_string) + + # ArgumentTypeErrors indicate errors + except ArgumentTypeError: + name = getattr(action.type, '__name__', repr(action.type)) + msg = str(_sys.exc_info()[1]) + raise ArgumentError(action, msg) + + # TypeErrors or ValueErrors also indicate errors + except (TypeError, ValueError): + name = getattr(action.type, '__name__', repr(action.type)) + msg = _('invalid %s value: %r') + raise ArgumentError(action, msg % (name, arg_string)) + + # return the converted value + return result + + def _check_value(self, action, value): + # converted value must be one of the choices (if specified) + if action.choices is not None and value not in action.choices: + tup = value, ', '.join(map(repr, action.choices)) + msg = _('invalid choice: %r (choose from %s)') % tup + raise ArgumentError(action, msg) + + # ======================= + # Help-formatting methods + # ======================= + def format_usage(self): + formatter = self._get_formatter() + formatter.add_usage(self.usage, self._actions, + self._mutually_exclusive_groups) + return formatter.format_help() + + def format_help(self): + formatter = self._get_formatter() + + # usage + formatter.add_usage(self.usage, self._actions, + self._mutually_exclusive_groups) + + # description + formatter.add_text(self.description) + + # positionals, optionals and user-defined groups + for action_group in self._action_groups: + formatter.start_section(action_group.title) + formatter.add_text(action_group.description) + formatter.add_arguments(action_group._group_actions) + formatter.end_section() + + # epilog + formatter.add_text(self.epilog) + + # determine help from format above + return formatter.format_help() + + def format_version(self): + import warnings + warnings.warn( + 'The format_version method is deprecated -- the "version" ' + 'argument to ArgumentParser is no longer supported.', + DeprecationWarning) + formatter = self._get_formatter() + formatter.add_text(self.version) + return formatter.format_help() + + def _get_formatter(self): + return self.formatter_class(prog=self.prog) + + # ===================== + # Help-printing methods + # ===================== + def print_usage(self, file=None): + if file is None: + file = _sys.stdout + self._print_message(self.format_usage(), file) + + def print_help(self, file=None): + if file is None: + file = _sys.stdout + self._print_message(self.format_help(), file) + + def print_version(self, file=None): + import warnings + warnings.warn( + 'The print_version method is deprecated -- the "version" ' + 'argument to ArgumentParser is no longer supported.', + DeprecationWarning) + self._print_message(self.format_version(), file) + + def _print_message(self, message, file=None): + if message: + if file is None: + file = _sys.stderr + file.write(message) + + # =============== + # Exiting methods + # =============== + def exit(self, status=0, message=None): + if message: + self._print_message(message, _sys.stderr) + _sys.exit(status) + + def error(self, message): + """error(message: string) + + Prints a usage message incorporating the message to stderr and + exits. + + If you override this in a subclass, it should not return -- it + should either exit or raise an exception. + """ + self.print_usage(_sys.stderr) + self.exit(2, _('%s: error: %s\n') % (self.prog, message)) diff --git a/keycodemapdb/tools/keymap-gen b/keycodemapdb/tools/keymap-gen new file mode 100755 index 0000000..d4594b4 --- /dev/null +++ b/keycodemapdb/tools/keymap-gen @@ -0,0 +1,972 @@ +#!/usr/bin/python +# -*- python -*- +# +# Keycode Map Generator +# +# Copyright (C) 2009-2017 Red Hat, Inc. +# +# This file is dual license under the terms of the GPLv2 or later +# and 3-clause BSD licenses. +# + +# Requires >= 2.6 +from __future__ import print_function + +import csv +try: + import argparse +except: + import os, sys + sys.path.append(os.path.join(os.path.dirname(__file__), "../thirdparty")) + import argparse +import hashlib +import time +import sys + +class Database: + + # Linux: linux/input.h + MAP_LINUX = "linux" + + # OS-X: Carbon/HIToolbox/Events.h + MAP_OSX = "osx" + + # AT Set 1: linux/drivers/input/keyboard/atkbd.c + # (atkbd_set2_keycode + atkbd_unxlate_table) + MAP_ATSET1 = "atset1" + + # AT Set 2: linux/drivers/input/keyboard/atkbd.c + # (atkbd_set2_keycode) + MAP_ATSET2 = "atset2" + + # AT Set 3: linux/drivers/input/keyboard/atkbd.c + # (atkbd_set3_keycode) + MAP_ATSET3 = "atset3" + + # Linux RAW: linux/drivers/char/keyboard.c (x86_keycodes) + MAP_XTKBD = "xtkbd" + + # USB HID: linux/drivers/hid/usbhid/usbkbd.c (usb_kbd_keycode) + MAP_USB = "usb" + + # Win32: mingw32/winuser.h + MAP_WIN32 = "win32" + + # XWin XT: xorg-server/hw/xwin/{winkeybd.c,winkeynames.h} + # (xt + manually transcribed) + MAP_XWINXT = "xwinxt" + + # X11: http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h + MAP_X11 = "x11" + + # XKBD XT: xf86-input-keyboard/src/at_scancode.c + # (xt + manually transcribed) + MAP_XKBDXT = "xkbdxt" + + # Xorg with evdev: linux + an offset + MAP_XORGEVDEV = "xorgevdev" + + # Xorg with kbd: xkbdxt + an offset + MAP_XORGKBD = "xorgkbd" + + # Xorg with OS-X: osx + an offset + MAP_XORGXQUARTZ = "xorgxquartz" + + # Xorg + Cygwin: xwinxt + an offset + MAP_XORGXWIN = "xorgxwin" + + # QEMU key numbers: xtkbd + special re-encoding of high bit + MAP_QNUM = "qnum" + + # HTML codes + MAP_HTML = "html" + + # XKB key names + MAP_XKB = "xkb" + + # QEMU keycodes + MAP_QCODE = "qcode" + + # Sun / Sparc scan codes + # Reference: "SPARC International Keyboard Spec 1", page 7 "US scan set" + MAP_SUN = "sun" + + # Apple Desktop Bus + # Reference: http://www.archive.org/stream/apple-guide-macintosh-family-hardware/Apple_Guide_to_the_Macintosh_Family_Hardware_2e#page/n345/mode/2up + MAP_ADB = "adb" + + MAP_LIST = ( + MAP_LINUX, + MAP_OSX, + MAP_ATSET1, + MAP_ATSET2, + MAP_ATSET3, + MAP_USB, + MAP_WIN32, + MAP_XWINXT, + MAP_XKBDXT, + MAP_X11, + MAP_HTML, + MAP_XKB, + MAP_QCODE, + MAP_SUN, + MAP_ADB, + + # These are derived from maps above + MAP_XTKBD, + MAP_XORGEVDEV, + MAP_XORGKBD, + MAP_XORGXQUARTZ, + MAP_XORGXWIN, + MAP_QNUM, + ) + + CODE_COLUMNS = { + MAP_LINUX: 1, + MAP_OSX: 3, + MAP_ATSET1: 4, + MAP_ATSET2: 5, + MAP_ATSET3: 6, + MAP_USB: 7, + MAP_WIN32: 9, + MAP_XWINXT: 10, + MAP_XKBDXT: 11, + MAP_X11: 13, + MAP_HTML: 14, + MAP_XKB: 15, + MAP_SUN: 17, + MAP_ADB: 18, + } + + ENUM_COLUMNS = { + MAP_QCODE: 14, + } + + NAME_COLUMNS = { + MAP_LINUX: 0, + MAP_OSX: 2, + MAP_WIN32: 8, + MAP_X11: 12, + MAP_HTML: 14, + MAP_XKB: 15, + MAP_QCODE: 16, + } + + ENUM_BOUND = { + MAP_QCODE: "Q_KEY_CODE__MAX", + } + + def __init__(self): + + self.mapto = {} + self.mapfrom = {} + self.mapname = {} + self.mapchecksum = None + + for name in self.MAP_LIST: + # Key is a MAP_LINUX, value is a MAP_XXX + self.mapto[name] = {} + # key is a MAP_XXX, value is a MAP_LINUX + self.mapfrom[name] = {} + + for name in self.NAME_COLUMNS.keys(): + # key is a MAP_LINUX, value is a string + self.mapname[name] = {} + + def _generate_checksum(self, filename): + hash = hashlib.sha256() + with open(filename, "rb") as f: + for chunk in iter(lambda: f.read(4096), b""): + hash.update(chunk) + self.mapchecksum = hash.hexdigest() + + def load(self, filename): + self._generate_checksum(filename) + + with open(filename, 'r') as f: + reader = csv.reader(f) + + first = True + + for row in reader: + # Discard column headings + if first: + first = False + continue + + # We special case MAP_LINUX since that is out + # master via which all other mappings are done + linux = self.load_linux(row) + + # Now load all the remaining master data values + self.load_data(row, linux) + + # Then load all the keycode names + self.load_names(row, linux) + + # Finally calculate derived key maps + self.derive_data(row, linux) + + def load_linux(self, row): + col = self.CODE_COLUMNS[self.MAP_LINUX] + linux = row[col] + + if linux.startswith("0x"): + linux = int(linux, 16) + else: + linux = int(linux, 10) + + self.mapto[self.MAP_LINUX][linux] = linux + self.mapfrom[self.MAP_LINUX][linux] = linux + + return linux + + + def load_data(self, row, linux): + for mapname in self.CODE_COLUMNS: + if mapname == self.MAP_LINUX: + continue + + col = self.CODE_COLUMNS[mapname] + val = row[col] + + if val == "": + continue + + if val.startswith("0x"): + val = int(val, 16) + elif val.isdigit(): + val = int(val, 10) + + self.mapto[mapname][linux] = val + self.mapfrom[mapname][val] = linux + + def load_names(self, row, linux): + for mapname in self.NAME_COLUMNS: + col = self.NAME_COLUMNS[mapname] + val = row[col] + + if val == "": + continue + + self.mapname[mapname][linux] = val + + + def derive_data(self, row, linux): + # Linux RAW is XT scan codes with special encoding of the + # 0xe0 scan codes + if linux in self.mapto[self.MAP_ATSET1]: + at1 = self.mapto[self.MAP_ATSET1][linux] + if at1 > 0x7f: + assert((at1 & ~0x7f) == 0xe000) + xtkbd = 0x100 | (at1 & 0x7f) + else: + xtkbd = at1 + self.mapto[self.MAP_XTKBD][linux] = xtkbd + self.mapfrom[self.MAP_XTKBD][xtkbd] = linux + + # Xorg KBD is XKBD XT offset by 8 + if linux in self.mapto[self.MAP_XKBDXT]: + xorgkbd = self.mapto[self.MAP_XKBDXT][linux] + 8 + self.mapto[self.MAP_XORGKBD][linux] = xorgkbd + self.mapfrom[self.MAP_XORGKBD][xorgkbd] = linux + + # Xorg evdev is Linux offset by 8 + self.mapto[self.MAP_XORGEVDEV][linux] = linux + 8 + self.mapfrom[self.MAP_XORGEVDEV][linux + 8] = linux + + # Xorg XQuartx is OS-X offset by 8 + if linux in self.mapto[self.MAP_OSX]: + xorgxquartz = self.mapto[self.MAP_OSX][linux] + 8 + self.mapto[self.MAP_XORGXQUARTZ][linux] = xorgxquartz + self.mapfrom[self.MAP_XORGXQUARTZ][xorgxquartz] = linux + + # Xorg Xwin (aka Cygwin) is XWin XT offset by 8 + if linux in self.mapto[self.MAP_XWINXT]: + xorgxwin = self.mapto[self.MAP_XWINXT][linux] + 8 + self.mapto[self.MAP_XORGXWIN][linux] = xorgxwin + self.mapfrom[self.MAP_XORGXWIN][xorgxwin] = linux + + # QNUM keycodes are XT scan codes with a slightly + # different encoding of 0xe0 scan codes + if linux in self.mapto[self.MAP_ATSET1]: + at1 = self.mapto[self.MAP_ATSET1][linux] + if at1 > 0x7f: + assert((at1 & ~0x7f) == 0xe000) + qnum = 0x80 | (at1 & 0x7f) + else: + qnum = at1 + self.mapto[self.MAP_QNUM][linux] = qnum + self.mapfrom[self.MAP_QNUM][qnum] = linux + + # Hack for compatibility with previous mistakes in handling + # Print/SysRq. The preferred qnum for Print/SysRq is 0x54, + # but QEMU previously allowed 0xb7 too + if qnum == 0x54: + self.mapfrom[self.MAP_QNUM][0xb7] = self.mapfrom[self.MAP_QNUM][0x54] + + if linux in self.mapname[self.MAP_QCODE]: + qcodeenum = self.mapname[self.MAP_QCODE][linux] + qcodeenum = "Q_KEY_CODE_" + qcodeenum.upper() + self.mapto[self.MAP_QCODE][linux] = qcodeenum + self.mapfrom[self.MAP_QCODE][qcodeenum] = linux + +class LanguageGenerator(object): + + def _boilerplate(self, lines): + raise NotImplementedError() + + def generate_header(self, database, args): + today = time.strftime("%Y-%m-%d %H:%M") + self._boilerplate([ + "This file is auto-generated from keymaps.csv on %s" % today, + "Database checksum sha256(%s)" % database.mapchecksum, + "To re-generate, run:", + " %s" % args, + ]) + +class LanguageSrcGenerator(LanguageGenerator): + + TYPE_INT = "integer" + TYPE_STRING = "string" + TYPE_ENUM = "enum" + + def _array_start(self, varname, length, defvalue, fromtype, totype): + raise NotImplementedError() + + def _array_end(self, fromtype, totype): + raise NotImplementedError() + + def _array_entry(self, index, value, comment, fromtype, totype): + raise NotImplementedError() + + def generate_code_map(self, varname, database, frommapname, tomapname): + if frommapname not in database.mapfrom: + raise Exception("Unknown map %s, expected one of %s" % ( + frommapname, ", ".join(database.mapfrom.keys()))) + if tomapname not in database.mapto: + raise Exception("Unknown map %s, expected one of %s" % ( + tomapname, ", ".join(database.mapto.keys()))) + + tolinux = database.mapfrom[frommapname] + fromlinux = database.mapto[tomapname] + + if varname is None: + varname = "code_map_%s_to_%s" % (frommapname, tomapname) + + if frommapname in database.ENUM_COLUMNS: + fromtype = self.TYPE_ENUM + elif type(tolinux.keys()[0]) == str: + fromtype = self.TYPE_STRING + else: + fromtype = self.TYPE_INT + + if tomapname in database.ENUM_COLUMNS: + totype = self.TYPE_ENUM + elif type(fromlinux.values()[0]) == str: + totype = self.TYPE_STRING + else: + totype = self.TYPE_INT + + keys = tolinux.keys() + keys.sort() + if fromtype == self.TYPE_INT: + keys = range(keys[-1] + 1) + + if fromtype == self.TYPE_ENUM: + keymax = database.ENUM_BOUND[frommapname] + else: + keymax = len(keys) + + defvalue = fromlinux.get(0, None) + if fromtype == self.TYPE_ENUM: + self._array_start(varname, keymax, defvalue, fromtype, totype) + else: + self._array_start(varname, keymax, None, fromtype, totype) + + for src in keys: + linux = tolinux.get(src, None) + if linux is None: + dst = None + else: + dst = fromlinux.get(linux, defvalue) + + comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux), + self._label(database, Database.MAP_LINUX, linux, linux), + self._label(database, tomapname, dst, linux)) + self._array_entry(src, dst, comment, fromtype, totype) + self._array_end(fromtype, totype) + + def generate_code_table(self, varname, database, mapname): + if mapname not in database.mapto: + raise Exception("Unknown map %s, expected one of %s" % ( + mapname, ", ".join(database.mapto.keys()))) + + keys = database.mapto[Database.MAP_LINUX].keys() + keys.sort() + names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] + + if varname is None: + varname = "code_table_%s" % mapname + + if mapname in database.ENUM_COLUMNS: + totype = self.TYPE_ENUM + elif type(database.mapto[mapname].values()[0]) == str: + totype = self.TYPE_STRING + else: + totype = self.TYPE_INT + + self._array_start(varname, len(keys), None, self.TYPE_INT, totype) + + defvalue = database.mapto[mapname].get(0, None) + for i in range(len(keys)): + key = keys[i] + dst = database.mapto[mapname].get(key, defvalue) + self._array_entry(i, dst, names[i], self.TYPE_INT, totype) + + self._array_end(self.TYPE_INT, totype) + + def generate_name_map(self, varname, database, frommapname, tomapname): + if frommapname not in database.mapfrom: + raise Exception("Unknown map %s, expected one of %s" % ( + frommapname, ", ".join(database.mapfrom.keys()))) + if tomapname not in database.mapname: + raise Exception("Unknown map %s, expected one of %s" % ( + tomapname, ", ".join(database.mapname.keys()))) + + tolinux = database.mapfrom[frommapname] + fromlinux = database.mapname[tomapname] + + if varname is None: + varname = "name_map_%s_to_%s" % (frommapname, tomapname) + + keys = tolinux.keys() + keys.sort() + if type(keys[0]) == int: + keys = range(keys[-1] + 1) + + if type(keys[0]) == int: + fromtype = self.TYPE_INT + else: + fromtype = self.TYPE_STRING + + self._array_start(varname, len(keys), None, fromtype, self.TYPE_STRING) + + for src in keys: + linux = tolinux.get(src, None) + if linux is None: + dst = None + else: + dst = fromlinux.get(linux, None) + + comment = "%s -> %s -> %s" % (self._label(database, frommapname, src, linux), + self._label(database, Database.MAP_LINUX, linux, linux), + self._label(database, tomapname, dst, linux)) + self._array_entry(src, dst, comment, fromtype, self.TYPE_STRING) + self._array_end(fromtype, self.TYPE_STRING) + + def generate_name_table(self, varname, database, mapname): + if mapname not in database.mapname: + raise Exception("Unknown map %s, expected one of %s" % ( + mapname, ", ".join(database.mapname.keys()))) + + keys = database.mapto[Database.MAP_LINUX].keys() + keys.sort() + names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] + + if varname is None: + varname = "name_table_%s" % mapname + + self._array_start(varname, len(keys), None, self.TYPE_INT, self.TYPE_STRING) + + for i in range(len(keys)): + key = keys[i] + dst = database.mapname[mapname].get(key, None) + self._array_entry(i, dst, names[i], self.TYPE_INT, self.TYPE_STRING) + + self._array_end(self.TYPE_INT, self.TYPE_STRING) + + def _label(self, database, mapname, val, linux): + if mapname in database.mapname: + return "%s:%s (%s)" % (mapname, val, database.mapname[mapname].get(linux, "unnamed")) + else: + return "%s:%s" % (mapname, val) + +class LanguageDocGenerator(LanguageGenerator): + + def _array_start_name_doc(self, varname, namemap): + raise NotImplementedError() + + def _array_start_code_doc(self, varname, namemap, codemap): + raise NotImplementedError() + + def _array_end(self): + raise NotImplementedError() + + def _array_name_entry(self, value, name): + raise NotImplementedError() + + def _array_code_entry(self, value, name): + raise NotImplementedError() + + def generate_name_docs(self, varname, database, mapname): + if mapname not in database.mapname: + raise Exception("Unknown map %s, expected one of %s" % ( + mapname, ", ".join(database.mapname.keys()))) + + keys = database.mapto[Database.MAP_LINUX].keys() + keys.sort() + names = [database.mapname[Database.MAP_LINUX].get(key, "unnamed") for key in keys] + + if varname is None: + varname = mapname + + self._array_start_name_doc(varname, mapname) + + for i in range(len(keys)): + key = keys[i] + dst = database.mapname[mapname].get(key, None) + self._array_name_entry(key, dst) + + self._array_end() + + + def generate_code_docs(self, varname, database, mapname): + if mapname not in database.mapfrom: + raise Exception("Unknown map %s, expected one of %s" % ( + mapname, ", ".join(database.mapfrom.keys()))) + + tolinux = database.mapfrom[mapname] + keys = tolinux.keys() + keys.sort() + if mapname in database.mapname: + names = database.mapname[mapname] + namemap = mapname + else: + names = database.mapname[Database.MAP_LINUX] + namemap = Database.MAP_LINUX + + if varname is None: + varname = mapname + + self._array_start_code_doc(varname, mapname, namemap) + + for i in range(len(keys)): + key = keys[i] + self._array_code_entry(key, names.get(tolinux[key], "unnamed")) + + self._array_end() + +class CLanguageGenerator(LanguageSrcGenerator): + + def __init__(self, inttypename, strtypename, lentypename): + self.inttypename = inttypename + self.strtypename = strtypename + self.lentypename = lentypename + + def _boilerplate(self, lines): + print("/*") + for line in lines: + print(" * %s" % line) + print("*/") + + def _array_start(self, varname, length, defvalue, fromtype, totype): + self._varname = varname; + totypename = self.strtypename if totype == self.TYPE_STRING else self.inttypename + if fromtype in (self.TYPE_INT, self.TYPE_ENUM): + if type(length) == str: + print("const %s %s[%s] = {" % (totypename, varname, length)) + else: + print("const %s %s[%d] = {" % (totypename, varname, length)) + else: + print("const struct _%s {" % varname) + print(" const %s from;" % self.strtypename) + print(" const %s to;" % totypename) + print("} %s[] = {" % varname) + + if defvalue != None: + if totype == self.TYPE_ENUM: + if type(length) == str: + print(" [0 ... %s-1] = %s," % (length, defvalue)) + else: + print(" [0 ... 0x%x-1] = %s," % (length, defvalue)) + else: + if type(length) == str: + print(" [0 ... %s-1] = 0x%x," % (length, defvalue)) + else: + print(" [0 ... 0x%x-1] = 0x%x," % (length, defvalue)) + + def _array_end(self, fromtype, totype): + print("};") + print("const %s %s_len = sizeof(%s)/sizeof(%s[0]);" % + (self.lentypename, self._varname, self._varname, self._varname)) + + def _array_entry(self, index, value, comment, fromtype, totype): + if value is None: + return + if fromtype == self.TYPE_INT: + indexfmt = "0x%x" + elif fromtype == self.TYPE_ENUM: + indexfmt = "%s" + else: + indexfmt = "\"%s\"" + + if totype == self.TYPE_INT: + valuefmt = "0x%x" + elif totype == self.TYPE_ENUM: + valuefmt = "%s" + else: + valuefmt = "\"%s\"" + + if fromtype != self.TYPE_STRING: + print((" [" + indexfmt + "] = " + valuefmt + ", /* %s */") % (index, value, comment)) + else: + print((" {" + indexfmt + ", " + valuefmt + "}, /* %s */") % (index, value, comment)) + +class CppLanguageGenerator(CLanguageGenerator): + + def _array_start(self, varname, length, defvalue, fromtype, totype): + if fromtype == self.TYPE_ENUM: + raise NotImplementedError("Enums not supported as source in C++ generator") + totypename = "const " + self.strtypename if totype == self.TYPE_STRING else self.inttypename + if fromtype == self.TYPE_INT: + print("#include ") + print("const std::vector<%s> %s = {" % (totypename, varname)) + else: + print("#include ") + print("#include ") + print("const std::map %s = {" % (totypename, varname)) + + def _array_end(self, fromtype, totype): + print("};") + + # designated initializers not available in C++ + def _array_entry(self, index, value, comment, fromtype, totype): + if fromtype == self.TYPE_STRING: + return super(CppLanguageGenerator, self)._array_entry(index, value, comment, fromtype, totype) + + if value is None: + print(" 0, /* %s */" % comment) + elif totype == self.TYPE_INT: + print(" 0x%x, /* %s */" % (value, comment)) + elif totype == self.TYPE_ENUM: + print(" %s, /* %s */" % (value, comment)) + else: + print(" \"%s\", /* %s */" % (value, comment)) + +class StdCLanguageGenerator(CLanguageGenerator): + + def __init__(self): + super(StdCLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") + +class StdCppLanguageGenerator(CppLanguageGenerator): + + def __init__(self): + super(StdCppLanguageGenerator, self).__init__("unsigned short", "char *", "unsigned int") + +class GLib2LanguageGenerator(CLanguageGenerator): + + def __init__(self): + super(GLib2LanguageGenerator, self).__init__("guint16", "gchar *", "guint") + +class PythonLanguageGenerator(LanguageSrcGenerator): + + def _boilerplate(self, lines): + print("#") + for line in lines: + print("# %s" % line) + print("#") + + def _array_start(self, varname, length, defvalue, fromtype, totype): + if fromtype == self.TYPE_ENUM: + raise NotImplementedError("Enums not supported as source in Python generator") + + if fromtype != self.TYPE_STRING: + print("%s = [" % varname) + else: + print("%s = {" % varname) + + def _array_end(self, fromtype, totype): + if fromtype != self.TYPE_STRING: + print("]") + else: + print("}") + + def _array_entry(self, index, value, comment, fromtype, totype): + if fromtype == self.TYPE_INT: + if value is None: + print(" None, # %s" % (comment)) + elif totype == self.TYPE_INT: + print(" 0x%x, # %s" % (value, comment)) + elif totype == self.TYPE_ENUM: + print(" %s, # %s" % (value, comment)) + else: + print(" \"%s\", # %s" % (value, comment)) + else: + if value is None: + print(" \"%s\": None, # %s" % (index, comment)) + elif totype == self.TYPE_INT: + print(" \"%s\": 0x%x, # %s" % (index, value, comment)) + elif totype == self.TYPE_ENUM: + print(" \"%s\": %s, # %s" % (index, value, comment)) + else: + print(" \"%s\": \"%s\", # %s" % (index, value, comment)) + +class PerlLanguageGenerator(LanguageSrcGenerator): + + def _boilerplate(self, lines): + print("#") + for line in lines: + print("# %s" % line) + print("#") + + def _array_start(self, varname, length, defvalue, fromtype, totype): + if fromtype == self.TYPE_ENUN: + raise NotImplementedError("Enums not supported as source in Python generator") + if fromtype == self.TYPE_INT: + print("my @%s = (" % varname) + else: + print("my %%%s = (" % varname) + + def _array_end(self, fromtype, totype): + print(");") + + def _array_entry(self, index, value, comment, fromtype, totype): + if fromtype == self.TYPE_INT: + if value is None: + print(" undef, # %s" % (comment)) + elif totype == self.TYPE_INT: + print(" 0x%x, # %s" % (value, comment)) + elif totype == self.TYPE_ENUM: + print(" %s, # %s" % (value, comment)) + else: + print(" \"%s\", # %s" % (value, comment)) + else: + if value is None: + print(" \"%s\", undef, # %s" % (index, comment)) + elif totype == self.TYPE_INT: + print(" \"%s\", 0x%x, # %s" % (index, value, comment)) + elif totype == self.TYPE_ENUM: + print(" \"%s\", 0x%x, # %s" % (index, value, comment)) + else: + print(" \"%s\", \"%s\", # %s" % (index, value, comment)) + +class JavaScriptLanguageGenerator(LanguageSrcGenerator): + + def _boilerplate(self, lines): + print("/*") + for line in lines: + print(" * %s" % line) + print("*/") + + def _array_start(self, varname, length, defvalue, fromtype, totype): + print("export default {") + + def _array_end(self, fromtype, totype): + print("};") + + def _array_entry(self, index, value, comment, fromtype, totype): + if value is None: + return + + if fromtype == self.TYPE_INT: + fromfmt = "0x%x" + elif fromtype == self.TYPE_ENUM: + fromfmt = "%s" + else: + fromfmt = "\"%s\"" + + if totype == self.TYPE_INT: + tofmt = "0x%x" + elif totype == self.TYPE_ENUM: + tofmt = "%s" + else: + tofmt = "\"%s\"" + + print((" " + fromfmt + ": " + tofmt + ", /* %s */") % (index, value, comment)) + +class PodLanguageGenerator(LanguageDocGenerator): + + def _boilerplate(self, lines): + print("#") + for line in lines: + print("# %s" % line) + print("#") + + def _array_start_name_doc(self, varname, namemap): + print("=head1 %s" % varname) + print("") + print("List of %s key code names, with corresponding key code values" % namemap) + print("") + print("=over 4") + print("") + + def _array_start_code_doc(self, varname, codemap, namemap): + print("=head1 %s" % varname) + print("") + print("List of %s key code values, with corresponding %s key code names" % (codemap, namemap)) + print("") + print("=over 4") + print("") + + def _array_end(self): + print("=back") + print("") + + def _array_name_entry(self, value, name): + print("=item %s" % name) + print("") + print("Key value %d (0x%x)" % (value, value)) + print("") + + def _array_code_entry(self, value, name): + print("=item %d (0x%x)" % (value, value)) + print("") + print("Key name %s" % name) + print("") + +SRC_GENERATORS = { + "stdc": StdCLanguageGenerator(), + "stdc++": StdCppLanguageGenerator(), + "glib2": GLib2LanguageGenerator(), + "python2": PythonLanguageGenerator(), + "python3": PythonLanguageGenerator(), + "perl": PerlLanguageGenerator(), + "js": JavaScriptLanguageGenerator(), +} +DOC_GENERATORS = { + "pod": PodLanguageGenerator(), +} + +def code_map(args): + database = Database() + database.load(args.keymaps) + + cliargs = ["keymap-gen", "--lang=%s" % args.lang] + if args.varname is not None: + cliargs.append("--varname=%s" % args.varname) + cliargs.extend(["code-map", "keymaps.csv", args.frommapname, args.tomapname]) + SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) + + SRC_GENERATORS[args.lang].generate_code_map(args.varname, database, args.frommapname, args.tomapname) + +def code_table(args): + database = Database() + database.load(args.keymaps) + + cliargs = ["keymap-gen", "--lang=%s" % args.lang] + if args.varname is not None: + cliargs.append("--varname=%s" % args.varname) + cliargs.extend(["code-table", "keymaps.csv", args.mapname]) + SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) + + SRC_GENERATORS[args.lang].generate_code_table(args.varname, database, args.mapname) + +def name_map(args): + database = Database() + database.load(args.keymaps) + + cliargs = ["keymap-gen", "--lang=%s" % args.lang] + if args.varname is not None: + cliargs.append("--varname=%s" % args.varname) + cliargs.extend(["name-map", "keymaps.csv", args.frommapname, args.tomapname]) + SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) + + SRC_GENERATORS[args.lang].generate_name_map(args.varname, database, args.frommapname, args.tomapname) + +def name_table(args): + database = Database() + database.load(args.keymaps) + + + cliargs = ["keymap-gen", "--lang=%s" % args.lang] + if args.varname is not None: + cliargs.append("--varname=%s" % args.varname) + cliargs.extend(["name-table", "keymaps.csv", args.mapname]) + SRC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) + + SRC_GENERATORS[args.lang].generate_name_table(args.varname, database, args.mapname) + +def code_docs(args): + database = Database() + database.load(args.keymaps) + + + cliargs = ["keymap-gen", "--lang=%s" % args.lang] + if args.varname is not None: + cliargs.append("--varname=%s" % args.varname) + cliargs.extend(["code-docs", "keymaps.csv", args.mapname]) + DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) + + DOC_GENERATORS[args.lang].generate_code_docs(args.varname, database, args.mapname) + +def name_docs(args): + database = Database() + database.load(args.keymaps) + + + cliargs = ["keymap-gen", "--lang=%s" % args.lang] + if args.varname is not None: + cliargs.append("--varname=%s" % args.varname) + cliargs.extend(["name-docs", "keymaps.csv", args.mapname]) + DOC_GENERATORS[args.lang].generate_header(database, " ".join(cliargs)) + + DOC_GENERATORS[args.lang].generate_name_docs(args.varname, database, args.mapname) + +def usage(): + print ("Please select a command:") + print (" 'code-map', 'code-table', 'name-map', 'name-table', 'docs'") + sys.exit(1) + +def main(): + parser = argparse.ArgumentParser() + + parser.add_argument("--lang", default="stdc", + help="Output language, (src=%s, doc=%s)" % ( + ",".join(SRC_GENERATORS.keys()), + ",".join(DOC_GENERATORS.keys()))) + parser.add_argument("--varname", default=None, + help="Data variable name") + + subparsers = parser.add_subparsers(help="sub-command help") + + codemapparser = subparsers.add_parser("code-map", help="Generate a mapping between code tables") + codemapparser.add_argument("keymaps", help="Path to keymap CSV data file") + codemapparser.add_argument("frommapname", help="Source code table name") + codemapparser.add_argument("tomapname", help="Target code table name") + codemapparser.set_defaults(func=code_map) + + codetableparser = subparsers.add_parser("code-table", help="Generate a flat code table") + codetableparser.add_argument("keymaps", help="Path to keymap CSV data file") + codetableparser.add_argument("mapname", help="Code table name") + codetableparser.set_defaults(func=code_table) + + namemapparser = subparsers.add_parser("name-map", help="Generate a mapping to names") + namemapparser.add_argument("keymaps", help="Path to keymap CSV data file") + namemapparser.add_argument("frommapname", help="Source code table name") + namemapparser.add_argument("tomapname", help="Target name table name") + namemapparser.set_defaults(func=name_map) + + nametableparser = subparsers.add_parser("name-table", help="Generate a flat name table") + nametableparser.add_argument("keymaps", help="Path to keymap CSV data file") + nametableparser.add_argument("mapname", help="Name table name") + nametableparser.set_defaults(func=name_table) + + codedocsparser = subparsers.add_parser("code-docs", help="Generate code documentation") + codedocsparser.add_argument("keymaps", help="Path to keymap CSV data file") + codedocsparser.add_argument("mapname", help="Code table name") + codedocsparser.set_defaults(func=code_docs) + + namedocsparser = subparsers.add_parser("name-docs", help="Generate name documentation") + namedocsparser.add_argument("keymaps", help="Path to keymap CSV data file") + namedocsparser.add_argument("mapname", help="Name table name") + namedocsparser.set_defaults(func=name_docs) + + args = parser.parse_args() + if hasattr(args, "func"): + args.func(args) + else: + usage() + + +main() diff --git a/qemu b/qemu index 4cd4265..7c1beb5 160000 --- a/qemu +++ b/qemu @@ -1 +1 @@ -Subproject commit 4cd42653f5c1df326a2678a84f24a78fb9601277 +Subproject commit 7c1beb52ed86191d9e965444d934adaa2531710f -- 2.39.2