Merge remote-tracking branch 'remotes/borntraeger/tags/s390x-20170718' into staging
s390: add z14 cpu model
- add a CPU model for the IBM z14 which was announced on July 17th 2017
- update linux headers to 4.13-rc0 to get a fix for an ioctl definition
# gpg: Signature made Tue 18 Jul 2017 09:56:24 BST
# gpg: using RSA key 0x117BBC80B5A61C7C
# gpg: Good signature from "Christian Borntraeger (IBM) <borntraeger@de.ibm.com>"
# Primary key fingerprint: F922 9381 A334 08F9 DBAB FBCA 117B BC80 B5A6 1C7C
* remotes/borntraeger/tags/s390x-20170718:
s390x/cpumodel: z14 cpu models
linux header sync against v4.13-rc1
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
diff --git a/MAINTAINERS b/MAINTAINERS
index b9aa878..5ea273f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -375,7 +375,7 @@
F: include/hw/*/allwinner*
F: hw/arm/cubieboard.c
-ARM PrimeCell
+ARM PrimeCell and CMSDK devices
M: Peter Maydell <peter.maydell@linaro.org>
L: qemu-arm@nongnu.org
S: Maintained
@@ -389,6 +389,10 @@
F: hw/sd/pl181.c
F: hw/timer/pl031.c
F: include/hw/arm/primecell.h
+F: hw/timer/cmsdk-apb-timer.c
+F: include/hw/timer/cmsdk-apb-timer.h
+F: hw/char/cmsdk-apb-uart.c
+F: include/hw/char/cmsdk-apb-uart.h
ARM cores
M: Peter Maydell <peter.maydell@linaro.org>
@@ -450,6 +454,14 @@
F: hw/arm/integratorcp.c
F: hw/misc/arm_integrator_debug.c
+MPS2
+M: Peter Maydell <peter.maydell@linaro.org>
+L: qemu-arm@nongnu.org
+S: Maintained
+F: hw/arm/mps2.c
+F: hw/misc/mps2-scc.c
+F: include/hw/misc/mps2-scc.h
+
Musicpal
M: Jan Kiszka <jan.kiszka@web.de>
L: qemu-arm@nongnu.org
diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c
index 3581618..d84b01d 100644
--- a/accel/tcg/cpu-exec.c
+++ b/accel/tcg/cpu-exec.c
@@ -280,6 +280,7 @@
CPUArchState *env;
tb_page_addr_t phys_page1;
uint32_t flags;
+ uint32_t trace_vcpu_dstate;
};
static bool tb_cmp(const void *p, const void *d)
@@ -291,6 +292,7 @@
tb->page_addr[0] == desc->phys_page1 &&
tb->cs_base == desc->cs_base &&
tb->flags == desc->flags &&
+ tb->trace_vcpu_dstate == desc->trace_vcpu_dstate &&
!atomic_read(&tb->invalid)) {
/* check next page if needed */
if (tb->page_addr[1] == -1) {
@@ -319,10 +321,11 @@
desc.env = (CPUArchState *)cpu->env_ptr;
desc.cs_base = cs_base;
desc.flags = flags;
+ desc.trace_vcpu_dstate = *cpu->trace_dstate;
desc.pc = pc;
phys_pc = get_page_addr_code(desc.env, pc);
desc.phys_page1 = phys_pc & TARGET_PAGE_MASK;
- h = tb_hash_func(phys_pc, pc, flags);
+ h = tb_hash_func(phys_pc, pc, flags, *cpu->trace_dstate);
return qht_lookup(&tcg_ctx.tb_ctx.htable, tb_cmp, &desc, h);
}
@@ -342,7 +345,8 @@
cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags);
tb = atomic_rcu_read(&cpu->tb_jmp_cache[tb_jmp_cache_hash_func(pc)]);
if (unlikely(!tb || tb->pc != pc || tb->cs_base != cs_base ||
- tb->flags != flags)) {
+ tb->flags != flags ||
+ tb->trace_vcpu_dstate != *cpu->trace_dstate)) {
tb = tb_htable_lookup(cpu, pc, cs_base, flags);
if (!tb) {
diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c
index 4e1831c..090ebad 100644
--- a/accel/tcg/translate-all.c
+++ b/accel/tcg/translate-all.c
@@ -54,6 +54,7 @@
#include "exec/tb-hash.h"
#include "translate-all.h"
#include "qemu/bitmap.h"
+#include "qemu/error-report.h"
#include "qemu/timer.h"
#include "qemu/main-loop.h"
#include "exec/log.h"
@@ -112,6 +113,11 @@
#define V_L2_BITS 10
#define V_L2_SIZE (1 << V_L2_BITS)
+/* Make sure all possible CPU event bits fit in tb->trace_vcpu_dstate */
+QEMU_BUILD_BUG_ON(CPU_TRACE_DSTATE_MAX_EVENTS >
+ sizeof(((TranslationBlock *)0)->trace_vcpu_dstate)
+ * BITS_PER_BYTE);
+
/*
* L1 Mapping properties
*/
@@ -1071,7 +1077,7 @@
/* remove the TB from the hash list */
phys_pc = tb->page_addr[0] + (tb->pc & ~TARGET_PAGE_MASK);
- h = tb_hash_func(phys_pc, tb->pc, tb->flags);
+ h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->trace_vcpu_dstate);
qht_remove(&tcg_ctx.tb_ctx.htable, tb, h);
/* remove the TB from the page list */
@@ -1216,7 +1222,7 @@
}
/* add in the hash table */
- h = tb_hash_func(phys_pc, tb->pc, tb->flags);
+ h = tb_hash_func(phys_pc, tb->pc, tb->flags, tb->trace_vcpu_dstate);
qht_insert(&tcg_ctx.tb_ctx.htable, tb, h);
#ifdef DEBUG_TB_CHECK
@@ -1262,6 +1268,7 @@
tb->cs_base = cs_base;
tb->flags = flags;
tb->cflags = cflags;
+ tb->trace_vcpu_dstate = *cpu->trace_dstate;
tb->invalid = false;
#ifdef CONFIG_PROFILER
diff --git a/block/io.c b/block/io.c
index aece54c..d9dc822 100644
--- a/block/io.c
+++ b/block/io.c
@@ -2359,7 +2359,6 @@
assert(max_pdiscard >= bs->bl.request_alignment);
while (bytes > 0) {
- int ret;
int num = bytes;
if (head) {
diff --git a/configure b/configure
index a3f0522..e8798ce 100755
--- a/configure
+++ b/configure
@@ -4915,6 +4915,21 @@
fi
##########################################
+# check for utmpx.h, it is missing e.g. on OpenBSD
+
+have_utmpx=no
+cat > $TMPC << EOF
+#include <utmpx.h>
+struct utmpx user_info;
+int main(void) {
+ return 0;
+}
+EOF
+if compile_prog "" "" ; then
+ have_utmpx=yes
+fi
+
+##########################################
# End of CC checks
# After here, no more $cc or $ld runs
@@ -5959,6 +5974,10 @@
echo "CONFIG_STATIC_ASSERT=y" >> $config_host_mak
fi
+if test "$have_utmpx" = "yes" ; then
+ echo "HAVE_UTMPX=y" >> $config_host_mak
+fi
+
# Hold two types of flag:
# CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on
# a thread we have a handle to
diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 93e995d..bbdd3c1 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -83,6 +83,7 @@
CONFIG_TUSB6010=y
CONFIG_IMX=y
CONFIG_MAINSTONE=y
+CONFIG_MPS2=y
CONFIG_NSERIES=y
CONFIG_RASPI=y
CONFIG_REALVIEW=y
@@ -95,6 +96,11 @@
CONFIG_STM32F2XX_SPI=y
CONFIG_STM32F205_SOC=y
+CONFIG_CMSDK_APB_TIMER=y
+CONFIG_CMSDK_APB_UART=y
+
+CONFIG_MPS2_SCC=y
+
CONFIG_VERSATILE_PCI=y
CONFIG_VERSATILE_I2C=y
diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt
index c4941de..f6a624f 100644
--- a/docs/colo-proxy.txt
+++ b/docs/colo-proxy.txt
@@ -182,6 +182,32 @@
-chardev socket,id=red1,host=3.3.3.3,port=9004
-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0
-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1
+-object filter-rewriter,id=f3,netdev=hn0,queue=all
+
+If you want to use virtio-net-pci or other driver with vnet_header:
+
+Primary(ip:3.3.3.3):
+-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,downscript=/etc/qemu-ifdown
+-device e1000,id=e0,netdev=hn0,mac=52:a4:00:12:78:66
+-chardev socket,id=mirror0,host=3.3.3.3,port=9003,server,nowait
+-chardev socket,id=compare1,host=3.3.3.3,port=9004,server,nowait
+-chardev socket,id=compare0,host=3.3.3.3,port=9001,server,nowait
+-chardev socket,id=compare0-0,host=3.3.3.3,port=9001
+-chardev socket,id=compare_out,host=3.3.3.3,port=9005,server,nowait
+-chardev socket,id=compare_out0,host=3.3.3.3,port=9005
+-object filter-mirror,id=m0,netdev=hn0,queue=tx,outdev=mirror0,vnet_hdr_support
+-object filter-redirector,netdev=hn0,id=redire0,queue=rx,indev=compare_out,vnet_hdr_support
+-object filter-redirector,netdev=hn0,id=redire1,queue=rx,outdev=compare0,vnet_hdr_support
+-object colo-compare,id=comp0,primary_in=compare0-0,secondary_in=compare1,outdev=compare_out0,vnet_hdr_support
+
+Secondary(ip:3.3.3.8):
+-netdev tap,id=hn0,vhost=off,script=/etc/qemu-ifup,down script=/etc/qemu-ifdown
+-device e1000,netdev=hn0,mac=52:a4:00:12:78:66
+-chardev socket,id=red0,host=3.3.3.3,port=9003
+-chardev socket,id=red1,host=3.3.3.3,port=9004
+-object filter-redirector,id=f1,netdev=hn0,queue=tx,indev=red0,vnet_hdr_support
+-object filter-redirector,id=f2,netdev=hn0,queue=rx,outdev=red1,vnet_hdr_support
+-object filter-rewriter,id=f3,netdev=hn0,queue=all,vnet_hdr_support
Note:
a.COLO-proxy must work with COLO-frame and Block-replication.
diff --git a/docs/devel/bitmaps.md b/docs/devel/bitmaps.md
deleted file mode 100644
index a2e8d51..0000000
--- a/docs/devel/bitmaps.md
+++ /dev/null
@@ -1,505 +0,0 @@
-<!--
-Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
-All rights reserved.
-
-This file is licensed via The FreeBSD Documentation License, the full text of
-which is included at the end of this document.
--->
-
-# Dirty Bitmaps and Incremental Backup
-
-* Dirty Bitmaps are objects that track which data needs to be backed up for the
- next incremental backup.
-
-* Dirty bitmaps can be created at any time and attached to any node
- (not just complete drives.)
-
-## Dirty Bitmap Names
-
-* A dirty bitmap's name is unique to the node, but bitmaps attached to different
- nodes can share the same name.
-
-* Dirty bitmaps created for internal use by QEMU may be anonymous and have no
- name, but any user-created bitmaps may not be. There can be any number of
- anonymous bitmaps per node.
-
-* The name of a user-created bitmap must not be empty ("").
-
-## Bitmap Modes
-
-* A Bitmap can be "frozen," which means that it is currently in-use by a backup
- operation and cannot be deleted, renamed, written to, reset,
- etc.
-
-* The normal operating mode for a bitmap is "active."
-
-## Basic QMP Usage
-
-### Supported Commands ###
-
-* block-dirty-bitmap-add
-* block-dirty-bitmap-remove
-* block-dirty-bitmap-clear
-
-### Creation
-
-* To create a new bitmap, enabled, on the drive with id=drive0:
-
-```json
-{ "execute": "block-dirty-bitmap-add",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
-}
-```
-
-* This bitmap will have a default granularity that matches the cluster size of
- its associated drive, if available, clamped to between [4KiB, 64KiB].
- The current default for qcow2 is 64KiB.
-
-* To create a new bitmap that tracks changes in 32KiB segments:
-
-```json
-{ "execute": "block-dirty-bitmap-add",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0",
- "granularity": 32768
- }
-}
-```
-
-### Deletion
-
-* Bitmaps that are frozen cannot be deleted.
-
-* Deleting the bitmap does not impact any other bitmaps attached to the same
- node, nor does it affect any backups already created from this node.
-
-* Because bitmaps are only unique to the node to which they are attached,
- you must specify the node/drive name here, too.
-
-```json
-{ "execute": "block-dirty-bitmap-remove",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
-}
-```
-
-### Resetting
-
-* Resetting a bitmap will clear all information it holds.
-
-* An incremental backup created from an empty bitmap will copy no data,
- as if nothing has changed.
-
-```json
-{ "execute": "block-dirty-bitmap-clear",
- "arguments": {
- "node": "drive0",
- "name": "bitmap0"
- }
-}
-```
-
-## Transactions
-
-### Justification
-
-Bitmaps can be safely modified when the VM is paused or halted by using
-the basic QMP commands. For instance, you might perform the following actions:
-
-1. Boot the VM in a paused state.
-2. Create a full drive backup of drive0.
-3. Create a new bitmap attached to drive0.
-4. Resume execution of the VM.
-5. Incremental backups are ready to be created.
-
-At this point, the bitmap and drive backup would be correctly in sync,
-and incremental backups made from this point forward would be correctly aligned
-to the full drive backup.
-
-This is not particularly useful if we decide we want to start incremental
-backups after the VM has been running for a while, for which we will need to
-perform actions such as the following:
-
-1. Boot the VM and begin execution.
-2. Using a single transaction, perform the following operations:
- * Create bitmap0.
- * Create a full drive backup of drive0.
-3. Incremental backups are now ready to be created.
-
-### Supported Bitmap Transactions
-
-* block-dirty-bitmap-add
-* block-dirty-bitmap-clear
-
-The usages are identical to their respective QMP commands, but see below
-for examples.
-
-### Example: New Incremental Backup
-
-As outlined in the justification, perhaps we want to create a new incremental
-backup chain attached to a drive.
-
-```json
-{ "execute": "transaction",
- "arguments": {
- "actions": [
- {"type": "block-dirty-bitmap-add",
- "data": {"node": "drive0", "name": "bitmap0"} },
- {"type": "drive-backup",
- "data": {"device": "drive0", "target": "/path/to/full_backup.img",
- "sync": "full", "format": "qcow2"} }
- ]
- }
-}
-```
-
-### Example: New Incremental Backup Anchor Point
-
-Maybe we just want to create a new full backup with an existing bitmap and
-want to reset the bitmap to track the new chain.
-
-```json
-{ "execute": "transaction",
- "arguments": {
- "actions": [
- {"type": "block-dirty-bitmap-clear",
- "data": {"node": "drive0", "name": "bitmap0"} },
- {"type": "drive-backup",
- "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
- "sync": "full", "format": "qcow2"} }
- ]
- }
-}
-```
-
-## Incremental Backups
-
-The star of the show.
-
-**Nota Bene!** Only incremental backups of entire drives are supported for now.
-So despite the fact that you can attach a bitmap to any arbitrary node, they are
-only currently useful when attached to the root node. This is because
-drive-backup only supports drives/devices instead of arbitrary nodes.
-
-### Example: First Incremental Backup
-
-1. Create a full backup and sync it to the dirty bitmap, as in the transactional
-examples above; or with the VM offline, manually create a full copy and then
-create a new bitmap before the VM begins execution.
-
- * Let's assume the full backup is named 'full_backup.img'.
- * Let's assume the bitmap you created is 'bitmap0' attached to 'drive0'.
-
-2. Create a destination image for the incremental backup that utilizes the
-full backup as a backing image.
-
- * Let's assume it is named 'incremental.0.img'.
-
- ```sh
- # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
- ```
-
-3. Issue the incremental backup command:
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.0.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-### Example: Second Incremental Backup
-
-1. Create a new destination image for the incremental backup that points to the
- previous one, e.g.: 'incremental.1.img'
-
- ```sh
- # qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
- ```
-
-2. Issue a new incremental backup command. The only difference here is that we
- have changed the target image below.
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.1.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-## Errors
-
-* In the event of an error that occurs after a backup job is successfully
- launched, either by a direct QMP command or a QMP transaction, the user
- will receive a BLOCK_JOB_COMPLETE event with a failure message, accompanied
- by a BLOCK_JOB_ERROR event.
-
-* In the case of an event being cancelled, the user will receive a
- BLOCK_JOB_CANCELLED event instead of a pair of COMPLETE and ERROR events.
-
-* In either case, the incremental backup data contained within the bitmap is
- safely rolled back, and the data within the bitmap is not lost. The image
- file created for the failed attempt can be safely deleted.
-
-* Once the underlying problem is fixed (e.g. more storage space is freed up),
- you can simply retry the incremental backup command with the same bitmap.
-
-### Example
-
-1. Create a target image:
-
- ```sh
- # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
- ```
-
-2. Attempt to create an incremental backup via QMP:
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.0.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-3. Receive an event notifying us of failure:
-
- ```json
- { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
- "data": { "speed": 0, "offset": 0, "len": 67108864,
- "error": "No space left on device",
- "device": "drive1", "type": "backup" },
- "event": "BLOCK_JOB_COMPLETED" }
- ```
-
-4. Delete the failed incremental, and re-create the image.
-
- ```sh
- # rm incremental.0.img
- # qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
- ```
-
-5. Retry the command after fixing the underlying problem,
- such as freeing up space on the backup volume:
-
- ```json
- { "execute": "drive-backup",
- "arguments": {
- "device": "drive0",
- "bitmap": "bitmap0",
- "target": "incremental.0.img",
- "format": "qcow2",
- "sync": "incremental",
- "mode": "existing"
- }
- }
- ```
-
-6. Receive confirmation that the job completed successfully:
-
- ```json
- { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
- "data": { "device": "drive1", "type": "backup",
- "speed": 0, "len": 67108864, "offset": 67108864},
- "event": "BLOCK_JOB_COMPLETED" }
- ```
-
-### Partial Transactional Failures
-
-* Sometimes, a transaction will succeed in launching and return success,
- but then later the backup jobs themselves may fail. It is possible that
- a management application may have to deal with a partial backup failure
- after a successful transaction.
-
-* If multiple backup jobs are specified in a single transaction, when one of
- them fails, it will not interact with the other backup jobs in any way.
-
-* The job(s) that succeeded will clear the dirty bitmap associated with the
- operation, but the job(s) that failed will not. It is not "safe" to delete
- any incremental backups that were created successfully in this scenario,
- even though others failed.
-
-#### Example
-
-* QMP example highlighting two backup jobs:
-
- ```json
- { "execute": "transaction",
- "arguments": {
- "actions": [
- { "type": "drive-backup",
- "data": { "device": "drive0", "bitmap": "bitmap0",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d0-incr-1.qcow2" } },
- { "type": "drive-backup",
- "data": { "device": "drive1", "bitmap": "bitmap1",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d1-incr-1.qcow2" } },
- ]
- }
- }
- ```
-
-* QMP example response, highlighting one success and one failure:
- * Acknowledgement that the Transaction was accepted and jobs were launched:
- ```json
- { "return": {} }
- ```
-
- * Later, QEMU sends notice that the first job was completed:
- ```json
- { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
- "data": { "device": "drive0", "type": "backup",
- "speed": 0, "len": 67108864, "offset": 67108864 },
- "event": "BLOCK_JOB_COMPLETED"
- }
- ```
-
- * Later yet, QEMU sends notice that the second job has failed:
- ```json
- { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
- "data": { "device": "drive1", "action": "report",
- "operation": "read" },
- "event": "BLOCK_JOB_ERROR" }
- ```
-
- ```json
- { "timestamp": { "seconds": 1447192399, "microseconds": 685853 },
- "data": { "speed": 0, "offset": 0, "len": 67108864,
- "error": "Input/output error",
- "device": "drive1", "type": "backup" },
- "event": "BLOCK_JOB_COMPLETED" }
-
-* In the above example, "d0-incr-1.qcow2" is valid and must be kept,
- but "d1-incr-1.qcow2" is invalid and should be deleted. If a VM-wide
- incremental backup of all drives at a point-in-time is to be made,
- new backups for both drives will need to be made, taking into account
- that a new incremental backup for drive0 needs to be based on top of
- "d0-incr-1.qcow2."
-
-### Grouped Completion Mode
-
-* While jobs launched by transactions normally complete or fail on their own,
- it is possible to instruct them to complete or fail together as a group.
-
-* QMP transactions take an optional properties structure that can affect
- the semantics of the transaction.
-
-* The "completion-mode" transaction property can be either "individual"
- which is the default, legacy behavior described above, or "grouped,"
- a new behavior detailed below.
-
-* Delayed Completion: In grouped completion mode, no jobs will report
- success until all jobs are ready to report success.
-
-* Grouped failure: If any job fails in grouped completion mode, all remaining
- jobs will be cancelled. Any incremental backups will restore their dirty
- bitmap objects as if no backup command was ever issued.
-
- * Regardless of if QEMU reports a particular incremental backup job as
- CANCELLED or as an ERROR, the in-memory bitmap will be restored.
-
-#### Example
-
-* Here's the same example scenario from above with the new property:
-
- ```json
- { "execute": "transaction",
- "arguments": {
- "actions": [
- { "type": "drive-backup",
- "data": { "device": "drive0", "bitmap": "bitmap0",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d0-incr-1.qcow2" } },
- { "type": "drive-backup",
- "data": { "device": "drive1", "bitmap": "bitmap1",
- "format": "qcow2", "mode": "existing",
- "sync": "incremental", "target": "d1-incr-1.qcow2" } },
- ],
- "properties": {
- "completion-mode": "grouped"
- }
- }
- }
- ```
-
-* QMP example response, highlighting a failure for drive2:
- * Acknowledgement that the Transaction was accepted and jobs were launched:
- ```json
- { "return": {} }
- ```
-
- * Later, QEMU sends notice that the second job has errored out,
- but that the first job was also cancelled:
- ```json
- { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
- "data": { "device": "drive1", "action": "report",
- "operation": "read" },
- "event": "BLOCK_JOB_ERROR" }
- ```
-
- ```json
- { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
- "data": { "speed": 0, "offset": 0, "len": 67108864,
- "error": "Input/output error",
- "device": "drive1", "type": "backup" },
- "event": "BLOCK_JOB_COMPLETED" }
- ```
-
- ```json
- { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
- "data": { "device": "drive0", "type": "backup", "speed": 0,
- "len": 67108864, "offset": 16777216 },
- "event": "BLOCK_JOB_CANCELLED" }
- ```
-
-<!--
-The FreeBSD Documentation License
-
-Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
-PDF, PostScript, RTF and so forth) with or without modification, are permitted
-provided that the following conditions are met:
-
-Redistributions of source code (Markdown) must retain the above copyright
-notice, this list of conditions and the following disclaimer of this file
-unmodified.
-
-Redistributions in compiled form (transformed to other DTDs, converted to PDF,
-PostScript, RTF and other formats) 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.
-
-THIS DOCUMENTATION 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 HOLDER 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 DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--->
diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt
index 8c0029b..5768a0b 100644
--- a/docs/devel/tracing.txt
+++ b/docs/devel/tracing.txt
@@ -14,8 +14,7 @@
2. Create a file with the events you want to trace:
- echo bdrv_aio_readv > /tmp/events
- echo bdrv_aio_writev >> /tmp/events
+ echo memory_region_ops_read >/tmp/events
3. Run the virtual machine to produce a trace file:
diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst
new file mode 100644
index 0000000..7bcfe7f
--- /dev/null
+++ b/docs/interop/bitmaps.rst
@@ -0,0 +1,555 @@
+..
+ Copyright 2015 John Snow <jsnow@redhat.com> and Red Hat, Inc.
+ All rights reserved.
+
+ This file is licensed via The FreeBSD Documentation License, the full
+ text of which is included at the end of this document.
+
+====================================
+Dirty Bitmaps and Incremental Backup
+====================================
+
+- Dirty Bitmaps are objects that track which data needs to be backed up
+ for the next incremental backup.
+
+- Dirty bitmaps can be created at any time and attached to any node
+ (not just complete drives).
+
+.. contents::
+
+Dirty Bitmap Names
+------------------
+
+- A dirty bitmap's name is unique to the node, but bitmaps attached to
+ different nodes can share the same name.
+
+- Dirty bitmaps created for internal use by QEMU may be anonymous and
+ have no name, but any user-created bitmaps must have a name. There
+ can be any number of anonymous bitmaps per node.
+
+- The name of a user-created bitmap must not be empty ("").
+
+Bitmap Modes
+------------
+
+- A bitmap can be "frozen," which means that it is currently in-use by
+ a backup operation and cannot be deleted, renamed, written to, reset,
+ etc.
+
+- The normal operating mode for a bitmap is "active."
+
+Basic QMP Usage
+---------------
+
+Supported Commands
+~~~~~~~~~~~~~~~~~~
+
+- ``block-dirty-bitmap-add``
+- ``block-dirty-bitmap-remove``
+- ``block-dirty-bitmap-clear``
+
+Creation
+~~~~~~~~
+
+- To create a new bitmap, enabled, on the drive with id=drive0:
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-add",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0"
+ }
+ }
+
+- This bitmap will have a default granularity that matches the cluster
+ size of its associated drive, if available, clamped to between [4KiB,
+ 64KiB]. The current default for qcow2 is 64KiB.
+
+- To create a new bitmap that tracks changes in 32KiB segments:
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-add",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0",
+ "granularity": 32768
+ }
+ }
+
+Deletion
+~~~~~~~~
+
+- Bitmaps that are frozen cannot be deleted.
+
+- Deleting the bitmap does not impact any other bitmaps attached to the
+ same node, nor does it affect any backups already created from this
+ node.
+
+- Because bitmaps are only unique to the node to which they are
+ attached, you must specify the node/drive name here, too.
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-remove",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0"
+ }
+ }
+
+Resetting
+~~~~~~~~~
+
+- Resetting a bitmap will clear all information it holds.
+
+- An incremental backup created from an empty bitmap will copy no data,
+ as if nothing has changed.
+
+.. code:: json
+
+ { "execute": "block-dirty-bitmap-clear",
+ "arguments": {
+ "node": "drive0",
+ "name": "bitmap0"
+ }
+ }
+
+Transactions
+------------
+
+Justification
+~~~~~~~~~~~~~
+
+Bitmaps can be safely modified when the VM is paused or halted by using
+the basic QMP commands. For instance, you might perform the following
+actions:
+
+1. Boot the VM in a paused state.
+2. Create a full drive backup of drive0.
+3. Create a new bitmap attached to drive0.
+4. Resume execution of the VM.
+5. Incremental backups are ready to be created.
+
+At this point, the bitmap and drive backup would be correctly in sync,
+and incremental backups made from this point forward would be correctly
+aligned to the full drive backup.
+
+This is not particularly useful if we decide we want to start
+incremental backups after the VM has been running for a while, for which
+we will need to perform actions such as the following:
+
+1. Boot the VM and begin execution.
+2. Using a single transaction, perform the following operations:
+
+ - Create ``bitmap0``.
+ - Create a full drive backup of ``drive0``.
+
+3. Incremental backups are now ready to be created.
+
+Supported Bitmap Transactions
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- ``block-dirty-bitmap-add``
+- ``block-dirty-bitmap-clear``
+
+The usages are identical to their respective QMP commands, but see below
+for examples.
+
+Example: New Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As outlined in the justification, perhaps we want to create a new
+incremental backup chain attached to a drive.
+
+.. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ {"type": "block-dirty-bitmap-add",
+ "data": {"node": "drive0", "name": "bitmap0"} },
+ {"type": "drive-backup",
+ "data": {"device": "drive0", "target": "/path/to/full_backup.img",
+ "sync": "full", "format": "qcow2"} }
+ ]
+ }
+ }
+
+Example: New Incremental Backup Anchor Point
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Maybe we just want to create a new full backup with an existing bitmap
+and want to reset the bitmap to track the new chain.
+
+.. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ {"type": "block-dirty-bitmap-clear",
+ "data": {"node": "drive0", "name": "bitmap0"} },
+ {"type": "drive-backup",
+ "data": {"device": "drive0", "target": "/path/to/new_full_backup.img",
+ "sync": "full", "format": "qcow2"} }
+ ]
+ }
+ }
+
+Incremental Backups
+-------------------
+
+The star of the show.
+
+**Nota Bene!** Only incremental backups of entire drives are supported
+for now. So despite the fact that you can attach a bitmap to any
+arbitrary node, they are only currently useful when attached to the root
+node. This is because drive-backup only supports drives/devices instead
+of arbitrary nodes.
+
+Example: First Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Create a full backup and sync it to the dirty bitmap, as in the
+ transactional examples above; or with the VM offline, manually create
+ a full copy and then create a new bitmap before the VM begins
+ execution.
+
+ - Let's assume the full backup is named ``full_backup.img``.
+ - Let's assume the bitmap you created is ``bitmap0`` attached to
+ ``drive0``.
+
+2. Create a destination image for the incremental backup that utilizes
+ the full backup as a backing image.
+
+ - Let's assume the new incremental image is named
+ ``incremental.0.img``.
+
+ .. code:: bash
+
+ $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+3. Issue the incremental backup command:
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.0.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+Example: Second Incremental Backup
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+1. Create a new destination image for the incremental backup that points
+ to the previous one, e.g.: ``incremental.1.img``
+
+ .. code:: bash
+
+ $ qemu-img create -f qcow2 incremental.1.img -b incremental.0.img -F qcow2
+
+2. Issue a new incremental backup command. The only difference here is
+ that we have changed the target image below.
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.1.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+Errors
+------
+
+- In the event of an error that occurs after a backup job is
+ successfully launched, either by a direct QMP command or a QMP
+ transaction, the user will receive a ``BLOCK_JOB_COMPLETE`` event with
+ a failure message, accompanied by a ``BLOCK_JOB_ERROR`` event.
+
+- In the case of an event being cancelled, the user will receive a
+ ``BLOCK_JOB_CANCELLED`` event instead of a pair of COMPLETE and ERROR
+ events.
+
+- In either case, the incremental backup data contained within the
+ bitmap is safely rolled back, and the data within the bitmap is not
+ lost. The image file created for the failed attempt can be safely
+ deleted.
+
+- Once the underlying problem is fixed (e.g. more storage space is
+ freed up), you can simply retry the incremental backup command with
+ the same bitmap.
+
+Example
+~~~~~~~
+
+1. Create a target image:
+
+ .. code:: bash
+
+ $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+2. Attempt to create an incremental backup via QMP:
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.0.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+3. Receive an event notifying us of failure:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1424709442, "microseconds": 844524 },
+ "data": { "speed": 0, "offset": 0, "len": 67108864,
+ "error": "No space left on device",
+ "device": "drive1", "type": "backup" },
+ "event": "BLOCK_JOB_COMPLETED" }
+
+4. Delete the failed incremental, and re-create the image.
+
+ .. code:: bash
+
+ $ rm incremental.0.img
+ $ qemu-img create -f qcow2 incremental.0.img -b full_backup.img -F qcow2
+
+5. Retry the command after fixing the underlying problem, such as
+ freeing up space on the backup volume:
+
+ .. code:: json
+
+ { "execute": "drive-backup",
+ "arguments": {
+ "device": "drive0",
+ "bitmap": "bitmap0",
+ "target": "incremental.0.img",
+ "format": "qcow2",
+ "sync": "incremental",
+ "mode": "existing"
+ }
+ }
+
+6. Receive confirmation that the job completed successfully:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1424709668, "microseconds": 526525 },
+ "data": { "device": "drive1", "type": "backup",
+ "speed": 0, "len": 67108864, "offset": 67108864},
+ "event": "BLOCK_JOB_COMPLETED" }
+
+Partial Transactional Failures
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Sometimes, a transaction will succeed in launching and return
+ success, but then later the backup jobs themselves may fail. It is
+ possible that a management application may have to deal with a
+ partial backup failure after a successful transaction.
+
+- If multiple backup jobs are specified in a single transaction, when
+ one of them fails, it will not interact with the other backup jobs in
+ any way.
+
+- The job(s) that succeeded will clear the dirty bitmap associated with
+ the operation, but the job(s) that failed will not. It is not "safe"
+ to delete any incremental backups that were created successfully in
+ this scenario, even though others failed.
+
+Example
+^^^^^^^
+
+- QMP example highlighting two backup jobs:
+
+ .. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ { "type": "drive-backup",
+ "data": { "device": "drive0", "bitmap": "bitmap0",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d0-incr-1.qcow2" } },
+ { "type": "drive-backup",
+ "data": { "device": "drive1", "bitmap": "bitmap1",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d1-incr-1.qcow2" } },
+ ]
+ }
+ }
+
+- QMP example response, highlighting one success and one failure:
+
+ - Acknowledgement that the Transaction was accepted and jobs were
+ launched:
+
+ .. code:: json
+
+ { "return": {} }
+
+ - Later, QEMU sends notice that the first job was completed:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447192343, "microseconds": 615698 },
+ "data": { "device": "drive0", "type": "backup",
+ "speed": 0, "len": 67108864, "offset": 67108864 },
+ "event": "BLOCK_JOB_COMPLETED"
+ }
+
+ - Later yet, QEMU sends notice that the second job has failed:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447192399, "microseconds": 683015 },
+ "data": { "device": "drive1", "action": "report",
+ "operation": "read" },
+ "event": "BLOCK_JOB_ERROR" }
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447192399, "microseconds":
+ 685853 }, "data": { "speed": 0, "offset": 0, "len": 67108864,
+ "error": "Input/output error", "device": "drive1", "type":
+ "backup" }, "event": "BLOCK_JOB_COMPLETED" }
+
+- In the above example, ``d0-incr-1.qcow2`` is valid and must be kept,
+ but ``d1-incr-1.qcow2`` is invalid and should be deleted. If a VM-wide
+ incremental backup of all drives at a point-in-time is to be made,
+ new backups for both drives will need to be made, taking into account
+ that a new incremental backup for drive0 needs to be based on top of
+ ``d0-incr-1.qcow2``.
+
+Grouped Completion Mode
+~~~~~~~~~~~~~~~~~~~~~~~
+
+- While jobs launched by transactions normally complete or fail on
+ their own, it is possible to instruct them to complete or fail
+ together as a group.
+
+- QMP transactions take an optional properties structure that can
+ affect the semantics of the transaction.
+
+- The "completion-mode" transaction property can be either "individual"
+ which is the default, legacy behavior described above, or "grouped,"
+ a new behavior detailed below.
+
+- Delayed Completion: In grouped completion mode, no jobs will report
+ success until all jobs are ready to report success.
+
+- Grouped failure: If any job fails in grouped completion mode, all
+ remaining jobs will be cancelled. Any incremental backups will
+ restore their dirty bitmap objects as if no backup command was ever
+ issued.
+
+ - Regardless of if QEMU reports a particular incremental backup job
+ as CANCELLED or as an ERROR, the in-memory bitmap will be
+ restored.
+
+Example
+^^^^^^^
+
+- Here's the same example scenario from above with the new property:
+
+ .. code:: json
+
+ { "execute": "transaction",
+ "arguments": {
+ "actions": [
+ { "type": "drive-backup",
+ "data": { "device": "drive0", "bitmap": "bitmap0",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d0-incr-1.qcow2" } },
+ { "type": "drive-backup",
+ "data": { "device": "drive1", "bitmap": "bitmap1",
+ "format": "qcow2", "mode": "existing",
+ "sync": "incremental", "target": "d1-incr-1.qcow2" } },
+ ],
+ "properties": {
+ "completion-mode": "grouped"
+ }
+ }
+ }
+
+- QMP example response, highlighting a failure for ``drive2``:
+
+ - Acknowledgement that the Transaction was accepted and jobs were
+ launched:
+
+ .. code:: json
+
+ { "return": {} }
+
+ - Later, QEMU sends notice that the second job has errored out, but
+ that the first job was also cancelled:
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447193702, "microseconds": 632377 },
+ "data": { "device": "drive1", "action": "report",
+ "operation": "read" },
+ "event": "BLOCK_JOB_ERROR" }
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447193702, "microseconds": 640074 },
+ "data": { "speed": 0, "offset": 0, "len": 67108864,
+ "error": "Input/output error",
+ "device": "drive1", "type": "backup" },
+ "event": "BLOCK_JOB_COMPLETED" }
+
+ .. code:: json
+
+ { "timestamp": { "seconds": 1447193702, "microseconds": 640163 },
+ "data": { "device": "drive0", "type": "backup", "speed": 0,
+ "len": 67108864, "offset": 16777216 },
+ "event": "BLOCK_JOB_CANCELLED" }
+
+.. raw:: html
+
+ <!--
+ The FreeBSD Documentation License
+
+ Redistribution and use in source (Markdown) and 'compiled' forms (SGML, HTML,
+ PDF, PostScript, RTF and so forth) with or without modification, are permitted
+ provided that the following conditions are met:
+
+ Redistributions of source code (Markdown) must retain the above copyright
+ notice, this list of conditions and the following disclaimer of this file
+ unmodified.
+
+ Redistributions in compiled form (transformed to other DTDs, converted to PDF,
+ PostScript, RTF and other formats) 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.
+
+ THIS DOCUMENTATION 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 HOLDER 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 DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ -->
diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst
new file mode 100644
index 0000000..5f01797
--- /dev/null
+++ b/docs/interop/live-block-operations.rst
@@ -0,0 +1,1088 @@
+..
+ Copyright (C) 2017 Red Hat Inc.
+
+ This work is licensed under the terms of the GNU GPL, version 2 or
+ later. See the COPYING file in the top-level directory.
+
+============================
+Live Block Device Operations
+============================
+
+QEMU Block Layer currently (as of QEMU 2.9) supports four major kinds of
+live block device jobs -- stream, commit, mirror, and backup. These can
+be used to manipulate disk image chains to accomplish certain tasks,
+namely: live copy data from backing files into overlays; shorten long
+disk image chains by merging data from overlays into backing files; live
+synchronize data from a disk image chain (including current active disk)
+to another target image; and point-in-time (and incremental) backups of
+a block device. Below is a description of the said block (QMP)
+primitives, and some (non-exhaustive list of) examples to illustrate
+their use.
+
+.. note::
+ The file ``qapi/block-core.json`` in the QEMU source tree has the
+ canonical QEMU API (QAPI) schema documentation for the QMP
+ primitives discussed here.
+
+.. todo (kashyapc):: Remove the ".. contents::" directive when Sphinx is
+ integrated.
+
+.. contents::
+
+Disk image backing chain notation
+---------------------------------
+
+A simple disk image chain. (This can be created live using QMP
+``blockdev-snapshot-sync``, or offline via ``qemu-img``)::
+
+ (Live QEMU)
+ |
+ .
+ V
+
+ [A] <----- [B]
+
+ (backing file) (overlay)
+
+The arrow can be read as: Image [A] is the backing file of disk image
+[B]. And live QEMU is currently writing to image [B], consequently, it
+is also referred to as the "active layer".
+
+There are two kinds of terminology that are common when referring to
+files in a disk image backing chain:
+
+(1) Directional: 'base' and 'top'. Given the simple disk image chain
+ above, image [A] can be referred to as 'base', and image [B] as
+ 'top'. (This terminology can be seen in in QAPI schema file,
+ block-core.json.)
+
+(2) Relational: 'backing file' and 'overlay'. Again, taking the same
+ simple disk image chain from the above, disk image [A] is referred
+ to as the backing file, and image [B] as overlay.
+
+ Throughout this document, we will use the relational terminology.
+
+.. important::
+ The overlay files can generally be any format that supports a
+ backing file, although QCOW2 is the preferred format and the one
+ used in this document.
+
+
+Brief overview of live block QMP primitives
+-------------------------------------------
+
+The following are the four different kinds of live block operations that
+QEMU block layer supports.
+
+(1) ``block-stream``: Live copy of data from backing files into overlay
+ files.
+
+ .. note:: Once the 'stream' operation has finished, three things to
+ note:
+
+ (a) QEMU rewrites the backing chain to remove
+ reference to the now-streamed and redundant backing
+ file;
+
+ (b) the streamed file *itself* won't be removed by QEMU,
+ and must be explicitly discarded by the user;
+
+ (c) the streamed file remains valid -- i.e. further
+ overlays can be created based on it. Refer the
+ ``block-stream`` section further below for more
+ details.
+
+(2) ``block-commit``: Live merge of data from overlay files into backing
+ files (with the optional goal of removing the overlay file from the
+ chain). Since QEMU 2.0, this includes "active ``block-commit``"
+ (i.e. merge the current active layer into the base image).
+
+ .. note:: Once the 'commit' operation has finished, there are three
+ things to note here as well:
+
+ (a) QEMU rewrites the backing chain to remove reference
+ to now-redundant overlay images that have been
+ committed into a backing file;
+
+ (b) the committed file *itself* won't be removed by QEMU
+ -- it ought to be manually removed;
+
+ (c) however, unlike in the case of ``block-stream``, the
+ intermediate images will be rendered invalid -- i.e.
+ no more further overlays can be created based on
+ them. Refer the ``block-commit`` section further
+ below for more details.
+
+(3) ``drive-mirror`` (and ``blockdev-mirror``): Synchronize a running
+ disk to another image.
+
+(4) ``drive-backup`` (and ``blockdev-backup``): Point-in-time (live) copy
+ of a block device to a destination.
+
+
+.. _`Interacting with a QEMU instance`:
+
+Interacting with a QEMU instance
+--------------------------------
+
+To show some example invocations of command-line, we will use the
+following invocation of QEMU, with a QMP server running over UNIX
+socket::
+
+ $ ./x86_64-softmmu/qemu-system-x86_64 -display none -nodefconfig \
+ -M q35 -nodefaults -m 512 \
+ -blockdev node-name=node-A,driver=qcow2,file.driver=file,file.node-name=file,file.filename=./a.qcow2 \
+ -device virtio-blk,drive=node-A,id=virtio0 \
+ -monitor stdio -qmp unix:/tmp/qmp-sock,server,nowait
+
+The ``-blockdev`` command-line option, used above, is available from
+QEMU 2.9 onwards. In the above invocation, notice the ``node-name``
+parameter that is used to refer to the disk image a.qcow2 ('node-A') --
+this is a cleaner way to refer to a disk image (as opposed to referring
+to it by spelling out file paths). So, we will continue to designate a
+``node-name`` to each further disk image created (either via
+``blockdev-snapshot-sync``, or ``blockdev-add``) as part of the disk
+image chain, and continue to refer to the disks using their
+``node-name`` (where possible, because ``block-commit`` does not yet, as
+of QEMU 2.9, accept ``node-name`` parameter) when performing various
+block operations.
+
+To interact with the QEMU instance launched above, we will use the
+``qmp-shell`` utility (located at: ``qemu/scripts/qmp``, as part of the
+QEMU source directory), which takes key-value pairs for QMP commands.
+Invoke it as below (which will also print out the complete raw JSON
+syntax for reference -- examples in the following sections)::
+
+ $ ./qmp-shell -v -p /tmp/qmp-sock
+ (QEMU)
+
+.. note::
+ In the event we have to repeat a certain QMP command, we will: for
+ the first occurrence of it, show the ``qmp-shell`` invocation, *and*
+ the corresponding raw JSON QMP syntax; but for subsequent
+ invocations, present just the ``qmp-shell`` syntax, and omit the
+ equivalent JSON output.
+
+
+Example disk image chain
+------------------------
+
+We will use the below disk image chain (and occasionally spelling it
+out where appropriate) when discussing various primitives::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+Where [A] is the original base image; [B] and [C] are intermediate
+overlay images; image [D] is the active layer -- i.e. live QEMU is
+writing to it. (The rule of thumb is: live QEMU will always be pointing
+to the rightmost image in a disk image chain.)
+
+The above image chain can be created by invoking
+``blockdev-snapshot-sync`` commands as following (which shows the
+creation of overlay image [B]) using the ``qmp-shell`` (our invocation
+also prints the raw JSON invocation of it)::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-A snapshot-file=b.qcow2 snapshot-node-name=node-B format=qcow2
+ {
+ "execute": "blockdev-snapshot-sync",
+ "arguments": {
+ "node-name": "node-A",
+ "snapshot-file": "b.qcow2",
+ "format": "qcow2",
+ "snapshot-node-name": "node-B"
+ }
+ }
+
+Here, "node-A" is the name QEMU internally uses to refer to the base
+image [A] -- it is the backing file, based on which the overlay image,
+[B], is created.
+
+To create the rest of the overlay images, [C], and [D] (omitting the raw
+JSON output for brevity)::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-B snapshot-file=c.qcow2 snapshot-node-name=node-C format=qcow2
+ (QEMU) blockdev-snapshot-sync node-name=node-C snapshot-file=d.qcow2 snapshot-node-name=node-D format=qcow2
+
+
+A note on points-in-time vs file names
+--------------------------------------
+
+In our disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+We have *three* points in time and an active layer:
+
+- Point 1: Guest state when [B] was created is contained in file [A]
+- Point 2: Guest state when [C] was created is contained in [A] + [B]
+- Point 3: Guest state when [D] was created is contained in
+ [A] + [B] + [C]
+- Active layer: Current guest state is contained in [A] + [B] + [C] +
+ [D]
+
+Therefore, be aware with naming choices:
+
+- Naming a file after the time it is created is misleading -- the
+ guest data for that point in time is *not* contained in that file
+ (as explained earlier)
+- Rather, think of files as a *delta* from the backing file
+
+
+Live block streaming --- ``block-stream``
+-----------------------------------------
+
+The ``block-stream`` command allows you to do live copy data from backing
+files into overlay images.
+
+Given our original example disk image chain from earlier::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+The disk image chain can be shortened in one of the following different
+ways (not an exhaustive list).
+
+.. _`Case-1`:
+
+(1) Merge everything into the active layer: I.e. copy all contents from
+ the base image, [A], and overlay images, [B] and [C], into [D],
+ *while* the guest is running. The resulting chain will be a
+ standalone image, [D] -- with contents from [A], [B] and [C] merged
+ into it (where live QEMU writes go to)::
+
+ [D]
+
+.. _`Case-2`:
+
+(2) Taking the same example disk image chain mentioned earlier, merge
+ only images [B] and [C] into [D], the active layer. The result will
+ be contents of images [B] and [C] will be copied into [D], and the
+ backing file pointer of image [D] will be adjusted to point to image
+ [A]. The resulting chain will be::
+
+ [A] <-- [D]
+
+.. _`Case-3`:
+
+(3) Intermediate streaming (available since QEMU 2.8): Starting afresh
+ with the original example disk image chain, with a total of four
+ images, it is possible to copy contents from image [B] into image
+ [C]. Once the copy is finished, image [B] can now be (optionally)
+ discarded; and the backing file pointer of image [C] will be
+ adjusted to point to [A]. I.e. after performing "intermediate
+ streaming" of [B] into [C], the resulting image chain will be (where
+ live QEMU is writing to [D])::
+
+ [A] <-- [C] <-- [D]
+
+
+QMP invocation for ``block-stream``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For `Case-1`_, to merge contents of all the backing files into the
+active layer, where 'node-D' is the current active image (by default
+``block-stream`` will flatten the entire chain); ``qmp-shell`` (and its
+corresponding JSON output)::
+
+ (QEMU) block-stream device=node-D job-id=job0
+ {
+ "execute": "block-stream",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0"
+ }
+ }
+
+For `Case-2`_, merge contents of the images [B] and [C] into [D], where
+image [D] ends up referring to image [A] as its backing file::
+
+ (QEMU) block-stream device=node-D base-node=node-A job-id=job0
+
+And for `Case-3`_, of "intermediate" streaming", merge contents of
+images [B] into [C], where [C] ends up referring to [A] as its backing
+image::
+
+ (QEMU) block-stream device=node-C base-node=node-A job-id=job0
+
+Progress of a ``block-stream`` operation can be monitored via the QMP
+command::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+
+
+Once the ``block-stream`` operation has completed, QEMU will emit an
+event, ``BLOCK_JOB_COMPLETED``. The intermediate overlays remain valid,
+and can now be (optionally) discarded, or retained to create further
+overlays based on them. Finally, the ``block-stream`` jobs can be
+restarted at anytime.
+
+
+Live block commit --- ``block-commit``
+--------------------------------------
+
+The ``block-commit`` command lets you merge live data from overlay
+images into backing file(s). Since QEMU 2.0, this includes "live active
+commit" (i.e. it is possible to merge the "active layer", the right-most
+image in a disk image chain where live QEMU will be writing to, into the
+base image). This is analogous to ``block-stream``, but in the opposite
+direction.
+
+Again, starting afresh with our example disk image chain, where live
+QEMU is writing to the right-most image in the chain, [D]::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+The disk image chain can be shortened in one of the following ways:
+
+.. _`block-commit_Case-1`:
+
+(1) Commit content from only image [B] into image [A]. The resulting
+ chain is the following, where image [C] is adjusted to point at [A]
+ as its new backing file::
+
+ [A] <-- [C] <-- [D]
+
+(2) Commit content from images [B] and [C] into image [A]. The
+ resulting chain, where image [D] is adjusted to point to image [A]
+ as its new backing file::
+
+ [A] <-- [D]
+
+.. _`block-commit_Case-3`:
+
+(3) Commit content from images [B], [C], and the active layer [D] into
+ image [A]. The resulting chain (in this case, a consolidated single
+ image)::
+
+ [A]
+
+(4) Commit content from image only image [C] into image [B]. The
+ resulting chain::
+
+ [A] <-- [B] <-- [D]
+
+(5) Commit content from image [C] and the active layer [D] into image
+ [B]. The resulting chain::
+
+ [A] <-- [B]
+
+
+QMP invocation for ``block-commit``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For :ref:`Case-1 <block-commit_Case-1>`, to merge contents only from
+image [B] into image [A], the invocation is as follows::
+
+ (QEMU) block-commit device=node-D base=a.qcow2 top=b.qcow2 job-id=job0
+ {
+ "execute": "block-commit",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "top": "b.qcow2",
+ "base": "a.qcow2"
+ }
+ }
+
+Once the above ``block-commit`` operation has completed, a
+``BLOCK_JOB_COMPLETED`` event will be issued, and no further action is
+required. As the end result, the backing file of image [C] is adjusted
+to point to image [A], and the original 4-image chain will end up being
+transformed to::
+
+ [A] <-- [C] <-- [D]
+
+.. note::
+ The intermediate image [B] is invalid (as in: no more further
+ overlays based on it can be created).
+
+ Reasoning: An intermediate image after a 'stream' operation still
+ represents that old point-in-time, and may be valid in that context.
+ However, an intermediate image after a 'commit' operation no longer
+ represents any point-in-time, and is invalid in any context.
+
+
+However, :ref:`Case-3 <block-commit_Case-3>` (also called: "active
+``block-commit``") is a *two-phase* operation: In the first phase, the
+content from the active overlay, along with the intermediate overlays,
+is copied into the backing file (also called the base image). In the
+second phase, adjust the said backing file as the current active image
+-- possible via issuing the command ``block-job-complete``. Optionally,
+the ``block-commit`` operation can be cancelled by issuing the command
+``block-job-cancel``, but be careful when doing this.
+
+Once the ``block-commit`` operation has completed, the event
+``BLOCK_JOB_READY`` will be emitted, signalling that the synchronization
+has finished. Now the job can be gracefully completed by issuing the
+command ``block-job-complete`` -- until such a command is issued, the
+'commit' operation remains active.
+
+The following is the flow for :ref:`Case-3 <block-commit_Case-3>` to
+convert a disk image chain such as this::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+Into::
+
+ [A]
+
+Where content from all the subsequent overlays, [B], and [C], including
+the active layer, [D], is committed back to [A] -- which is where live
+QEMU is performing all its current writes).
+
+Start the "active ``block-commit``" operation::
+
+ (QEMU) block-commit device=node-D base=a.qcow2 top=d.qcow2 job-id=job0
+ {
+ "execute": "block-commit",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "top": "d.qcow2",
+ "base": "a.qcow2"
+ }
+ }
+
+
+Once the synchronization has completed, the event ``BLOCK_JOB_READY`` will
+be emitted.
+
+Then, optionally query for the status of the active block operations.
+We can see the 'commit' job is now ready to be completed, as indicated
+by the line *"ready": true*::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+ {
+ "return": [
+ {
+ "busy": false,
+ "type": "commit",
+ "len": 1376256,
+ "paused": false,
+ "ready": true,
+ "io-status": "ok",
+ "offset": 1376256,
+ "device": "job0",
+ "speed": 0
+ }
+ ]
+ }
+
+Gracefully complete the 'commit' block device job::
+
+ (QEMU) block-job-complete device=job0
+ {
+ "execute": "block-job-complete",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+ {
+ "return": {}
+ }
+
+Finally, once the above job is completed, an event
+``BLOCK_JOB_COMPLETED`` will be emitted.
+
+.. note::
+ The invocation for rest of the cases (2, 4, and 5), discussed in the
+ previous section, is omitted for brevity.
+
+
+Live disk synchronization --- ``drive-mirror`` and ``blockdev-mirror``
+----------------------------------------------------------------------
+
+Synchronize a running disk image chain (all or part of it) to a target
+image.
+
+Again, given our familiar disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+The ``drive-mirror`` (and its newer equivalent ``blockdev-mirror``) allows
+you to copy data from the entire chain into a single target image (which
+can be located on a different host).
+
+Once a 'mirror' job has started, there are two possible actions while a
+``drive-mirror`` job is active:
+
+(1) Issuing the command ``block-job-cancel`` after it emits the event
+ ``BLOCK_JOB_CANCELLED``: will (after completing synchronization of
+ the content from the disk image chain to the target image, [E])
+ create a point-in-time (which is at the time of *triggering* the
+ cancel command) copy, contained in image [E], of the the entire disk
+ image chain (or only the top-most image, depending on the ``sync``
+ mode).
+
+(2) Issuing the command ``block-job-complete`` after it emits the event
+ ``BLOCK_JOB_COMPLETED``: will, after completing synchronization of
+ the content, adjust the guest device (i.e. live QEMU) to point to
+ the target image, and, causing all the new writes from this point on
+ to happen there. One use case for this is live storage migration.
+
+About synchronization modes: The synchronization mode determines
+*which* part of the disk image chain will be copied to the target.
+Currently, there are four different kinds:
+
+(1) ``full`` -- Synchronize the content of entire disk image chain to
+ the target
+
+(2) ``top`` -- Synchronize only the contents of the top-most disk image
+ in the chain to the target
+
+(3) ``none`` -- Synchronize only the new writes from this point on.
+
+ .. note:: In the case of ``drive-backup`` (or ``blockdev-backup``),
+ the behavior of ``none`` synchronization mode is different.
+ Normally, a ``backup`` job consists of two parts: Anything
+ that is overwritten by the guest is first copied out to
+ the backup, and in the background the whole image is
+ copied from start to end. With ``sync=none``, it's only
+ the first part.
+
+(4) ``incremental`` -- Synchronize content that is described by the
+ dirty bitmap
+
+.. note::
+ Refer to the :doc:`bitmaps` document in the QEMU source
+ tree to learn about the detailed workings of the ``incremental``
+ synchronization mode.
+
+
+QMP invocation for ``drive-mirror``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To copy the contents of the entire disk image chain, from [A] all the
+way to [D], to a new target (``drive-mirror`` will create the destination
+file, if it doesn't already exist), call it [E]::
+
+ (QEMU) drive-mirror device=node-D target=e.qcow2 sync=full job-id=job0
+ {
+ "execute": "drive-mirror",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "target": "e.qcow2",
+ "sync": "full"
+ }
+ }
+
+The ``"sync": "full"``, from the above, means: copy the *entire* chain
+to the destination.
+
+Following the above, querying for active block jobs will show that a
+'mirror' job is "ready" to be completed (and QEMU will also emit an
+event, ``BLOCK_JOB_READY``)::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+ {
+ "return": [
+ {
+ "busy": false,
+ "type": "mirror",
+ "len": 21757952,
+ "paused": false,
+ "ready": true,
+ "io-status": "ok",
+ "offset": 21757952,
+ "device": "job0",
+ "speed": 0
+ }
+ ]
+ }
+
+And, as noted in the previous section, there are two possible actions
+at this point:
+
+(a) Create a point-in-time snapshot by ending the synchronization. The
+ point-in-time is at the time of *ending* the sync. (The result of
+ the following being: the target image, [E], will be populated with
+ content from the entire chain, [A] to [D])::
+
+ (QEMU) block-job-cancel device=job0
+ {
+ "execute": "block-job-cancel",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+
+(b) Or, complete the operation and pivot the live QEMU to the target
+ copy::
+
+ (QEMU) block-job-complete device=job0
+
+In either of the above cases, if you once again run the
+`query-block-jobs` command, there should not be any active block
+operation.
+
+Comparing 'commit' and 'mirror': In both then cases, the overlay images
+can be discarded. However, with 'commit', the *existing* base image
+will be modified (by updating it with contents from overlays); while in
+the case of 'mirror', a *new* target image is populated with the data
+from the disk image chain.
+
+
+QMP invocation for live storage migration with ``drive-mirror`` + NBD
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Live storage migration (without shared storage setup) is one of the most
+common use-cases that takes advantage of the ``drive-mirror`` primitive
+and QEMU's built-in Network Block Device (NBD) server. Here's a quick
+walk-through of this setup.
+
+Given the disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+Instead of copying content from the entire chain, synchronize *only* the
+contents of the *top*-most disk image (i.e. the active layer), [D], to a
+target, say, [TargetDisk].
+
+.. important::
+ The destination host must already have the contents of the backing
+ chain, involving images [A], [B], and [C], visible via other means
+ -- whether by ``cp``, ``rsync``, or by some storage array-specific
+ command.)
+
+Sometimes, this is also referred to as "shallow copy" -- because only
+the "active layer", and not the rest of the image chain, is copied to
+the destination.
+
+.. note::
+ In this example, for the sake of simplicity, we'll be using the same
+ ``localhost`` as both source and destination.
+
+As noted earlier, on the destination host the contents of the backing
+chain -- from images [A] to [C] -- are already expected to exist in some
+form (e.g. in a file called, ``Contents-of-A-B-C.qcow2``). Now, on the
+destination host, let's create a target overlay image (with the image
+``Contents-of-A-B-C.qcow2`` as its backing file), to which the contents
+of image [D] (from the source QEMU) will be mirrored to::
+
+ $ qemu-img create -f qcow2 -b ./Contents-of-A-B-C.qcow2 \
+ -F qcow2 ./target-disk.qcow2
+
+And start the destination QEMU (we already have the source QEMU running
+-- discussed in the section: `Interacting with a QEMU instance`_)
+instance, with the following invocation. (As noted earlier, for
+simplicity's sake, the destination QEMU is started on the same host, but
+it could be located elsewhere)::
+
+ $ ./x86_64-softmmu/qemu-system-x86_64 -display none -nodefconfig \
+ -M q35 -nodefaults -m 512 \
+ -blockdev node-name=node-TargetDisk,driver=qcow2,file.driver=file,file.node-name=file,file.filename=./target-disk.qcow2 \
+ -device virtio-blk,drive=node-TargetDisk,id=virtio0 \
+ -S -monitor stdio -qmp unix:./qmp-sock2,server,nowait \
+ -incoming tcp:localhost:6666
+
+Given the disk image chain on source QEMU::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+On the destination host, it is expected that the contents of the chain
+``[A] <-- [B] <-- [C]`` are *already* present, and therefore copy *only*
+the content of image [D].
+
+(1) [On *destination* QEMU] As part of the first step, start the
+ built-in NBD server on a given host (local host, represented by
+ ``::``)and port::
+
+ (QEMU) nbd-server-start addr={"type":"inet","data":{"host":"::","port":"49153"}}
+ {
+ "execute": "nbd-server-start",
+ "arguments": {
+ "addr": {
+ "data": {
+ "host": "::",
+ "port": "49153"
+ },
+ "type": "inet"
+ }
+ }
+ }
+
+(2) [On *destination* QEMU] And export the destination disk image using
+ QEMU's built-in NBD server::
+
+ (QEMU) nbd-server-add device=node-TargetDisk writable=true
+ {
+ "execute": "nbd-server-add",
+ "arguments": {
+ "device": "node-TargetDisk"
+ }
+ }
+
+(3) [On *source* QEMU] Then, invoke ``drive-mirror`` (NB: since we're
+ running ``drive-mirror`` with ``mode=existing`` (meaning:
+ synchronize to a pre-created file, therefore 'existing', file on the
+ target host), with the synchronization mode as 'top' (``"sync:
+ "top"``)::
+
+ (QEMU) drive-mirror device=node-D target=nbd:localhost:49153:exportname=node-TargetDisk sync=top mode=existing job-id=job0
+ {
+ "execute": "drive-mirror",
+ "arguments": {
+ "device": "node-D",
+ "mode": "existing",
+ "job-id": "job0",
+ "target": "nbd:localhost:49153:exportname=node-TargetDisk",
+ "sync": "top"
+ }
+ }
+
+(4) [On *source* QEMU] Once ``drive-mirror`` copies the entire data, and the
+ event ``BLOCK_JOB_READY`` is emitted, issue ``block-job-cancel`` to
+ gracefully end the synchronization, from source QEMU::
+
+ (QEMU) block-job-cancel device=job0
+ {
+ "execute": "block-job-cancel",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+
+(5) [On *destination* QEMU] Then, stop the NBD server::
+
+ (QEMU) nbd-server-stop
+ {
+ "execute": "nbd-server-stop",
+ "arguments": {}
+ }
+
+(6) [On *destination* QEMU] Finally, resume the guest vCPUs by issuing the
+ QMP command `cont`::
+
+ (QEMU) cont
+ {
+ "execute": "cont",
+ "arguments": {}
+ }
+
+.. note::
+ Higher-level libraries (e.g. libvirt) automate the entire above
+ process (although note that libvirt does not allow same-host
+ migrations to localhost for other reasons).
+
+
+Notes on ``blockdev-mirror``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``blockdev-mirror`` command is equivalent in core functionality to
+``drive-mirror``, except that it operates at node-level in a BDS graph.
+
+Also: for ``blockdev-mirror``, the 'target' image needs to be explicitly
+created (using ``qemu-img``) and attach it to live QEMU via
+``blockdev-add``, which assigns a name to the to-be created target node.
+
+E.g. the sequence of actions to create a point-in-time backup of an
+entire disk image chain, to a target, using ``blockdev-mirror`` would be:
+
+(0) Create the QCOW2 overlays, to arrive at a backing chain of desired
+ depth
+
+(1) Create the target image (using ``qemu-img``), say, ``e.qcow2``
+
+(2) Attach the above created file (``e.qcow2``), run-time, using
+ ``blockdev-add`` to QEMU
+
+(3) Perform ``blockdev-mirror`` (use ``"sync": "full"`` to copy the
+ entire chain to the target). And notice the event
+ ``BLOCK_JOB_READY``
+
+(4) Optionally, query for active block jobs, there should be a 'mirror'
+ job ready to be completed
+
+(5) Gracefully complete the 'mirror' block device job, and notice the
+ the event ``BLOCK_JOB_COMPLETED``
+
+(6) Shutdown the guest by issuing the QMP ``quit`` command so that
+ caches are flushed
+
+(7) Then, finally, compare the contents of the disk image chain, and
+ the target copy with ``qemu-img compare``. You should notice:
+ "Images are identical"
+
+
+QMP invocation for ``blockdev-mirror``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given the disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+To copy the contents of the entire disk image chain, from [A] all the
+way to [D], to a new target, call it [E]. The following is the flow.
+
+Create the overlay images, [B], [C], and [D]::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-A snapshot-file=b.qcow2 snapshot-node-name=node-B format=qcow2
+ (QEMU) blockdev-snapshot-sync node-name=node-B snapshot-file=c.qcow2 snapshot-node-name=node-C format=qcow2
+ (QEMU) blockdev-snapshot-sync node-name=node-C snapshot-file=d.qcow2 snapshot-node-name=node-D format=qcow2
+
+Create the target image, [E]::
+
+ $ qemu-img create -f qcow2 e.qcow2 39M
+
+Add the above created target image to QEMU, via ``blockdev-add``::
+
+ (QEMU) blockdev-add driver=qcow2 node-name=node-E file={"driver":"file","filename":"e.qcow2"}
+ {
+ "execute": "blockdev-add",
+ "arguments": {
+ "node-name": "node-E",
+ "driver": "qcow2",
+ "file": {
+ "driver": "file",
+ "filename": "e.qcow2"
+ }
+ }
+ }
+
+Perform ``blockdev-mirror``, and notice the event ``BLOCK_JOB_READY``::
+
+ (QEMU) blockdev-mirror device=node-B target=node-E sync=full job-id=job0
+ {
+ "execute": "blockdev-mirror",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "target": "node-E",
+ "sync": "full"
+ }
+ }
+
+Query for active block jobs, there should be a 'mirror' job ready::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+ {
+ "return": [
+ {
+ "busy": false,
+ "type": "mirror",
+ "len": 21561344,
+ "paused": false,
+ "ready": true,
+ "io-status": "ok",
+ "offset": 21561344,
+ "device": "job0",
+ "speed": 0
+ }
+ ]
+ }
+
+Gracefully complete the block device job operation, and notice the
+event ``BLOCK_JOB_COMPLETED``::
+
+ (QEMU) block-job-complete device=job0
+ {
+ "execute": "block-job-complete",
+ "arguments": {
+ "device": "job0"
+ }
+ }
+ {
+ "return": {}
+ }
+
+Shutdown the guest, by issuing the ``quit`` QMP command::
+
+ (QEMU) quit
+ {
+ "execute": "quit",
+ "arguments": {}
+ }
+
+
+Live disk backup --- ``drive-backup`` and ``blockdev-backup``
+-------------------------------------------------------------
+
+The ``drive-backup`` (and its newer equivalent ``blockdev-backup``) allows
+you to create a point-in-time snapshot.
+
+In this case, the point-in-time is when you *start* the ``drive-backup``
+(or its newer equivalent ``blockdev-backup``) command.
+
+
+QMP invocation for ``drive-backup``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Yet again, starting afresh with our example disk image chain::
+
+ [A] <-- [B] <-- [C] <-- [D]
+
+To create a target image [E], with content populated from image [A] to
+[D], from the above chain, the following is the syntax. (If the target
+image does not exist, ``drive-backup`` will create it)::
+
+ (QEMU) drive-backup device=node-D sync=full target=e.qcow2 job-id=job0
+ {
+ "execute": "drive-backup",
+ "arguments": {
+ "device": "node-D",
+ "job-id": "job0",
+ "sync": "full",
+ "target": "e.qcow2"
+ }
+ }
+
+Once the above ``drive-backup`` has completed, a ``BLOCK_JOB_COMPLETED`` event
+will be issued, indicating the live block device job operation has
+completed, and no further action is required.
+
+
+Notes on ``blockdev-backup``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The ``blockdev-backup`` command is equivalent in functionality to
+``drive-backup``, except that it operates at node-level in a Block Driver
+State (BDS) graph.
+
+E.g. the sequence of actions to create a point-in-time backup
+of an entire disk image chain, to a target, using ``blockdev-backup``
+would be:
+
+(0) Create the QCOW2 overlays, to arrive at a backing chain of desired
+ depth
+
+(1) Create the target image (using ``qemu-img``), say, ``e.qcow2``
+
+(2) Attach the above created file (``e.qcow2``), run-time, using
+ ``blockdev-add`` to QEMU
+
+(3) Perform ``blockdev-backup`` (use ``"sync": "full"`` to copy the
+ entire chain to the target). And notice the event
+ ``BLOCK_JOB_COMPLETED``
+
+(4) Shutdown the guest, by issuing the QMP ``quit`` command, so that
+ caches are flushed
+
+(5) Then, finally, compare the contents of the disk image chain, and
+ the target copy with ``qemu-img compare``. You should notice:
+ "Images are identical"
+
+The following section shows an example QMP invocation for
+``blockdev-backup``.
+
+QMP invocation for ``blockdev-backup``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Given a disk image chain of depth 1 where image [B] is the active
+overlay (live QEMU is writing to it)::
+
+ [A] <-- [B]
+
+The following is the procedure to copy the content from the entire chain
+to a target image (say, [E]), which has the full content from [A] and
+[B].
+
+Create the overlay [B]::
+
+ (QEMU) blockdev-snapshot-sync node-name=node-A snapshot-file=b.qcow2 snapshot-node-name=node-B format=qcow2
+ {
+ "execute": "blockdev-snapshot-sync",
+ "arguments": {
+ "node-name": "node-A",
+ "snapshot-file": "b.qcow2",
+ "format": "qcow2",
+ "snapshot-node-name": "node-B"
+ }
+ }
+
+
+Create a target image that will contain the copy::
+
+ $ qemu-img create -f qcow2 e.qcow2 39M
+
+Then add it to QEMU via ``blockdev-add``::
+
+ (QEMU) blockdev-add driver=qcow2 node-name=node-E file={"driver":"file","filename":"e.qcow2"}
+ {
+ "execute": "blockdev-add",
+ "arguments": {
+ "node-name": "node-E",
+ "driver": "qcow2",
+ "file": {
+ "driver": "file",
+ "filename": "e.qcow2"
+ }
+ }
+ }
+
+Then invoke ``blockdev-backup`` to copy the contents from the entire
+image chain, consisting of images [A] and [B] to the target image
+'e.qcow2'::
+
+ (QEMU) blockdev-backup device=node-B target=node-E sync=full job-id=job0
+ {
+ "execute": "blockdev-backup",
+ "arguments": {
+ "device": "node-B",
+ "job-id": "job0",
+ "target": "node-E",
+ "sync": "full"
+ }
+ }
+
+Once the above 'backup' operation has completed, the event,
+``BLOCK_JOB_COMPLETED`` will be emitted, signalling successful
+completion.
+
+Next, query for any active block device jobs (there should be none)::
+
+ (QEMU) query-block-jobs
+ {
+ "execute": "query-block-jobs",
+ "arguments": {}
+ }
+
+Shutdown the guest::
+
+ (QEMU) quit
+ {
+ "execute": "quit",
+ "arguments": {}
+ }
+ "return": {}
+ }
+
+.. note::
+ The above step is really important; if forgotten, an error, "Failed
+ to get shared "write" lock on e.qcow2", will be thrown when you do
+ ``qemu-img compare`` to verify the integrity of the disk image
+ with the backup content.
+
+
+The end result will be the image 'e.qcow2' containing a
+point-in-time backup of the disk image chain -- i.e. contents from
+images [A] and [B] at the time the ``blockdev-backup`` command was
+initiated.
+
+One way to confirm the backup disk image contains the identical content
+with the disk image chain is to compare the backup and the contents of
+the chain, you should see "Images are identical". (NB: this is assuming
+QEMU was launched with ``-S`` option, which will not start the CPUs at
+guest boot up)::
+
+ $ qemu-img compare b.qcow2 e.qcow2
+ Warning: Image size mismatch!
+ Images are identical.
+
+NOTE: The "Warning: Image size mismatch!" is expected, as we created the
+target image (e.qcow2) with 39M size.
diff --git a/docs/live-block-ops.txt b/docs/live-block-ops.txt
deleted file mode 100644
index 2211d14..0000000
--- a/docs/live-block-ops.txt
+++ /dev/null
@@ -1,72 +0,0 @@
-LIVE BLOCK OPERATIONS
-=====================
-
-High level description of live block operations. Note these are not
-supported for use with the raw format at the moment.
-
-Note also that this document is incomplete and it currently only
-covers the 'stream' operation. Other operations supported by QEMU such
-as 'commit', 'mirror' and 'backup' are not described here yet. Please
-refer to the qapi/block-core.json file for an overview of those.
-
-Snapshot live merge
-===================
-
-Given a snapshot chain, described in this document in the following
-format:
-
-[A] <- [B] <- [C] <- [D] <- [E]
-
-Where the rightmost object ([E] in the example) described is the current
-image which the guest OS has write access to. To the left of it is its base
-image, and so on accordingly until the leftmost image, which has no
-base.
-
-The snapshot live merge operation transforms such a chain into a
-smaller one with fewer elements, such as this transformation relative
-to the first example:
-
-[A] <- [E]
-
-Data is copied in the right direction with destination being the
-rightmost image, but any other intermediate image can be specified
-instead. In this example data is copied from [C] into [D], so [D] can
-be backed by [B]:
-
-[A] <- [B] <- [D] <- [E]
-
-The operation is implemented in QEMU through image streaming facilities.
-
-The basic idea is to execute 'block_stream virtio0' while the guest is
-running. Progress can be monitored using 'info block-jobs'. When the
-streaming operation completes it raises a QMP event. 'block_stream'
-copies data from the backing file(s) into the active image. When finished,
-it adjusts the backing file pointer.
-
-The 'base' parameter specifies an image which data need not be
-streamed from. This image will be used as the backing file for the
-destination image when the operation is finished.
-
-In the first example above, the command would be:
-
-(qemu) block_stream virtio0 file-A.img
-
-In order to specify a destination image different from the active
-(rightmost) one we can use its node name instead.
-
-In the second example above, the command would be:
-
-(qemu) block_stream node-D file-B.img
-
-Live block copy
-===============
-
-To copy an in use image to another destination in the filesystem, one
-should create a live snapshot in the desired destination, then stream
-into that image. Example:
-
-(qemu) snapshot_blkdev ide0-hd0 /new-path/disk.img qcow2
-
-(qemu) block_stream ide0-hd0
-
-
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 4c5c4ee..a2e56ec 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -18,3 +18,4 @@
obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o
obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o
+obj-$(CONFIG_MPS2) += mps2.o
diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c
new file mode 100644
index 0000000..f727b43
--- /dev/null
+++ b/hw/arm/mps2.c
@@ -0,0 +1,385 @@
+/*
+ * ARM V2M MPS2 board emulation.
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* The MPS2 and MPS2+ dev boards are FPGA based (the 2+ has a bigger
+ * FPGA but is otherwise the same as the 2). Since the CPU itself
+ * and most of the devices are in the FPGA, the details of the board
+ * as seen by the guest depend significantly on the FPGA image.
+ * We model the following FPGA images:
+ * "mps2-an385" -- Cortex-M3 as documented in ARM Application Note AN385
+ * "mps2-an511" -- Cortex-M3 'DesignStart' as documented in AN511
+ *
+ * Links to the TRM for the board itself and to the various Application
+ * Notes which document the FPGA images can be found here:
+ * https://developer.arm.com/products/system-design/development-boards/cortex-m-prototyping-system
+ */
+
+#include "qemu/osdep.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "hw/arm/arm.h"
+#include "hw/arm/armv7m.h"
+#include "hw/or-irq.h"
+#include "hw/boards.h"
+#include "exec/address-spaces.h"
+#include "sysemu/sysemu.h"
+#include "hw/misc/unimp.h"
+#include "hw/char/cmsdk-apb-uart.h"
+#include "hw/timer/cmsdk-apb-timer.h"
+#include "hw/misc/mps2-scc.h"
+#include "hw/devices.h"
+#include "net/net.h"
+
+typedef enum MPS2FPGAType {
+ FPGA_AN385,
+ FPGA_AN511,
+} MPS2FPGAType;
+
+typedef struct {
+ MachineClass parent;
+ MPS2FPGAType fpga_type;
+ const char *cpu_model;
+ uint32_t scc_id;
+} MPS2MachineClass;
+
+typedef struct {
+ MachineState parent;
+
+ ARMv7MState armv7m;
+ MemoryRegion psram;
+ MemoryRegion ssram1;
+ MemoryRegion ssram1_m;
+ MemoryRegion ssram23;
+ MemoryRegion ssram23_m;
+ MemoryRegion blockram;
+ MemoryRegion blockram_m1;
+ MemoryRegion blockram_m2;
+ MemoryRegion blockram_m3;
+ MemoryRegion sram;
+ MPS2SCC scc;
+} MPS2MachineState;
+
+#define TYPE_MPS2_MACHINE "mps2"
+#define TYPE_MPS2_AN385_MACHINE MACHINE_TYPE_NAME("mps2-an385")
+#define TYPE_MPS2_AN511_MACHINE MACHINE_TYPE_NAME("mps2-an511")
+
+#define MPS2_MACHINE(obj) \
+ OBJECT_CHECK(MPS2MachineState, obj, TYPE_MPS2_MACHINE)
+#define MPS2_MACHINE_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(MPS2MachineClass, obj, TYPE_MPS2_MACHINE)
+#define MPS2_MACHINE_CLASS(klass) \
+ OBJECT_CLASS_CHECK(MPS2MachineClass, klass, TYPE_MPS2_MACHINE)
+
+/* Main SYSCLK frequency in Hz */
+#define SYSCLK_FRQ 25000000
+
+/* Initialize the auxiliary RAM region @mr and map it into
+ * the memory map at @base.
+ */
+static void make_ram(MemoryRegion *mr, const char *name,
+ hwaddr base, hwaddr size)
+{
+ memory_region_init_ram(mr, NULL, name, size, &error_fatal);
+ memory_region_add_subregion(get_system_memory(), base, mr);
+}
+
+/* Create an alias of an entire original MemoryRegion @orig
+ * located at @base in the memory map.
+ */
+static void make_ram_alias(MemoryRegion *mr, const char *name,
+ MemoryRegion *orig, hwaddr base)
+{
+ memory_region_init_alias(mr, NULL, name, orig, 0,
+ memory_region_size(orig));
+ memory_region_add_subregion(get_system_memory(), base, mr);
+}
+
+static void mps2_common_init(MachineState *machine)
+{
+ MPS2MachineState *mms = MPS2_MACHINE(machine);
+ MPS2MachineClass *mmc = MPS2_MACHINE_GET_CLASS(machine);
+ MemoryRegion *system_memory = get_system_memory();
+ DeviceState *armv7m, *sccdev;
+
+ if (!machine->cpu_model) {
+ machine->cpu_model = mmc->cpu_model;
+ }
+
+ if (strcmp(machine->cpu_model, mmc->cpu_model) != 0) {
+ error_report("This board can only be used with CPU %s", mmc->cpu_model);
+ exit(1);
+ }
+
+ /* The FPGA images have an odd combination of different RAMs,
+ * because in hardware they are different implementations and
+ * connected to different buses, giving varying performance/size
+ * tradeoffs. For QEMU they're all just RAM, though. We arbitrarily
+ * call the 16MB our "system memory", as it's the largest lump.
+ *
+ * Common to both boards:
+ * 0x21000000..0x21ffffff : PSRAM (16MB)
+ * AN385 only:
+ * 0x00000000 .. 0x003fffff : ZBT SSRAM1
+ * 0x00400000 .. 0x007fffff : mirror of ZBT SSRAM1
+ * 0x20000000 .. 0x203fffff : ZBT SSRAM 2&3
+ * 0x20400000 .. 0x207fffff : mirror of ZBT SSRAM 2&3
+ * 0x01000000 .. 0x01003fff : block RAM (16K)
+ * 0x01004000 .. 0x01007fff : mirror of above
+ * 0x01008000 .. 0x0100bfff : mirror of above
+ * 0x0100c000 .. 0x0100ffff : mirror of above
+ * AN511 only:
+ * 0x00000000 .. 0x0003ffff : FPGA block RAM
+ * 0x00400000 .. 0x007fffff : ZBT SSRAM1
+ * 0x20000000 .. 0x2001ffff : SRAM
+ * 0x20400000 .. 0x207fffff : ZBT SSRAM 2&3
+ *
+ * The AN385 has a feature where the lowest 16K can be mapped
+ * either to the bottom of the ZBT SSRAM1 or to the block RAM.
+ * This is of no use for QEMU so we don't implement it (as if
+ * zbt_boot_ctrl is always zero).
+ */
+ memory_region_allocate_system_memory(&mms->psram,
+ NULL, "mps.ram", 0x1000000);
+ memory_region_add_subregion(system_memory, 0x21000000, &mms->psram);
+
+ switch (mmc->fpga_type) {
+ case FPGA_AN385:
+ make_ram(&mms->ssram1, "mps.ssram1", 0x0, 0x400000);
+ make_ram_alias(&mms->ssram1_m, "mps.ssram1_m", &mms->ssram1, 0x400000);
+ make_ram(&mms->ssram23, "mps.ssram23", 0x20000000, 0x400000);
+ make_ram_alias(&mms->ssram23_m, "mps.ssram23_m",
+ &mms->ssram23, 0x20400000);
+ make_ram(&mms->blockram, "mps.blockram", 0x01000000, 0x4000);
+ make_ram_alias(&mms->blockram_m1, "mps.blockram_m1",
+ &mms->blockram, 0x01004000);
+ make_ram_alias(&mms->blockram_m2, "mps.blockram_m2",
+ &mms->blockram, 0x01008000);
+ make_ram_alias(&mms->blockram_m3, "mps.blockram_m3",
+ &mms->blockram, 0x0100c000);
+ break;
+ case FPGA_AN511:
+ make_ram(&mms->blockram, "mps.blockram", 0x0, 0x40000);
+ make_ram(&mms->ssram1, "mps.ssram1", 0x00400000, 0x00800000);
+ make_ram(&mms->sram, "mps.sram", 0x20000000, 0x20000);
+ make_ram(&mms->ssram23, "mps.ssram23", 0x20400000, 0x400000);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ object_initialize(&mms->armv7m, sizeof(mms->armv7m), TYPE_ARMV7M);
+ armv7m = DEVICE(&mms->armv7m);
+ qdev_set_parent_bus(armv7m, sysbus_get_default());
+ switch (mmc->fpga_type) {
+ case FPGA_AN385:
+ qdev_prop_set_uint32(armv7m, "num-irq", 32);
+ break;
+ case FPGA_AN511:
+ qdev_prop_set_uint32(armv7m, "num-irq", 64);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ qdev_prop_set_string(armv7m, "cpu-model", machine->cpu_model);
+ object_property_set_link(OBJECT(&mms->armv7m), OBJECT(system_memory),
+ "memory", &error_abort);
+ object_property_set_bool(OBJECT(&mms->armv7m), true, "realized",
+ &error_fatal);
+
+ create_unimplemented_device("zbtsmram mirror", 0x00400000, 0x00400000);
+ create_unimplemented_device("RESERVED 1", 0x00800000, 0x00800000);
+ create_unimplemented_device("Block RAM", 0x01000000, 0x00010000);
+ create_unimplemented_device("RESERVED 2", 0x01010000, 0x1EFF0000);
+ create_unimplemented_device("RESERVED 3", 0x20800000, 0x00800000);
+ create_unimplemented_device("PSRAM", 0x21000000, 0x01000000);
+ /* These three ranges all cover multiple devices; we may implement
+ * some of them below (in which case the real device takes precedence
+ * over the unimplemented-region mapping).
+ */
+ create_unimplemented_device("CMSDK APB peripheral region @0x40000000",
+ 0x40000000, 0x00010000);
+ create_unimplemented_device("CMSDK peripheral region @0x40010000",
+ 0x40010000, 0x00010000);
+ create_unimplemented_device("Extra peripheral region @0x40020000",
+ 0x40020000, 0x00010000);
+ create_unimplemented_device("RESERVED 4", 0x40030000, 0x001D0000);
+ create_unimplemented_device("VGA", 0x41000000, 0x0200000);
+
+ switch (mmc->fpga_type) {
+ case FPGA_AN385:
+ {
+ /* The overflow IRQs for UARTs 0, 1 and 2 are ORed together.
+ * Overflow for UARTs 4 and 5 doesn't trigger any interrupt.
+ */
+ Object *orgate;
+ DeviceState *orgate_dev;
+ int i;
+
+ orgate = object_new(TYPE_OR_IRQ);
+ object_property_set_int(orgate, 6, "num-lines", &error_fatal);
+ object_property_set_bool(orgate, true, "realized", &error_fatal);
+ orgate_dev = DEVICE(orgate);
+ qdev_connect_gpio_out(orgate_dev, 0, qdev_get_gpio_in(armv7m, 12));
+
+ for (i = 0; i < 5; i++) {
+ static const hwaddr uartbase[] = {0x40004000, 0x40005000,
+ 0x40006000, 0x40007000,
+ 0x40009000};
+ Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL;
+ /* RX irq number; TX irq is always one greater */
+ static const int uartirq[] = {0, 2, 4, 18, 20};
+ qemu_irq txovrint = NULL, rxovrint = NULL;
+
+ if (i < 3) {
+ txovrint = qdev_get_gpio_in(orgate_dev, i * 2);
+ rxovrint = qdev_get_gpio_in(orgate_dev, i * 2 + 1);
+ }
+
+ cmsdk_apb_uart_create(uartbase[i],
+ qdev_get_gpio_in(armv7m, uartirq[i] + 1),
+ qdev_get_gpio_in(armv7m, uartirq[i]),
+ txovrint, rxovrint,
+ NULL,
+ uartchr, SYSCLK_FRQ);
+ }
+ break;
+ }
+ case FPGA_AN511:
+ {
+ /* The overflow IRQs for all UARTs are ORed together.
+ * Tx and Rx IRQs for each UART are ORed together.
+ */
+ Object *orgate;
+ DeviceState *orgate_dev;
+ int i;
+
+ orgate = object_new(TYPE_OR_IRQ);
+ object_property_set_int(orgate, 10, "num-lines", &error_fatal);
+ object_property_set_bool(orgate, true, "realized", &error_fatal);
+ orgate_dev = DEVICE(orgate);
+ qdev_connect_gpio_out(orgate_dev, 0, qdev_get_gpio_in(armv7m, 12));
+
+ for (i = 0; i < 5; i++) {
+ /* system irq numbers for the combined tx/rx for each UART */
+ static const int uart_txrx_irqno[] = {0, 2, 45, 46, 56};
+ static const hwaddr uartbase[] = {0x40004000, 0x40005000,
+ 0x4002c000, 0x4002d000,
+ 0x4002e000};
+ Chardev *uartchr = i < MAX_SERIAL_PORTS ? serial_hds[i] : NULL;
+ Object *txrx_orgate;
+ DeviceState *txrx_orgate_dev;
+
+ txrx_orgate = object_new(TYPE_OR_IRQ);
+ object_property_set_int(txrx_orgate, 2, "num-lines", &error_fatal);
+ object_property_set_bool(txrx_orgate, true, "realized",
+ &error_fatal);
+ txrx_orgate_dev = DEVICE(txrx_orgate);
+ qdev_connect_gpio_out(txrx_orgate_dev, 0,
+ qdev_get_gpio_in(armv7m, uart_txrx_irqno[i]));
+ cmsdk_apb_uart_create(uartbase[i],
+ qdev_get_gpio_in(txrx_orgate_dev, 0),
+ qdev_get_gpio_in(txrx_orgate_dev, 1),
+ qdev_get_gpio_in(orgate_dev, 0),
+ qdev_get_gpio_in(orgate_dev, 1),
+ NULL,
+ uartchr, SYSCLK_FRQ);
+ }
+ break;
+ }
+ default:
+ g_assert_not_reached();
+ }
+
+ cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ);
+ cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ);
+
+ object_initialize(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC);
+ sccdev = DEVICE(&mms->scc);
+ qdev_set_parent_bus(armv7m, sysbus_get_default());
+ qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2);
+ qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008);
+ qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id);
+ object_property_set_bool(OBJECT(&mms->scc), true, "realized",
+ &error_fatal);
+ sysbus_mmio_map(SYS_BUS_DEVICE(sccdev), 0, 0x4002f000);
+
+ /* In hardware this is a LAN9220; the LAN9118 is software compatible
+ * except that it doesn't support the checksum-offload feature.
+ */
+ lan9118_init(&nd_table[0], 0x40200000,
+ qdev_get_gpio_in(armv7m,
+ mmc->fpga_type == FPGA_AN385 ? 13 : 47));
+
+ system_clock_scale = NANOSECONDS_PER_SECOND / SYSCLK_FRQ;
+
+ armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename,
+ 0x400000);
+}
+
+static void mps2_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->init = mps2_common_init;
+ mc->max_cpus = 1;
+}
+
+static void mps2_an385_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc);
+
+ mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3";
+ mmc->fpga_type = FPGA_AN385;
+ mmc->cpu_model = "cortex-m3";
+ mmc->scc_id = 0x41040000 | (385 << 4);
+}
+
+static void mps2_an511_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+ MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc);
+
+ mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3";
+ mmc->fpga_type = FPGA_AN511;
+ mmc->cpu_model = "cortex-m3";
+ mmc->scc_id = 0x4104000 | (511 << 4);
+}
+
+static const TypeInfo mps2_info = {
+ .name = TYPE_MPS2_MACHINE,
+ .parent = TYPE_MACHINE,
+ .abstract = true,
+ .instance_size = sizeof(MPS2MachineState),
+ .class_size = sizeof(MPS2MachineClass),
+ .class_init = mps2_class_init,
+};
+
+static const TypeInfo mps2_an385_info = {
+ .name = TYPE_MPS2_AN385_MACHINE,
+ .parent = TYPE_MPS2_MACHINE,
+ .class_init = mps2_an385_class_init,
+};
+
+static const TypeInfo mps2_an511_info = {
+ .name = TYPE_MPS2_AN511_MACHINE,
+ .parent = TYPE_MPS2_MACHINE,
+ .class_init = mps2_an511_class_init,
+};
+
+static void mps2_machine_init(void)
+{
+ type_register_static(&mps2_info);
+ type_register_static(&mps2_an385_info);
+ type_register_static(&mps2_an511_info);
+}
+
+type_init(mps2_machine_init);
diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs
index 55fcb68..1bcd37e 100644
--- a/hw/char/Makefile.objs
+++ b/hw/char/Makefile.objs
@@ -19,6 +19,7 @@
obj-$(CONFIG_STM32F2XX_USART) += stm32f2xx_usart.o
obj-$(CONFIG_RASPI) += bcm2835_aux.o
+common-obj-$(CONFIG_CMSDK_APB_UART) += cmsdk-apb-uart.o
common-obj-$(CONFIG_ETRAXFS) += etraxfs_ser.o
common-obj-$(CONFIG_ISA_DEBUG) += debugcon.o
common-obj-$(CONFIG_GRLIB) += grlib_apbuart.o
diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c
new file mode 100644
index 0000000..1ad1e14
--- /dev/null
+++ b/hw/char/cmsdk-apb-uart.c
@@ -0,0 +1,403 @@
+/*
+ * ARM CMSDK APB UART emulation
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* This is a model of the "APB UART" which is part of the Cortex-M
+ * System Design Kit (CMSDK) and documented in the Cortex-M System
+ * Design Kit Technical Reference Manual (ARM DDI0479C):
+ * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "chardev/char-fe.h"
+#include "chardev/char-serial.h"
+#include "hw/char/cmsdk-apb-uart.h"
+
+REG32(DATA, 0)
+REG32(STATE, 4)
+ FIELD(STATE, TXFULL, 0, 1)
+ FIELD(STATE, RXFULL, 1, 1)
+ FIELD(STATE, TXOVERRUN, 2, 1)
+ FIELD(STATE, RXOVERRUN, 3, 1)
+REG32(CTRL, 8)
+ FIELD(CTRL, TX_EN, 0, 1)
+ FIELD(CTRL, RX_EN, 1, 1)
+ FIELD(CTRL, TX_INTEN, 2, 1)
+ FIELD(CTRL, RX_INTEN, 3, 1)
+ FIELD(CTRL, TXO_INTEN, 4, 1)
+ FIELD(CTRL, RXO_INTEN, 5, 1)
+ FIELD(CTRL, HSTEST, 6, 1)
+REG32(INTSTATUS, 0xc)
+ FIELD(INTSTATUS, TX, 0, 1)
+ FIELD(INTSTATUS, RX, 1, 1)
+ FIELD(INTSTATUS, TXO, 2, 1)
+ FIELD(INTSTATUS, RXO, 3, 1)
+REG32(BAUDDIV, 0x10)
+REG32(PID4, 0xFD0)
+REG32(PID5, 0xFD4)
+REG32(PID6, 0xFD8)
+REG32(PID7, 0xFDC)
+REG32(PID0, 0xFE0)
+REG32(PID1, 0xFE4)
+REG32(PID2, 0xFE8)
+REG32(PID3, 0xFEC)
+REG32(CID0, 0xFF0)
+REG32(CID1, 0xFF4)
+REG32(CID2, 0xFF8)
+REG32(CID3, 0xFFC)
+
+/* PID/CID values */
+static const int uart_id[] = {
+ 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x21, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static bool uart_baudrate_ok(CMSDKAPBUART *s)
+{
+ /* The minimum permitted bauddiv setting is 16, so we just ignore
+ * settings below that (usually this means the device has just
+ * been reset and not yet programmed).
+ */
+ return s->bauddiv >= 16 && s->bauddiv <= s->pclk_frq;
+}
+
+static void uart_update_parameters(CMSDKAPBUART *s)
+{
+ QEMUSerialSetParams ssp;
+
+ /* This UART is always 8N1 but the baud rate is programmable. */
+ if (!uart_baudrate_ok(s)) {
+ return;
+ }
+
+ ssp.data_bits = 8;
+ ssp.parity = 'N';
+ ssp.stop_bits = 1;
+ ssp.speed = s->pclk_frq / s->bauddiv;
+ qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
+ trace_cmsdk_apb_uart_set_params(ssp.speed);
+}
+
+static void cmsdk_apb_uart_update(CMSDKAPBUART *s)
+{
+ /* update outbound irqs, including handling the way the rxo and txo
+ * interrupt status bits are just logical AND of the overrun bit in
+ * STATE and the overrun interrupt enable bit in CTRL.
+ */
+ uint32_t omask = (R_INTSTATUS_RXO_MASK | R_INTSTATUS_TXO_MASK);
+ s->intstatus &= ~omask;
+ s->intstatus |= (s->state & (s->ctrl >> 2) & omask);
+
+ qemu_set_irq(s->txint, !!(s->intstatus & R_INTSTATUS_TX_MASK));
+ qemu_set_irq(s->rxint, !!(s->intstatus & R_INTSTATUS_RX_MASK));
+ qemu_set_irq(s->txovrint, !!(s->intstatus & R_INTSTATUS_TXO_MASK));
+ qemu_set_irq(s->rxovrint, !!(s->intstatus & R_INTSTATUS_RXO_MASK));
+ qemu_set_irq(s->uartint, !!(s->intstatus));
+}
+
+static int uart_can_receive(void *opaque)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(opaque);
+
+ /* We can take a char if RX is enabled and the buffer is empty */
+ if (s->ctrl & R_CTRL_RX_EN_MASK && !(s->state & R_STATE_RXFULL_MASK)) {
+ return 1;
+ }
+ return 0;
+}
+
+static void uart_receive(void *opaque, const uint8_t *buf, int size)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(opaque);
+
+ trace_cmsdk_apb_uart_receive(*buf);
+
+ /* In fact uart_can_receive() ensures that we can't be
+ * called unless RX is enabled and the buffer is empty,
+ * but we include this logic as documentation of what the
+ * hardware does if a character arrives in these circumstances.
+ */
+ if (!(s->ctrl & R_CTRL_RX_EN_MASK)) {
+ /* Just drop the character on the floor */
+ return;
+ }
+
+ if (s->state & R_STATE_RXFULL_MASK) {
+ s->state |= R_STATE_RXOVERRUN_MASK;
+ }
+
+ s->rxbuf = *buf;
+ s->state |= R_STATE_RXFULL_MASK;
+ if (s->ctrl & R_CTRL_RX_INTEN_MASK) {
+ s->intstatus |= R_INTSTATUS_RX_MASK;
+ }
+ cmsdk_apb_uart_update(s);
+}
+
+static uint64_t uart_read(void *opaque, hwaddr offset, unsigned size)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(opaque);
+ uint64_t r;
+
+ switch (offset) {
+ case A_DATA:
+ r = s->rxbuf;
+ s->state &= ~R_STATE_RXFULL_MASK;
+ cmsdk_apb_uart_update(s);
+ break;
+ case A_STATE:
+ r = s->state;
+ break;
+ case A_CTRL:
+ r = s->ctrl;
+ break;
+ case A_INTSTATUS:
+ r = s->intstatus;
+ break;
+ case A_BAUDDIV:
+ r = s->bauddiv;
+ break;
+ case A_PID4 ... A_CID3:
+ r = uart_id[(offset - A_PID4) / 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB UART read: bad offset %x\n", (int) offset);
+ r = 0;
+ break;
+ }
+ trace_cmsdk_apb_uart_read(offset, r, size);
+ return r;
+}
+
+/* Try to send tx data, and arrange to be called back later if
+ * we can't (ie the char backend is busy/blocking).
+ */
+static gboolean uart_transmit(GIOChannel *chan, GIOCondition cond, void *opaque)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(opaque);
+ int ret;
+
+ s->watch_tag = 0;
+
+ if (!(s->ctrl & R_CTRL_TX_EN_MASK) || !(s->state & R_STATE_TXFULL_MASK)) {
+ return FALSE;
+ }
+
+ ret = qemu_chr_fe_write(&s->chr, &s->txbuf, 1);
+ if (ret <= 0) {
+ s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
+ uart_transmit, s);
+ if (!s->watch_tag) {
+ /* Most common reason to be here is "no chardev backend":
+ * just insta-drain the buffer, so the serial output
+ * goes into a void, rather than blocking the guest.
+ */
+ goto buffer_drained;
+ }
+ /* Transmit pending */
+ trace_cmsdk_apb_uart_tx_pending();
+ return FALSE;
+ }
+
+buffer_drained:
+ /* Character successfully sent */
+ trace_cmsdk_apb_uart_tx(s->txbuf);
+ s->state &= ~R_STATE_TXFULL_MASK;
+ /* Going from TXFULL set to clear triggers the tx interrupt */
+ if (s->ctrl & R_CTRL_TX_INTEN_MASK) {
+ s->intstatus |= R_INTSTATUS_TX_MASK;
+ }
+ cmsdk_apb_uart_update(s);
+ return FALSE;
+}
+
+static void uart_cancel_transmit(CMSDKAPBUART *s)
+{
+ if (s->watch_tag) {
+ g_source_remove(s->watch_tag);
+ s->watch_tag = 0;
+ }
+}
+
+static void uart_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(opaque);
+
+ trace_cmsdk_apb_uart_write(offset, value, size);
+
+ switch (offset) {
+ case A_DATA:
+ s->txbuf = value;
+ if (s->state & R_STATE_TXFULL_MASK) {
+ /* Buffer already full -- note the overrun and let the
+ * existing pending transmit callback handle the new char.
+ */
+ s->state |= R_STATE_TXOVERRUN_MASK;
+ cmsdk_apb_uart_update(s);
+ } else {
+ s->state |= R_STATE_TXFULL_MASK;
+ uart_transmit(NULL, G_IO_OUT, s);
+ }
+ break;
+ case A_STATE:
+ /* Bits 0 and 1 are read only; bits 2 and 3 are W1C */
+ s->state &= ~(value &
+ (R_STATE_TXOVERRUN_MASK | R_STATE_RXOVERRUN_MASK));
+ cmsdk_apb_uart_update(s);
+ break;
+ case A_CTRL:
+ s->ctrl = value & 0x7f;
+ if ((s->ctrl & R_CTRL_TX_EN_MASK) && !uart_baudrate_ok(s)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB UART: Tx enabled with invalid baudrate\n");
+ }
+ cmsdk_apb_uart_update(s);
+ break;
+ case A_INTSTATUS:
+ /* All bits are W1C. Clearing the overrun interrupt bits really
+ * clears the overrun status bits in the STATE register (which
+ * is then reflected into the intstatus value by the update function).
+ */
+ s->state &= ~(value & (R_INTSTATUS_TXO_MASK | R_INTSTATUS_RXO_MASK));
+ cmsdk_apb_uart_update(s);
+ break;
+ case A_BAUDDIV:
+ s->bauddiv = value & 0xFFFFF;
+ uart_update_parameters(s);
+ break;
+ case A_PID4 ... A_CID3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB UART write: write to RO offset 0x%x\n",
+ (int)offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB UART write: bad offset 0x%x\n", (int) offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps uart_ops = {
+ .read = uart_read,
+ .write = uart_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void cmsdk_apb_uart_reset(DeviceState *dev)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(dev);
+
+ trace_cmsdk_apb_uart_reset();
+ uart_cancel_transmit(s);
+ s->state = 0;
+ s->ctrl = 0;
+ s->intstatus = 0;
+ s->bauddiv = 0;
+ s->txbuf = 0;
+ s->rxbuf = 0;
+}
+
+static void cmsdk_apb_uart_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ CMSDKAPBUART *s = CMSDK_APB_UART(obj);
+
+ memory_region_init_io(&s->iomem, obj, &uart_ops, s, "uart", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->txint);
+ sysbus_init_irq(sbd, &s->rxint);
+ sysbus_init_irq(sbd, &s->txovrint);
+ sysbus_init_irq(sbd, &s->rxovrint);
+ sysbus_init_irq(sbd, &s->uartint);
+}
+
+static void cmsdk_apb_uart_realize(DeviceState *dev, Error **errp)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(dev);
+
+ if (s->pclk_frq == 0) {
+ error_setg(errp, "CMSDK APB UART: pclk-frq property must be set");
+ return;
+ }
+
+ /* This UART has no flow control, so we do not need to register
+ * an event handler to deal with CHR_EVENT_BREAK.
+ */
+ qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive,
+ NULL, NULL, s, NULL, true);
+}
+
+static int cmsdk_apb_uart_post_load(void *opaque, int version_id)
+{
+ CMSDKAPBUART *s = CMSDK_APB_UART(opaque);
+
+ /* If we have a pending character, arrange to resend it. */
+ if (s->state & R_STATE_TXFULL_MASK) {
+ s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP,
+ uart_transmit, s);
+ }
+ uart_update_parameters(s);
+ return 0;
+}
+
+static const VMStateDescription cmsdk_apb_uart_vmstate = {
+ .name = "cmsdk-apb-uart",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .post_load = cmsdk_apb_uart_post_load,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(state, CMSDKAPBUART),
+ VMSTATE_UINT32(ctrl, CMSDKAPBUART),
+ VMSTATE_UINT32(intstatus, CMSDKAPBUART),
+ VMSTATE_UINT32(bauddiv, CMSDKAPBUART),
+ VMSTATE_UINT8(txbuf, CMSDKAPBUART),
+ VMSTATE_UINT8(rxbuf, CMSDKAPBUART),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property cmsdk_apb_uart_properties[] = {
+ DEFINE_PROP_CHR("chardev", CMSDKAPBUART, chr),
+ DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBUART, pclk_frq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = cmsdk_apb_uart_realize;
+ dc->vmsd = &cmsdk_apb_uart_vmstate;
+ dc->reset = cmsdk_apb_uart_reset;
+ dc->props = cmsdk_apb_uart_properties;
+}
+
+static const TypeInfo cmsdk_apb_uart_info = {
+ .name = TYPE_CMSDK_APB_UART,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(CMSDKAPBUART),
+ .instance_init = cmsdk_apb_uart_init,
+ .class_init = cmsdk_apb_uart_class_init,
+};
+
+static void cmsdk_apb_uart_register_types(void)
+{
+ type_register_static(&cmsdk_apb_uart_info);
+}
+
+type_init(cmsdk_apb_uart_register_types);
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 7fd48bb..daf4ee4 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -56,3 +56,12 @@
pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR %08x read_count %d returning %d"
pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d"
pl011_put_fifo_full(void) "FIFO now full, RXFF set"
+
+# hw/char/cmsdk_apb_uart.c
+cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_uart_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_uart_reset(void) "CMSDK APB UART: reset"
+cmsdk_apb_uart_receive(uint8_t c) "CMSDK APB UART: got character 0x%x from backend"
+cmsdk_apb_uart_tx_pending(void) "CMSDK APB UART: character send to backend pending"
+cmsdk_apb_uart_tx(uint8_t c) "CMSDK APB UART: character 0x%x sent to backend"
+cmsdk_apb_uart_set_params(int speed) "CMSDK APB UART: params set to %d 8N1"
diff --git a/hw/core/machine.c b/hw/core/machine.c
index dc431fa..41b53a1 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -770,18 +770,11 @@
g_free(mc->name);
}
-static void machine_register_compat_for_subclass(ObjectClass *oc, void *opaque)
-{
- GlobalProperty *p = opaque;
- register_compat_prop(object_class_get_name(oc), p->property, p->value);
-}
-
void machine_register_compat_props(MachineState *machine)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
int i;
GlobalProperty *p;
- ObjectClass *oc;
if (!mc->compat_props) {
return;
@@ -789,22 +782,9 @@
for (i = 0; i < mc->compat_props->len; i++) {
p = g_array_index(mc->compat_props, GlobalProperty *, i);
- oc = object_class_by_name(p->driver);
- if (oc && object_class_is_abstract(oc)) {
- /* temporary hack to make sure we do not override
- * globals set explicitly on -global: if an abstract class
- * is on compat_props, register globals for all its
- * non-abstract subtypes instead.
- *
- * This doesn't solve the problem for cases where
- * a non-abstract typename mentioned on compat_props
- * has subclasses, like spapr-pci-host-bridge.
- */
- object_class_foreach(machine_register_compat_for_subclass,
- p->driver, false, p);
- } else {
- register_compat_prop(p->driver, p->property, p->value);
- }
+ /* Machine compat_props must never cause errors: */
+ p->errp = &error_abort;
+ qdev_prop_register_global(p);
}
}
diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c
index dcecdf0..58a8f92 100644
--- a/hw/core/qdev-properties.c
+++ b/hw/core/qdev-properties.c
@@ -1149,8 +1149,7 @@
return ret;
}
-static void qdev_prop_set_globals_for_type(DeviceState *dev,
- const char *typename)
+void qdev_prop_set_globals(DeviceState *dev)
{
GList *l;
@@ -1158,7 +1157,7 @@
GlobalProperty *prop = l->data;
Error *err = NULL;
- if (strcmp(typename, prop->driver) != 0) {
+ if (object_dynamic_cast(OBJECT(dev), prop->driver) == NULL) {
continue;
}
prop->used = true;
@@ -1176,16 +1175,6 @@
}
}
-void qdev_prop_set_globals(DeviceState *dev)
-{
- ObjectClass *class = object_get_class(OBJECT(dev));
-
- do {
- qdev_prop_set_globals_for_type(dev, object_class_get_name(class));
- class = object_class_get_parent(class);
- } while (class);
-}
-
/* --- 64bit unsigned int 'size' type --- */
static void get_size(Object *obj, Visitor *v, const char *name, void *opaque,
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index ec63fe0..606ab53 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -800,7 +800,7 @@
prop->info->description,
&error_abort);
- if (prop->info->set_default_value) {
+ if (prop->set_default) {
prop->info->set_default_value(obj, prop);
}
}
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 7e373db..3b91d32 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -52,6 +52,7 @@
obj-$(CONFIG_MIPS_CPS) += mips_cmgcr.o
obj-$(CONFIG_MIPS_CPS) += mips_cpc.o
obj-$(CONFIG_MIPS_ITU) += mips_itu.o
+obj-$(CONFIG_MPS2_SCC) += mps2-scc.o
obj-$(CONFIG_PVPANIC) += pvpanic.o
obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o
diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c
new file mode 100644
index 0000000..cc58d26
--- /dev/null
+++ b/hw/misc/mps2-scc.c
@@ -0,0 +1,310 @@
+/*
+ * ARM MPS2 SCC emulation
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* This is a model of the SCC (Serial Communication Controller)
+ * found in the FPGA images of MPS2 development boards.
+ *
+ * Documentation of it can be found in the MPS2 TRM:
+ * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100112_0100_03_en/index.html
+ * and also in the Application Notes documenting individual FPGA images.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/misc/mps2-scc.h"
+
+REG32(CFG0, 0)
+REG32(CFG1, 4)
+REG32(CFG3, 0xc)
+REG32(CFG4, 0x10)
+REG32(CFGDATA_RTN, 0xa0)
+REG32(CFGDATA_OUT, 0xa4)
+REG32(CFGCTRL, 0xa8)
+ FIELD(CFGCTRL, DEVICE, 0, 12)
+ FIELD(CFGCTRL, RES1, 12, 8)
+ FIELD(CFGCTRL, FUNCTION, 20, 6)
+ FIELD(CFGCTRL, RES2, 26, 4)
+ FIELD(CFGCTRL, WRITE, 30, 1)
+ FIELD(CFGCTRL, START, 31, 1)
+REG32(CFGSTAT, 0xac)
+ FIELD(CFGSTAT, DONE, 0, 1)
+ FIELD(CFGSTAT, ERROR, 1, 1)
+REG32(DLL, 0x100)
+REG32(AID, 0xFF8)
+REG32(ID, 0xFFC)
+
+/* Handle a write via the SYS_CFG channel to the specified function/device.
+ * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit).
+ */
+static bool scc_cfg_write(MPS2SCC *s, unsigned function,
+ unsigned device, uint32_t value)
+{
+ trace_mps2_scc_cfg_write(function, device, value);
+
+ if (function != 1 || device >= NUM_OSCCLK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPS2 SCC config write: bad function %d device %d\n",
+ function, device);
+ return false;
+ }
+
+ s->oscclk[device] = value;
+ return true;
+}
+
+/* Handle a read via the SYS_CFG channel to the specified function/device.
+ * Return false on error (reported to guest via SYS_CFGCTRL ERROR bit),
+ * or set *value on success.
+ */
+static bool scc_cfg_read(MPS2SCC *s, unsigned function,
+ unsigned device, uint32_t *value)
+{
+ if (function != 1 || device >= NUM_OSCCLK) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPS2 SCC config read: bad function %d device %d\n",
+ function, device);
+ return false;
+ }
+
+ *value = s->oscclk[device];
+
+ trace_mps2_scc_cfg_read(function, device, *value);
+ return true;
+}
+
+static uint64_t mps2_scc_read(void *opaque, hwaddr offset, unsigned size)
+{
+ MPS2SCC *s = MPS2_SCC(opaque);
+ uint64_t r;
+
+ switch (offset) {
+ case A_CFG0:
+ r = s->cfg0;
+ break;
+ case A_CFG1:
+ r = s->cfg1;
+ break;
+ case A_CFG3:
+ /* These are user-settable DIP switches on the board. We don't
+ * model that, so just return zeroes.
+ */
+ r = 0;
+ break;
+ case A_CFG4:
+ r = s->cfg4;
+ break;
+ case A_CFGDATA_RTN:
+ r = s->cfgdata_rtn;
+ break;
+ case A_CFGDATA_OUT:
+ r = s->cfgdata_out;
+ break;
+ case A_CFGCTRL:
+ r = s->cfgctrl;
+ break;
+ case A_CFGSTAT:
+ r = s->cfgstat;
+ break;
+ case A_DLL:
+ r = s->dll;
+ break;
+ case A_AID:
+ r = s->aid;
+ break;
+ case A_ID:
+ r = s->id;
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPS2 SCC read: bad offset %x\n", (int) offset);
+ r = 0;
+ break;
+ }
+
+ trace_mps2_scc_read(offset, r, size);
+ return r;
+}
+
+static void mps2_scc_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ MPS2SCC *s = MPS2_SCC(opaque);
+
+ trace_mps2_scc_write(offset, value, size);
+
+ switch (offset) {
+ case A_CFG0:
+ /* TODO on some boards bit 0 controls RAM remapping */
+ s->cfg0 = value;
+ break;
+ case A_CFG1:
+ /* CFG1 bits [7:0] control the board LEDs. We don't currently have
+ * a mechanism for displaying this graphically, so use a trace event.
+ */
+ trace_mps2_scc_leds(value & 0x80 ? '*' : '.',
+ value & 0x40 ? '*' : '.',
+ value & 0x20 ? '*' : '.',
+ value & 0x10 ? '*' : '.',
+ value & 0x08 ? '*' : '.',
+ value & 0x04 ? '*' : '.',
+ value & 0x02 ? '*' : '.',
+ value & 0x01 ? '*' : '.');
+ s->cfg1 = value;
+ break;
+ case A_CFGDATA_OUT:
+ s->cfgdata_out = value;
+ break;
+ case A_CFGCTRL:
+ /* Writing to CFGCTRL clears SYS_CFGSTAT */
+ s->cfgstat = 0;
+ s->cfgctrl = value & ~(R_CFGCTRL_RES1_MASK |
+ R_CFGCTRL_RES2_MASK |
+ R_CFGCTRL_START_MASK);
+
+ if (value & R_CFGCTRL_START_MASK) {
+ /* Start bit set -- do a read or write (instantaneously) */
+ int device = extract32(s->cfgctrl, R_CFGCTRL_DEVICE_SHIFT,
+ R_CFGCTRL_DEVICE_LENGTH);
+ int function = extract32(s->cfgctrl, R_CFGCTRL_FUNCTION_SHIFT,
+ R_CFGCTRL_FUNCTION_LENGTH);
+
+ s->cfgstat = R_CFGSTAT_DONE_MASK;
+ if (s->cfgctrl & R_CFGCTRL_WRITE_MASK) {
+ if (!scc_cfg_write(s, function, device, s->cfgdata_out)) {
+ s->cfgstat |= R_CFGSTAT_ERROR_MASK;
+ }
+ } else {
+ uint32_t result;
+ if (!scc_cfg_read(s, function, device, &result)) {
+ s->cfgstat |= R_CFGSTAT_ERROR_MASK;
+ } else {
+ s->cfgdata_rtn = result;
+ }
+ }
+ }
+ break;
+ case A_DLL:
+ /* DLL stands for Digital Locked Loop.
+ * Bits [31:24] (DLL_LOCK_MASK) are writable, and indicate a
+ * mask of which of the DLL_LOCKED bits [16:23] should be ORed
+ * together to determine the ALL_UNMASKED_DLLS_LOCKED bit [0].
+ * For QEMU, our DLLs are always locked, so we can leave bit 0
+ * as 1 always and don't need to recalculate it.
+ */
+ s->dll = deposit32(s->dll, 24, 8, extract32(value, 24, 8));
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "MPS2 SCC write: bad offset 0x%x\n", (int) offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps mps2_scc_ops = {
+ .read = mps2_scc_read,
+ .write = mps2_scc_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void mps2_scc_reset(DeviceState *dev)
+{
+ MPS2SCC *s = MPS2_SCC(dev);
+ int i;
+
+ trace_mps2_scc_reset();
+ s->cfg0 = 0;
+ s->cfg1 = 0;
+ s->cfgdata_rtn = 0;
+ s->cfgdata_out = 0;
+ s->cfgctrl = 0x100000;
+ s->cfgstat = 0;
+ s->dll = 0xffff0001;
+ for (i = 0; i < NUM_OSCCLK; i++) {
+ s->oscclk[i] = s->oscclk_reset[i];
+ }
+}
+
+static void mps2_scc_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ MPS2SCC *s = MPS2_SCC(obj);
+
+ memory_region_init_io(&s->iomem, obj, &mps2_scc_ops, s, "mps2-scc", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void mps2_scc_realize(DeviceState *dev, Error **errp)
+{
+}
+
+static const VMStateDescription mps2_scc_vmstate = {
+ .name = "mps2-scc",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(cfg0, MPS2SCC),
+ VMSTATE_UINT32(cfg1, MPS2SCC),
+ VMSTATE_UINT32(cfgdata_rtn, MPS2SCC),
+ VMSTATE_UINT32(cfgdata_out, MPS2SCC),
+ VMSTATE_UINT32(cfgctrl, MPS2SCC),
+ VMSTATE_UINT32(cfgstat, MPS2SCC),
+ VMSTATE_UINT32(dll, MPS2SCC),
+ VMSTATE_UINT32_ARRAY(oscclk, MPS2SCC, NUM_OSCCLK),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property mps2_scc_properties[] = {
+ /* Values for various read-only ID registers (which are specific
+ * to the board model or FPGA image)
+ */
+ DEFINE_PROP_UINT32("scc-cfg4", MPS2SCC, aid, 0),
+ DEFINE_PROP_UINT32("scc-aid", MPS2SCC, aid, 0),
+ DEFINE_PROP_UINT32("scc-id", MPS2SCC, aid, 0),
+ /* These are the initial settings for the source clocks on the board.
+ * In hardware they can be configured via a config file read by the
+ * motherboard configuration controller to suit the FPGA image.
+ * These default values are used by most of the standard FPGA images.
+ */
+ DEFINE_PROP_UINT32("oscclk0", MPS2SCC, oscclk_reset[0], 50000000),
+ DEFINE_PROP_UINT32("oscclk1", MPS2SCC, oscclk_reset[1], 24576000),
+ DEFINE_PROP_UINT32("oscclk2", MPS2SCC, oscclk_reset[2], 25000000),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void mps2_scc_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = mps2_scc_realize;
+ dc->vmsd = &mps2_scc_vmstate;
+ dc->reset = mps2_scc_reset;
+ dc->props = mps2_scc_properties;
+}
+
+static const TypeInfo mps2_scc_info = {
+ .name = TYPE_MPS2_SCC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MPS2SCC),
+ .instance_init = mps2_scc_init,
+ .class_init = mps2_scc_class_init,
+};
+
+static void mps2_scc_register_types(void)
+{
+ type_register_static(&mps2_scc_info);
+}
+
+type_init(mps2_scc_register_types);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index 0cc556c..28b8cd1 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -53,3 +53,11 @@
# hw/misc/aspeed_scu.c
aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32
+
+# hw/misc/mps2_scc.c
+mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+mps2_scc_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+mps2_scc_reset(void) "MPS2 SCC: reset"
+mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, char led1, char led0) "MPS2 SCC LEDs: %c%c%c%c%c%c%c%c"
+mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32
+mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 5630a9e..148071a 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -758,6 +758,8 @@
if (cmd == VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET) {
uint64_t supported_offloads;
+ offloads = virtio_ldq_p(vdev, &offloads);
+
if (!n->has_vnet_hdr) {
return VIRTIO_NET_ERR;
}
@@ -1942,7 +1944,7 @@
*/
if (n->net_conf.rx_queue_size < VIRTIO_NET_RX_QUEUE_MIN_SIZE ||
n->net_conf.rx_queue_size > VIRTQUEUE_MAX_SIZE ||
- (n->net_conf.rx_queue_size & (n->net_conf.rx_queue_size - 1))) {
+ !is_power_of_2(n->net_conf.rx_queue_size)) {
error_setg(errp, "Invalid rx_queue_size (= %" PRIu16 "), "
"must be a power of 2 between %d and %d.",
n->net_conf.rx_queue_size, VIRTIO_NET_RX_QUEUE_MIN_SIZE,
diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c
index e881e3b..5bd9044 100644
--- a/hw/nvram/fw_cfg.c
+++ b/hw/nvram/fw_cfg.c
@@ -37,17 +37,6 @@
#define FW_CFG_FILE_SLOTS_DFLT 0x20
-#define FW_CFG_NAME "fw_cfg"
-#define FW_CFG_PATH "/machine/" FW_CFG_NAME
-
-#define TYPE_FW_CFG "fw_cfg"
-#define TYPE_FW_CFG_IO "fw_cfg_io"
-#define TYPE_FW_CFG_MEM "fw_cfg_mem"
-
-#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG)
-#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO)
-#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM)
-
/* FW_CFG_VERSION bits */
#define FW_CFG_VERSION 0x01
#define FW_CFG_VERSION_DMA 0x02
@@ -61,51 +50,12 @@
#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */
-typedef struct FWCfgEntry {
+struct FWCfgEntry {
uint32_t len;
bool allow_write;
uint8_t *data;
void *callback_opaque;
FWCfgReadCallback read_callback;
-} FWCfgEntry;
-
-struct FWCfgState {
- /*< private >*/
- SysBusDevice parent_obj;
- /*< public >*/
-
- uint16_t file_slots;
- FWCfgEntry *entries[2];
- int *entry_order;
- FWCfgFiles *files;
- uint16_t cur_entry;
- uint32_t cur_offset;
- Notifier machine_ready;
-
- int fw_cfg_order_override;
-
- bool dma_enabled;
- dma_addr_t dma_addr;
- AddressSpace *dma_as;
- MemoryRegion dma_iomem;
-};
-
-struct FWCfgIoState {
- /*< private >*/
- FWCfgState parent_obj;
- /*< public >*/
-
- MemoryRegion comb_iomem;
-};
-
-struct FWCfgMemState {
- /*< private >*/
- FWCfgState parent_obj;
- /*< public >*/
-
- MemoryRegion ctl_iomem, data_iomem;
- uint32_t data_width;
- MemoryRegionOps wide_data_ops;
};
#define JPG_FILE 0
@@ -909,17 +859,16 @@
-static void fw_cfg_init1(DeviceState *dev)
+static void fw_cfg_common_realize(DeviceState *dev, Error **errp)
{
FWCfgState *s = FW_CFG(dev);
MachineState *machine = MACHINE(qdev_get_machine());
uint32_t version = FW_CFG_VERSION;
- assert(!object_resolve_path(FW_CFG_PATH, NULL));
-
- object_property_add_child(OBJECT(machine), FW_CFG_NAME, OBJECT(s), NULL);
-
- qdev_init_nofail(dev);
+ if (!fw_cfg_find()) {
+ error_setg(errp, "at most one %s device is permitted", TYPE_FW_CFG);
+ return;
+ }
fw_cfg_add_bytes(s, FW_CFG_SIGNATURE, (char *)"QEMU", 4);
fw_cfg_add_bytes(s, FW_CFG_UUID, &qemu_uuid, 16);
@@ -952,7 +901,9 @@
qdev_prop_set_bit(dev, "dma_enabled", false);
}
- fw_cfg_init1(dev);
+ object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
+ OBJECT(dev), NULL);
+ qdev_init_nofail(dev);
sbd = SYS_BUS_DEVICE(dev);
ios = FW_CFG_IO(dev);
@@ -990,7 +941,9 @@
qdev_prop_set_bit(dev, "dma_enabled", false);
}
- fw_cfg_init1(dev);
+ object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG,
+ OBJECT(dev), NULL);
+ qdev_init_nofail(dev);
sbd = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(sbd, 0, ctl_addr);
@@ -1017,9 +970,11 @@
FWCfgState *fw_cfg_find(void)
{
- return FW_CFG(object_resolve_path(FW_CFG_PATH, NULL));
+ /* Returns NULL unless there is exactly one fw_cfg device */
+ return FW_CFG(object_resolve_path_type("", TYPE_FW_CFG, NULL));
}
+
static void fw_cfg_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1091,6 +1046,8 @@
&fw_cfg_dma_mem_ops, FW_CFG(s), "fwcfg.dma",
sizeof(dma_addr_t));
}
+
+ fw_cfg_common_realize(dev, errp);
}
static void fw_cfg_io_class_init(ObjectClass *klass, void *data)
@@ -1157,6 +1114,8 @@
sizeof(dma_addr_t));
sysbus_init_mmio(sbd, &FW_CFG(s)->dma_iomem);
}
+
+ fw_cfg_common_realize(dev, errp);
}
static void fw_cfg_mem_class_init(ObjectClass *klass, void *data)
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index dd6f27e..15cce1c 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -41,3 +41,4 @@
common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o
common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o
+common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o
diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c
new file mode 100644
index 0000000..9878746
--- /dev/null
+++ b/hw/timer/cmsdk-apb-timer.c
@@ -0,0 +1,253 @@
+/*
+ * ARM CMSDK APB timer emulation
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+/* This is a model of the "APB timer" which is part of the Cortex-M
+ * System Design Kit (CMSDK) and documented in the Cortex-M System
+ * Design Kit Technical Reference Manual (ARM DDI0479C):
+ * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
+ *
+ * The hardware has an EXTIN input wire, which can be configured
+ * by the guest to act either as a 'timer enable' (timer does not run
+ * when EXTIN is low), or as a 'timer clock' (timer runs at frequency
+ * of EXTIN clock, not PCLK frequency). We don't model this.
+ *
+ * The documentation is not very clear about the exact behaviour;
+ * we choose to implement that the interrupt is triggered when
+ * the counter goes from 1 to 0, that the counter then holds at 0
+ * for one clock cycle before reloading from the RELOAD register,
+ * and that if the RELOAD register is 0 this does not cause an
+ * interrupt (as there is no further 1->0 transition).
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/main-loop.h"
+#include "qapi/error.h"
+#include "trace.h"
+#include "hw/sysbus.h"
+#include "hw/registerfields.h"
+#include "hw/timer/cmsdk-apb-timer.h"
+
+REG32(CTRL, 0)
+ FIELD(CTRL, EN, 0, 1)
+ FIELD(CTRL, SELEXTEN, 1, 1)
+ FIELD(CTRL, SELEXTCLK, 2, 1)
+ FIELD(CTRL, IRQEN, 3, 1)
+REG32(VALUE, 4)
+REG32(RELOAD, 8)
+REG32(INTSTATUS, 0xc)
+ FIELD(INTSTATUS, IRQ, 0, 1)
+REG32(PID4, 0xFD0)
+REG32(PID5, 0xFD4)
+REG32(PID6, 0xFD8)
+REG32(PID7, 0xFDC)
+REG32(PID0, 0xFE0)
+REG32(PID1, 0xFE4)
+REG32(PID2, 0xFE8)
+REG32(PID3, 0xFEC)
+REG32(CID0, 0xFF0)
+REG32(CID1, 0xFF4)
+REG32(CID2, 0xFF8)
+REG32(CID3, 0xFFC)
+
+/* PID/CID values */
+static const int timer_id[] = {
+ 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */
+ 0x22, 0xb8, 0x1b, 0x00, /* PID0..PID3 */
+ 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
+};
+
+static void cmsdk_apb_timer_update(CMSDKAPBTIMER *s)
+{
+ qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
+}
+
+static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+ CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
+ uint64_t r;
+
+ switch (offset) {
+ case A_CTRL:
+ r = s->ctrl;
+ break;
+ case A_VALUE:
+ r = ptimer_get_count(s->timer);
+ break;
+ case A_RELOAD:
+ r = ptimer_get_limit(s->timer);
+ break;
+ case A_INTSTATUS:
+ r = s->intstatus;
+ break;
+ case A_PID4 ... A_CID3:
+ r = timer_id[(offset - A_PID4) / 4];
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB timer read: bad offset %x\n", (int) offset);
+ r = 0;
+ break;
+ }
+ trace_cmsdk_apb_timer_read(offset, r, size);
+ return r;
+}
+
+static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
+ unsigned size)
+{
+ CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
+
+ trace_cmsdk_apb_timer_write(offset, value, size);
+
+ switch (offset) {
+ case A_CTRL:
+ if (value & 6) {
+ /* Bits [1] and [2] enable using EXTIN as either clock or
+ * an enable line. We don't model this.
+ */
+ qemu_log_mask(LOG_UNIMP,
+ "CMSDK APB timer: EXTIN input not supported\n");
+ }
+ s->ctrl = value & 0xf;
+ if (s->ctrl & R_CTRL_EN_MASK) {
+ ptimer_run(s->timer, 0);
+ } else {
+ ptimer_stop(s->timer);
+ }
+ break;
+ case A_RELOAD:
+ /* Writing to reload also sets the current timer value */
+ ptimer_set_limit(s->timer, value, 1);
+ break;
+ case A_VALUE:
+ ptimer_set_count(s->timer, value);
+ break;
+ case A_INTSTATUS:
+ /* Just one bit, which is W1C. */
+ value &= 1;
+ s->intstatus &= ~value;
+ cmsdk_apb_timer_update(s);
+ break;
+ case A_PID4 ... A_CID3:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB timer write: write to RO offset 0x%x\n",
+ (int)offset);
+ break;
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "CMSDK APB timer write: bad offset 0x%x\n", (int) offset);
+ break;
+ }
+}
+
+static const MemoryRegionOps cmsdk_apb_timer_ops = {
+ .read = cmsdk_apb_timer_read,
+ .write = cmsdk_apb_timer_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void cmsdk_apb_timer_tick(void *opaque)
+{
+ CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
+
+ if (s->ctrl & R_CTRL_IRQEN_MASK) {
+ s->intstatus |= R_INTSTATUS_IRQ_MASK;
+ cmsdk_apb_timer_update(s);
+ }
+}
+
+static void cmsdk_apb_timer_reset(DeviceState *dev)
+{
+ CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
+
+ trace_cmsdk_apb_timer_reset();
+ s->ctrl = 0;
+ s->intstatus = 0;
+ ptimer_stop(s->timer);
+ /* Set the limit and the count */
+ ptimer_set_limit(s->timer, 0, 1);
+}
+
+static void cmsdk_apb_timer_init(Object *obj)
+{
+ SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+ CMSDKAPBTIMER *s = CMSDK_APB_TIMER(obj);
+
+ memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
+ s, "cmsdk-apb-timer", 0x1000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->timerint);
+}
+
+static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
+{
+ CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
+ QEMUBH *bh;
+
+ if (s->pclk_frq == 0) {
+ error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
+ return;
+ }
+
+ bh = qemu_bh_new(cmsdk_apb_timer_tick, s);
+ s->timer = ptimer_init(bh,
+ PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD |
+ PTIMER_POLICY_NO_IMMEDIATE_TRIGGER |
+ PTIMER_POLICY_NO_IMMEDIATE_RELOAD |
+ PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
+
+ ptimer_set_freq(s->timer, s->pclk_frq);
+}
+
+static const VMStateDescription cmsdk_apb_timer_vmstate = {
+ .name = "cmsdk-apb-timer",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PTIMER(timer, CMSDKAPBTIMER),
+ VMSTATE_UINT32(ctrl, CMSDKAPBTIMER),
+ VMSTATE_UINT32(value, CMSDKAPBTIMER),
+ VMSTATE_UINT32(reload, CMSDKAPBTIMER),
+ VMSTATE_UINT32(intstatus, CMSDKAPBTIMER),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static Property cmsdk_apb_timer_properties[] = {
+ DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBTIMER, pclk_frq, 0),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = cmsdk_apb_timer_realize;
+ dc->vmsd = &cmsdk_apb_timer_vmstate;
+ dc->reset = cmsdk_apb_timer_reset;
+ dc->props = cmsdk_apb_timer_properties;
+}
+
+static const TypeInfo cmsdk_apb_timer_info = {
+ .name = TYPE_CMSDK_APB_TIMER,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(CMSDKAPBTIMER),
+ .instance_init = cmsdk_apb_timer_init,
+ .class_init = cmsdk_apb_timer_class_init,
+};
+
+static void cmsdk_apb_timer_register_types(void)
+{
+ type_register_static(&cmsdk_apb_timer_info);
+}
+
+type_init(cmsdk_apb_timer_register_types);
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index d17cfe6..fd8196b 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -55,3 +55,8 @@
systick_timer_tick(void) "systick reload"
systick_read(uint64_t addr, uint32_t value, unsigned size) "systick read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
+
+# hw/char/cmsdk_apb_timer.c
+cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
+cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset"
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index c1bb6d4..7b2924c 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -1109,6 +1109,14 @@
goto free_container_exit;
}
+ vfio_kvm_device_add_group(group);
+
+ QLIST_INIT(&container->group_list);
+ QLIST_INSERT_HEAD(&space->containers, container, next);
+
+ group->container = container;
+ QLIST_INSERT_HEAD(&container->group_list, group, container_next);
+
container->listener = vfio_memory_listener;
memory_listener_register(&container->listener, container->space->as);
@@ -1122,14 +1130,11 @@
container->initialized = true;
- QLIST_INIT(&container->group_list);
- QLIST_INSERT_HEAD(&space->containers, container, next);
-
- group->container = container;
- QLIST_INSERT_HEAD(&container->group_list, group, container_next);
-
return 0;
listener_release_exit:
+ QLIST_REMOVE(group, container_next);
+ QLIST_REMOVE(container, next);
+ vfio_kvm_device_del_group(group);
vfio_listener_release(container);
free_container_exit:
@@ -1234,8 +1239,6 @@
QLIST_INSERT_HEAD(&vfio_group_list, group, next);
- vfio_kvm_device_add_group(group);
-
return group;
close_fd_exit:
diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h
index bf8da2a..87b1b74 100644
--- a/include/exec/exec-all.h
+++ b/include/exec/exec-all.h
@@ -35,11 +35,34 @@
typedef ram_addr_t tb_page_addr_t;
#endif
-/* is_jmp field values */
+/* DisasContext is_jmp field values
+ *
+ * is_jmp starts as DISAS_NEXT. The translator will keep processing
+ * instructions until an exit condition is reached. If we reach the
+ * exit condition and is_jmp is still DISAS_NEXT (because of some
+ * other condition) we simply "jump" to the next address.
+ * The remaining exit cases are:
+ *
+ * DISAS_JUMP - Only the PC was modified dynamically (e.g computed)
+ * DISAS_TB_JUMP - Only the PC was modified statically (e.g. branch)
+ *
+ * In these cases as long as the PC is updated we can chain to the
+ * next TB either by exiting the loop or looking up the next TB via
+ * the loookup helper.
+ *
+ * DISAS_UPDATE - CPU State was modified dynamically
+ *
+ * This covers any other CPU state which necessities us exiting the
+ * TCG code to the main run-loop. Typically this includes anything
+ * that might change the interrupt state.
+ *
+ * Individual translators may define additional exit cases to deal
+ * with per-target special conditions.
+ */
#define DISAS_NEXT 0 /* next instruction can be analyzed */
#define DISAS_JUMP 1 /* only pc was modified dynamically */
-#define DISAS_UPDATE 2 /* cpu state was modified dynamically */
-#define DISAS_TB_JUMP 3 /* only pc was modified statically */
+#define DISAS_TB_JUMP 2 /* only pc was modified statically */
+#define DISAS_UPDATE 3 /* cpu state was modified dynamically */
#include "qemu/log.h"
@@ -330,6 +353,9 @@
#define CF_USE_ICOUNT 0x20000
#define CF_IGNORE_ICOUNT 0x40000 /* Do not generate icount code */
+ /* Per-vCPU dynamic tracing state used to generate this TB */
+ uint32_t trace_vcpu_dstate;
+
uint16_t invalid;
void *tc_ptr; /* pointer to the translated code */
diff --git a/include/exec/tb-hash-xx.h b/include/exec/tb-hash-xx.h
index 2c40b5c..6cd3022 100644
--- a/include/exec/tb-hash-xx.h
+++ b/include/exec/tb-hash-xx.h
@@ -49,7 +49,7 @@
* contiguous in memory.
*/
static inline
-uint32_t tb_hash_func5(uint64_t a0, uint64_t b0, uint32_t e)
+uint32_t tb_hash_func6(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f)
{
uint32_t v1 = TB_HASH_XX_SEED + PRIME32_1 + PRIME32_2;
uint32_t v2 = TB_HASH_XX_SEED + PRIME32_2;
@@ -78,11 +78,14 @@
v4 *= PRIME32_1;
h32 = rol32(v1, 1) + rol32(v2, 7) + rol32(v3, 12) + rol32(v4, 18);
- h32 += 20;
+ h32 += 24;
h32 += e * PRIME32_3;
h32 = rol32(h32, 17) * PRIME32_4;
+ h32 += f * PRIME32_3;
+ h32 = rol32(h32, 17) * PRIME32_4;
+
h32 ^= h32 >> 15;
h32 *= PRIME32_2;
h32 ^= h32 >> 13;
diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h
index b1fe2d0..17b5ee0 100644
--- a/include/exec/tb-hash.h
+++ b/include/exec/tb-hash.h
@@ -58,9 +58,10 @@
#endif /* CONFIG_SOFTMMU */
static inline
-uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags)
+uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags,
+ uint32_t trace_vcpu_dstate)
{
- return tb_hash_func5(phys_pc, pc, flags);
+ return tb_hash_func6(phys_pc, pc, flags, trace_vcpu_dstate);
}
#endif
diff --git a/include/hw/char/cmsdk-apb-uart.h b/include/hw/char/cmsdk-apb-uart.h
new file mode 100644
index 0000000..c41fba9
--- /dev/null
+++ b/include/hw/char/cmsdk-apb-uart.h
@@ -0,0 +1,78 @@
+/*
+ * ARM CMSDK APB UART emulation
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#ifndef CMSDK_APB_UART_H
+#define CMSDK_APB_UART_H
+
+#include "hw/sysbus.h"
+#include "chardev/char-fe.h"
+
+#define TYPE_CMSDK_APB_UART "cmsdk-apb-uart"
+#define CMSDK_APB_UART(obj) OBJECT_CHECK(CMSDKAPBUART, (obj), \
+ TYPE_CMSDK_APB_UART)
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion iomem;
+ CharBackend chr;
+ qemu_irq txint;
+ qemu_irq rxint;
+ qemu_irq txovrint;
+ qemu_irq rxovrint;
+ qemu_irq uartint;
+ guint watch_tag;
+ uint32_t pclk_frq;
+
+ uint32_t state;
+ uint32_t ctrl;
+ uint32_t intstatus;
+ uint32_t bauddiv;
+ /* This UART has no FIFO, only a 1-character buffer for each of Tx and Rx */
+ uint8_t txbuf;
+ uint8_t rxbuf;
+} CMSDKAPBUART;
+
+/**
+ * cmsdk_apb_uart_create - convenience function to create TYPE_CMSDK_APB_UART
+ * @addr: location in system memory to map registers
+ * @chr: Chardev backend to connect UART to, or NULL if no backend
+ * @pclk_frq: frequency in Hz of the PCLK clock (used for calculating baud rate)
+ */
+static inline DeviceState *cmsdk_apb_uart_create(hwaddr addr,
+ qemu_irq txint,
+ qemu_irq rxint,
+ qemu_irq txovrint,
+ qemu_irq rxovrint,
+ qemu_irq uartint,
+ Chardev *chr,
+ uint32_t pclk_frq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, TYPE_CMSDK_APB_UART);
+ s = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_chr(dev, "chardev", chr);
+ qdev_prop_set_uint32(dev, "pclk-frq", pclk_frq);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(s, 0, addr);
+ sysbus_connect_irq(s, 0, txint);
+ sysbus_connect_irq(s, 1, rxint);
+ sysbus_connect_irq(s, 2, txovrint);
+ sysbus_connect_irq(s, 3, rxovrint);
+ sysbus_connect_irq(s, 4, uartint);
+ return dev;
+}
+
+#endif
diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h
index f48d167..d80859b 100644
--- a/include/hw/i386/pc.h
+++ b/include/hw/i386/pc.h
@@ -380,6 +380,11 @@
#define PC_COMPAT_2_8 \
HW_COMPAT_2_8 \
{\
+ .driver = TYPE_X86_CPU,\
+ .property = "tcg-cpuid",\
+ .value = "off",\
+ },\
+ {\
.driver = "kvmclock",\
.property = "x-mach-use-reliable-get-clock",\
.value = "off",\
diff --git a/include/hw/misc/mps2-scc.h b/include/hw/misc/mps2-scc.h
new file mode 100644
index 0000000..7045473
--- /dev/null
+++ b/include/hw/misc/mps2-scc.h
@@ -0,0 +1,43 @@
+/*
+ * ARM MPS2 SCC emulation
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#ifndef MPS2_SCC_H
+#define MPS2_SCC_H
+
+#include "hw/sysbus.h"
+
+#define TYPE_MPS2_SCC "mps2-scc"
+#define MPS2_SCC(obj) OBJECT_CHECK(MPS2SCC, (obj), TYPE_MPS2_SCC)
+
+#define NUM_OSCCLK 3
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion iomem;
+
+ uint32_t cfg0;
+ uint32_t cfg1;
+ uint32_t cfg4;
+ uint32_t cfgdata_rtn;
+ uint32_t cfgdata_out;
+ uint32_t cfgctrl;
+ uint32_t cfgstat;
+ uint32_t dll;
+ uint32_t aid;
+ uint32_t id;
+ uint32_t oscclk[NUM_OSCCLK];
+ uint32_t oscclk_reset[NUM_OSCCLK];
+} MPS2SCC;
+
+#endif
diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h
index b980cba..b77ea48 100644
--- a/include/hw/nvram/fw_cfg.h
+++ b/include/hw/nvram/fw_cfg.h
@@ -1,8 +1,19 @@
#ifndef FW_CFG_H
#define FW_CFG_H
+#include "qemu/typedefs.h"
#include "exec/hwaddr.h"
#include "hw/nvram/fw_cfg_keys.h"
+#include "hw/sysbus.h"
+#include "sysemu/dma.h"
+
+#define TYPE_FW_CFG "fw_cfg"
+#define TYPE_FW_CFG_IO "fw_cfg_io"
+#define TYPE_FW_CFG_MEM "fw_cfg_mem"
+
+#define FW_CFG(obj) OBJECT_CHECK(FWCfgState, (obj), TYPE_FW_CFG)
+#define FW_CFG_IO(obj) OBJECT_CHECK(FWCfgIoState, (obj), TYPE_FW_CFG_IO)
+#define FW_CFG_MEM(obj) OBJECT_CHECK(FWCfgMemState, (obj), TYPE_FW_CFG_MEM)
typedef struct FWCfgFile {
uint32_t size; /* file size */
@@ -35,6 +46,45 @@
typedef void (*FWCfgReadCallback)(void *opaque);
+struct FWCfgState {
+ /*< private >*/
+ SysBusDevice parent_obj;
+ /*< public >*/
+
+ uint16_t file_slots;
+ FWCfgEntry *entries[2];
+ int *entry_order;
+ FWCfgFiles *files;
+ uint16_t cur_entry;
+ uint32_t cur_offset;
+ Notifier machine_ready;
+
+ int fw_cfg_order_override;
+
+ bool dma_enabled;
+ dma_addr_t dma_addr;
+ AddressSpace *dma_as;
+ MemoryRegion dma_iomem;
+};
+
+struct FWCfgIoState {
+ /*< private >*/
+ FWCfgState parent_obj;
+ /*< public >*/
+
+ MemoryRegion comb_iomem;
+};
+
+struct FWCfgMemState {
+ /*< private >*/
+ FWCfgState parent_obj;
+ /*< public >*/
+
+ MemoryRegion ctl_iomem, data_iomem;
+ uint32_t data_width;
+ MemoryRegionOps wide_data_ops;
+};
+
/**
* fw_cfg_add_bytes:
* @s: fw_cfg device being modified
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 5348815..ae31728 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -221,11 +221,21 @@
QLIST_ENTRY(BusState) sibling;
};
+/**
+ * Property:
+ * @set_default: true if the default value should be set from @defval,
+ * in which case @info->set_default_value must not be NULL
+ * (if false then no default value is set by the property system
+ * and the field retains whatever value it was given by instance_init).
+ * @defval: default value for the property. This is used only if @set_default
+ * is true.
+ */
struct Property {
const char *name;
const PropertyInfo *info;
ptrdiff_t offset;
uint8_t bitnr;
+ bool set_default;
union {
int64_t i;
uint64_t u;
diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h
index f6692d5..3929796 100644
--- a/include/hw/qdev-properties.h
+++ b/include/hw/qdev-properties.h
@@ -44,15 +44,24 @@
.info = &(_prop), \
.offset = offsetof(_state, _field) \
+ type_check(_type,typeof_field(_state, _field)), \
+ .set_default = true, \
.defval.i = (_type)_defval, \
}
+#define DEFINE_PROP_SIGNED_NODEFAULT(_name, _state, _field, _prop, _type) { \
+ .name = (_name), \
+ .info = &(_prop), \
+ .offset = offsetof(_state, _field) \
+ + type_check(_type, typeof_field(_state, _field)), \
+ }
+
#define DEFINE_PROP_BIT(_name, _state, _field, _bit, _defval) { \
.name = (_name), \
.info = &(qdev_prop_bit), \
.bitnr = (_bit), \
.offset = offsetof(_state, _field) \
+ type_check(uint32_t,typeof_field(_state, _field)), \
+ .set_default = true, \
.defval.u = (bool)_defval, \
}
@@ -61,15 +70,24 @@
.info = &(_prop), \
.offset = offsetof(_state, _field) \
+ type_check(_type, typeof_field(_state, _field)), \
+ .set_default = true, \
.defval.u = (_type)_defval, \
}
+#define DEFINE_PROP_UNSIGNED_NODEFAULT(_name, _state, _field, _prop, _type) { \
+ .name = (_name), \
+ .info = &(_prop), \
+ .offset = offsetof(_state, _field) \
+ + type_check(_type, typeof_field(_state, _field)), \
+ }
+
#define DEFINE_PROP_BIT64(_name, _state, _field, _bit, _defval) { \
.name = (_name), \
.info = &(qdev_prop_bit64), \
.bitnr = (_bit), \
.offset = offsetof(_state, _field) \
+ type_check(uint64_t, typeof_field(_state, _field)), \
+ .set_default = true, \
.defval.u = (bool)_defval, \
}
@@ -78,6 +96,7 @@
.info = &(qdev_prop_bool), \
.offset = offsetof(_state, _field) \
+ type_check(bool, typeof_field(_state, _field)), \
+ .set_default = true, \
.defval.u = (bool)_defval, \
}
@@ -111,6 +130,8 @@
_arrayfield, _arrayprop, _arraytype) { \
.name = (PROP_ARRAY_LEN_PREFIX _name), \
.info = &(qdev_prop_arraylen), \
+ .set_default = true, \
+ .defval.u = 0, \
.offset = offsetof(_state, _field) \
+ type_check(uint32_t, typeof_field(_state, _field)), \
.arrayinfo = &(_arrayprop), \
diff --git a/include/hw/timer/cmsdk-apb-timer.h b/include/hw/timer/cmsdk-apb-timer.h
new file mode 100644
index 0000000..f21686d
--- /dev/null
+++ b/include/hw/timer/cmsdk-apb-timer.h
@@ -0,0 +1,59 @@
+/*
+ * ARM CMSDK APB timer emulation
+ *
+ * Copyright (c) 2017 Linaro Limited
+ * Written by Peter Maydell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 or
+ * (at your option) any later version.
+ */
+
+#ifndef CMSDK_APB_TIMER_H
+#define CMSDK_APB_TIMER_H
+
+#include "hw/sysbus.h"
+#include "hw/ptimer.h"
+
+#define TYPE_CMSDK_APB_TIMER "cmsdk-apb-timer"
+#define CMSDK_APB_TIMER(obj) OBJECT_CHECK(CMSDKAPBTIMER, (obj), \
+ TYPE_CMSDK_APB_TIMER)
+
+typedef struct {
+ /*< private >*/
+ SysBusDevice parent_obj;
+
+ /*< public >*/
+ MemoryRegion iomem;
+ qemu_irq timerint;
+ uint32_t pclk_frq;
+ struct ptimer_state *timer;
+
+ uint32_t ctrl;
+ uint32_t value;
+ uint32_t reload;
+ uint32_t intstatus;
+} CMSDKAPBTIMER;
+
+/**
+ * cmsdk_apb_timer_create - convenience function to create TYPE_CMSDK_APB_TIMER
+ * @addr: location in system memory to map registers
+ * @pclk_frq: frequency in Hz of the PCLK clock (used for calculating baud rate)
+ */
+static inline DeviceState *cmsdk_apb_timer_create(hwaddr addr,
+ qemu_irq timerint,
+ uint32_t pclk_frq)
+{
+ DeviceState *dev;
+ SysBusDevice *s;
+
+ dev = qdev_create(NULL, TYPE_CMSDK_APB_TIMER);
+ s = SYS_BUS_DEVICE(dev);
+ qdev_prop_set_uint32(dev, "pclk-frq", pclk_frq);
+ qdev_init_nofail(dev);
+ sysbus_mmio_map(s, 0, addr);
+ sysbus_connect_irq(s, 0, timerint);
+ return dev;
+}
+
+#endif
diff --git a/include/net/net.h b/include/net/net.h
index 99b28d5..1c55a93 100644
--- a/include/net/net.h
+++ b/include/net/net.h
@@ -100,6 +100,7 @@
unsigned int queue_index;
unsigned rxfilter_notify_enabled:1;
int vring_enable;
+ int vnet_hdr_len;
QTAILQ_HEAD(NetFilterHead, NetFilterState) filters;
};
@@ -111,9 +112,13 @@
} NICState;
struct SocketReadState {
- int state; /* 0 = getting length, 1 = getting data */
+ /* 0 = getting length, 1 = getting vnet header length, 2 = getting data */
+ int state;
+ /* This flag decide whether to read the vnet_hdr_len field */
+ bool vnet_hdr;
uint32_t index;
uint32_t packet_len;
+ uint32_t vnet_hdr_len;
uint8_t buf[NET_BUFSIZE];
SocketReadStateFinalize *finalize;
};
@@ -176,7 +181,8 @@
void print_net_client(Monitor *mon, NetClientState *nc);
void hmp_info_network(Monitor *mon, const QDict *qdict);
void net_socket_rs_init(SocketReadState *rs,
- SocketReadStateFinalize *finalize);
+ SocketReadStateFinalize *finalize,
+ bool vnet_hdr);
/* NIC info */
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index b191591..7b0d4e7 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -30,6 +30,7 @@
typedef struct DriveInfo DriveInfo;
typedef struct Error Error;
typedef struct EventNotifier EventNotifier;
+typedef struct FWCfgEntry FWCfgEntry;
typedef struct FWCfgIoState FWCfgIoState;
typedef struct FWCfgMemState FWCfgMemState;
typedef struct FWCfgState FWCfgState;
diff --git a/include/qom/cpu.h b/include/qom/cpu.h
index 04c31e6..25eefea 100644
--- a/include/qom/cpu.h
+++ b/include/qom/cpu.h
@@ -259,6 +259,7 @@
struct qemu_work_item;
#define CPU_UNSET_NUMA_NODE_ID -1
+#define CPU_TRACE_DSTATE_MAX_EVENTS 32
/**
* CPUState:
@@ -301,6 +302,8 @@
* @kvm_fd: vCPU file descriptor for KVM.
* @work_mutex: Lock to prevent multiple access to queued_work_*.
* @queued_work_first: First asynchronous work pending.
+ * @trace_dstate_delayed: Delayed changes to trace_dstate (includes all changes
+ * to @trace_dstate).
* @trace_dstate: Dynamic tracing state of events for this vCPU (bitmask).
*
* State of one CPU core or thread.
@@ -370,12 +373,9 @@
struct KVMState *kvm_state;
struct kvm_run *kvm_run;
- /*
- * Used for events with 'vcpu' and *without* the 'disabled' properties.
- * Dynamically allocated based on bitmap requried to hold up to
- * trace_get_vcpu_event_count() entries.
- */
- unsigned long *trace_dstate;
+ /* Used for events with 'vcpu' and *without* the 'disabled' properties */
+ DECLARE_BITMAP(trace_dstate_delayed, CPU_TRACE_DSTATE_MAX_EVENTS);
+ DECLARE_BITMAP(trace_dstate, CPU_TRACE_DSTATE_MAX_EVENTS);
/* TODO Move common fields from CPUArchState here. */
int cpu_index; /* used by alpha TCG */
diff --git a/nbd/client.c b/nbd/client.c
index c3ee9f3..509ed5e 100644
--- a/nbd/client.c
+++ b/nbd/client.c
@@ -232,8 +232,7 @@
break;
case NBD_REP_ERR_UNKNOWN:
- error_setg(errp, "Requested export not available for option %" PRIx32
- " (%s)", reply->option, nbd_opt_lookup(reply->option));
+ error_setg(errp, "Requested export not available");
break;
case NBD_REP_ERR_SHUTDOWN:
@@ -253,7 +252,7 @@
}
if (msg) {
- error_append_hint(errp, "%s\n", msg);
+ error_append_hint(errp, "server reported: %s\n", msg);
}
cleanup:
@@ -902,7 +901,8 @@
uint8_t buf[NBD_REQUEST_SIZE];
trace_nbd_send_request(request->from, request->len, request->handle,
- request->flags, request->type);
+ request->flags, request->type,
+ nbd_cmd_lookup(request->type));
stl_be_p(buf, NBD_REQUEST_MAGIC);
stw_be_p(buf + 4, request->flags);
diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h
index 4065bc6..396ddb4 100644
--- a/nbd/nbd-internal.h
+++ b/nbd/nbd-internal.h
@@ -38,9 +38,13 @@
*/
/* Size of all NBD_OPT_*, without payload */
-#define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4)
+#define NBD_REQUEST_SIZE (4 + 2 + 2 + 8 + 8 + 4)
/* Size of all NBD_REP_* sent in answer to most NBD_OPT_*, without payload */
-#define NBD_REPLY_SIZE (4 + 4 + 8)
+#define NBD_REPLY_SIZE (4 + 4 + 8)
+/* Size of reply to NBD_OPT_EXPORT_NAME */
+#define NBD_REPLY_EXPORT_NAME_SIZE (8 + 2 + 124)
+/* Size of oldstyle negotiation */
+#define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124)
#define NBD_REQUEST_MAGIC 0x25609513
#define NBD_REPLY_MAGIC 0x67446698
diff --git a/nbd/server.c b/nbd/server.c
index 49ed574..82a78bf 100644
--- a/nbd/server.c
+++ b/nbd/server.c
@@ -283,12 +283,16 @@
Error **errp)
{
char name[NBD_MAX_NAME_SIZE + 1];
- char buf[8 + 4 + 124] = "";
+ char buf[NBD_REPLY_EXPORT_NAME_SIZE] = "";
size_t len;
int ret;
/* Client sends:
[20 .. xx] export name (length bytes)
+ Server replies:
+ [ 0 .. 7] size
+ [ 8 .. 9] export flags
+ [10 .. 133] reserved (0) [unless no_zeroes]
*/
trace_nbd_negotiate_handle_export_name();
if (length >= sizeof(name)) {
@@ -800,22 +804,21 @@
*/
static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp)
{
- char buf[8 + 8 + 8 + 128];
+ char buf[NBD_OLDSTYLE_NEGOTIATE_SIZE] = "";
int ret;
const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM |
NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA |
NBD_FLAG_SEND_WRITE_ZEROES);
bool oldStyle;
- /* Old style negotiation header without options
+ /* Old style negotiation header, no room for options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_CLIENT_MAGIC)
[16 .. 23] size
- [24 .. 25] server flags (0)
- [26 .. 27] export flags
+ [24 .. 27] export flags (zero-extended)
[28 .. 151] reserved (0)
- New style negotiation header with options
+ New style negotiation header, client can send options
[ 0 .. 7] passwd ("NBDMAGIC")
[ 8 .. 15] magic (NBD_OPTS_MAGIC)
[16 .. 17] server flags (0)
@@ -825,7 +828,6 @@
qio_channel_set_blocking(client->ioc, false, NULL);
trace_nbd_negotiate_begin();
- memset(buf, 0, sizeof(buf));
memcpy(buf, "NBDMAGIC", 8);
oldStyle = client->exp != NULL && !client->tlscreds;
@@ -834,7 +836,7 @@
client->exp->nbdflags | myflags);
stq_be_p(buf + 8, NBD_CLIENT_MAGIC);
stq_be_p(buf + 16, client->exp->size);
- stw_be_p(buf + 26, client->exp->nbdflags | myflags);
+ stl_be_p(buf + 24, client->exp->nbdflags | myflags);
if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) {
error_prepend(errp, "write failed: ");
diff --git a/nbd/trace-events b/nbd/trace-events
index f5024d8..d7c7746 100644
--- a/nbd/trace-events
+++ b/nbd/trace-events
@@ -28,7 +28,7 @@
nbd_client_loop_ret(int ret, const char *error) "NBD loop returned %d: %s"
nbd_client_clear_queue(void) "Clearing NBD queue"
nbd_client_clear_socket(void) "Clearing NBD socket"
-nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = %" PRIx16 ", .type = %" PRIu16 " }"
+nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name) "Sending request to server: { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = %" PRIx16 ", .type = %" PRIu16 " (%s) }"
nbd_receive_reply(uint32_t magic, int32_t error, uint64_t handle) "Got reply: { magic = 0x%" PRIx32 ", .error = % " PRId32 ", handle = %" PRIu64" }"
# nbd/server.c
diff --git a/net/colo-compare.c b/net/colo-compare.c
index abfc23c..ca67c68 100644
--- a/net/colo-compare.c
+++ b/net/colo-compare.c
@@ -73,6 +73,7 @@
CharBackend chr_out;
SocketReadState pri_rs;
SocketReadState sec_rs;
+ bool vnet_hdr;
/* connection list: the connections belonged to this NIC could be found
* in this list.
@@ -97,9 +98,10 @@
SECONDARY_IN,
};
-static int compare_chr_send(CharBackend *out,
+static int compare_chr_send(CompareState *s,
const uint8_t *buf,
- uint32_t size);
+ uint32_t size,
+ uint32_t vnet_hdr_len);
static gint seq_sorter(Packet *a, Packet *b, gpointer data)
{
@@ -121,9 +123,13 @@
Connection *conn;
if (mode == PRIMARY_IN) {
- pkt = packet_new(s->pri_rs.buf, s->pri_rs.packet_len);
+ pkt = packet_new(s->pri_rs.buf,
+ s->pri_rs.packet_len,
+ s->pri_rs.vnet_hdr_len);
} else {
- pkt = packet_new(s->sec_rs.buf, s->sec_rs.packet_len);
+ pkt = packet_new(s->sec_rs.buf,
+ s->sec_rs.packet_len,
+ s->sec_rs.vnet_hdr_len);
}
if (parse_packet_early(pkt)) {
@@ -195,8 +201,11 @@
sec_ip_src, sec_ip_dst);
}
+ offset = ppkt->vnet_hdr_len + offset;
+
if (ppkt->size == spkt->size) {
- return memcmp(ppkt->data + offset, spkt->data + offset,
+ return memcmp(ppkt->data + offset,
+ spkt->data + offset,
spkt->size - offset);
} else {
trace_colo_compare_main("Net packet size are not the same");
@@ -255,8 +264,9 @@
*/
if (ptcp->th_off > 5) {
ptrdiff_t tcp_offset;
+
tcp_offset = ppkt->transport_header - (uint8_t *)ppkt->data
- + (ptcp->th_off * 4);
+ + (ptcp->th_off * 4) - ppkt->vnet_hdr_len;
res = colo_packet_compare_common(ppkt, spkt, tcp_offset);
} else if (ptcp->th_sum == stcp->th_sum) {
res = colo_packet_compare_common(ppkt, spkt, ETH_HLEN);
@@ -479,7 +489,10 @@
}
if (result) {
- ret = compare_chr_send(&s->chr_out, pkt->data, pkt->size);
+ ret = compare_chr_send(s,
+ pkt->data,
+ pkt->size,
+ pkt->vnet_hdr_len);
if (ret < 0) {
error_report("colo_send_primary_packet failed");
}
@@ -500,9 +513,10 @@
}
}
-static int compare_chr_send(CharBackend *out,
+static int compare_chr_send(CompareState *s,
const uint8_t *buf,
- uint32_t size)
+ uint32_t size,
+ uint32_t vnet_hdr_len)
{
int ret = 0;
uint32_t len = htonl(size);
@@ -511,12 +525,24 @@
return 0;
}
- ret = qemu_chr_fe_write_all(out, (uint8_t *)&len, sizeof(len));
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
if (ret != sizeof(len)) {
goto err;
}
- ret = qemu_chr_fe_write_all(out, (uint8_t *)buf, size);
+ if (s->vnet_hdr) {
+ /*
+ * We send vnet header len make other module(like filter-redirector)
+ * know how to parse net packet correctly.
+ */
+ len = htonl(vnet_hdr_len);
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
+ if (ret != sizeof(len)) {
+ goto err;
+ }
+ }
+
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
if (ret != size) {
goto err;
}
@@ -655,13 +681,32 @@
s->outdev = g_strdup(value);
}
+static bool compare_get_vnet_hdr(Object *obj, Error **errp)
+{
+ CompareState *s = COLO_COMPARE(obj);
+
+ return s->vnet_hdr;
+}
+
+static void compare_set_vnet_hdr(Object *obj,
+ bool value,
+ Error **errp)
+{
+ CompareState *s = COLO_COMPARE(obj);
+
+ s->vnet_hdr = value;
+}
+
static void compare_pri_rs_finalize(SocketReadState *pri_rs)
{
CompareState *s = container_of(pri_rs, CompareState, pri_rs);
if (packet_enqueue(s, PRIMARY_IN)) {
trace_colo_compare_main("primary: unsupported packet in");
- compare_chr_send(&s->chr_out, pri_rs->buf, pri_rs->packet_len);
+ compare_chr_send(s,
+ pri_rs->buf,
+ pri_rs->packet_len,
+ pri_rs->vnet_hdr_len);
} else {
/* compare connection */
g_queue_foreach(&s->conn_list, colo_compare_connection, s);
@@ -743,8 +788,8 @@
return;
}
- net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize);
- net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize);
+ net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr);
+ net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr);
g_queue_init(&s->conn_list);
@@ -770,7 +815,10 @@
while (!g_queue_is_empty(&conn->primary_list)) {
pkt = g_queue_pop_head(&conn->primary_list);
- compare_chr_send(&s->chr_out, pkt->data, pkt->size);
+ compare_chr_send(s,
+ pkt->data,
+ pkt->size,
+ pkt->vnet_hdr_len);
packet_destroy(pkt, NULL);
}
while (!g_queue_is_empty(&conn->secondary_list)) {
@@ -788,6 +836,8 @@
static void colo_compare_init(Object *obj)
{
+ CompareState *s = COLO_COMPARE(obj);
+
object_property_add_str(obj, "primary_in",
compare_get_pri_indev, compare_set_pri_indev,
NULL);
@@ -797,6 +847,10 @@
object_property_add_str(obj, "outdev",
compare_get_outdev, compare_set_outdev,
NULL);
+
+ s->vnet_hdr = false;
+ object_property_add_bool(obj, "vnet_hdr_support", compare_get_vnet_hdr,
+ compare_set_vnet_hdr, NULL);
}
static void colo_compare_finalize(Object *obj)
diff --git a/net/colo.c b/net/colo.c
index 8cc166b..28ce7c8 100644
--- a/net/colo.c
+++ b/net/colo.c
@@ -43,11 +43,11 @@
{
int network_length;
static const uint8_t vlan[] = {0x81, 0x00};
- uint8_t *data = pkt->data;
+ uint8_t *data = pkt->data + pkt->vnet_hdr_len;
uint16_t l3_proto;
ssize_t l2hdr_len = eth_get_l2_hdr_length(data);
- if (pkt->size < ETH_HLEN) {
+ if (pkt->size < ETH_HLEN + pkt->vnet_hdr_len) {
trace_colo_proxy_main("pkt->size < ETH_HLEN");
return 1;
}
@@ -73,7 +73,7 @@
}
network_length = pkt->ip->ip_hl * 4;
- if (pkt->size < l2hdr_len + network_length) {
+ if (pkt->size < l2hdr_len + network_length + pkt->vnet_hdr_len) {
trace_colo_proxy_main("pkt->size < network_header + network_length");
return 1;
}
@@ -153,13 +153,14 @@
g_slice_free(Connection, conn);
}
-Packet *packet_new(const void *data, int size)
+Packet *packet_new(const void *data, int size, int vnet_hdr_len)
{
Packet *pkt = g_slice_new(Packet);
pkt->data = g_memdup(data, size);
pkt->size = size;
pkt->creation_ms = qemu_clock_get_ms(QEMU_CLOCK_HOST);
+ pkt->vnet_hdr_len = vnet_hdr_len;
return pkt;
}
diff --git a/net/colo.h b/net/colo.h
index 7c524f3..caedb0d 100644
--- a/net/colo.h
+++ b/net/colo.h
@@ -43,6 +43,8 @@
int size;
/* Time of packet creation, in wall clock ms */
int64_t creation_ms;
+ /* Get vnet_hdr_len from filter */
+ uint32_t vnet_hdr_len;
} Packet;
typedef struct ConnectionKey {
@@ -82,7 +84,7 @@
ConnectionKey *key,
GQueue *conn_list);
void connection_hashtable_reset(GHashTable *connection_track_table);
-Packet *packet_new(const void *data, int size);
+Packet *packet_new(const void *data, int size, int vnet_hdr_len);
void packet_destroy(void *opaque, void *user_data);
#endif /* QEMU_COLO_PROXY_H */
diff --git a/net/filter-mirror.c b/net/filter-mirror.c
index 6043549..90e2c92 100644
--- a/net/filter-mirror.c
+++ b/net/filter-mirror.c
@@ -41,12 +41,14 @@
CharBackend chr_in;
CharBackend chr_out;
SocketReadState rs;
+ bool vnet_hdr;
} MirrorState;
-static int filter_send(CharBackend *chr_out,
+static int filter_send(MirrorState *s,
const struct iovec *iov,
int iovcnt)
{
+ NetFilterState *nf = NETFILTER(s);
int ret = 0;
ssize_t size = 0;
uint32_t len = 0;
@@ -58,14 +60,31 @@
}
len = htonl(size);
- ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)&len, sizeof(len));
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
if (ret != sizeof(len)) {
goto err;
}
+ if (s->vnet_hdr) {
+ /*
+ * If vnet_hdr = on, we send vnet header len to make other
+ * module(like colo-compare) know how to parse net
+ * packet correctly.
+ */
+ ssize_t vnet_hdr_len;
+
+ vnet_hdr_len = nf->netdev->vnet_hdr_len;
+
+ len = htonl(vnet_hdr_len);
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)&len, sizeof(len));
+ if (ret != sizeof(len)) {
+ goto err;
+ }
+ }
+
buf = g_malloc(size);
iov_to_buf(iov, iovcnt, 0, buf, size);
- ret = qemu_chr_fe_write_all(chr_out, (uint8_t *)buf, size);
+ ret = qemu_chr_fe_write_all(&s->chr_out, (uint8_t *)buf, size);
g_free(buf);
if (ret != size) {
goto err;
@@ -141,7 +160,7 @@
MirrorState *s = FILTER_MIRROR(nf);
int ret;
- ret = filter_send(&s->chr_out, iov, iovcnt);
+ ret = filter_send(s, iov, iovcnt);
if (ret) {
error_report("filter mirror send failed(%s)", strerror(-ret));
}
@@ -164,7 +183,7 @@
int ret;
if (qemu_chr_fe_backend_connected(&s->chr_out)) {
- ret = filter_send(&s->chr_out, iov, iovcnt);
+ ret = filter_send(s, iov, iovcnt);
if (ret) {
error_report("filter redirector send failed(%s)", strerror(-ret));
}
@@ -229,7 +248,7 @@
}
}
- net_socket_rs_init(&s->rs, redirector_rs_finalize);
+ net_socket_rs_init(&s->rs, redirector_rs_finalize, s->vnet_hdr);
if (s->indev) {
chr = qemu_chr_find(s->indev);
@@ -318,6 +337,20 @@
}
}
+static bool filter_mirror_get_vnet_hdr(Object *obj, Error **errp)
+{
+ MirrorState *s = FILTER_MIRROR(obj);
+
+ return s->vnet_hdr;
+}
+
+static void filter_mirror_set_vnet_hdr(Object *obj, bool value, Error **errp)
+{
+ MirrorState *s = FILTER_MIRROR(obj);
+
+ s->vnet_hdr = value;
+}
+
static char *filter_redirector_get_outdev(Object *obj, Error **errp)
{
MirrorState *s = FILTER_REDIRECTOR(obj);
@@ -335,18 +368,48 @@
s->outdev = g_strdup(value);
}
+static bool filter_redirector_get_vnet_hdr(Object *obj, Error **errp)
+{
+ MirrorState *s = FILTER_REDIRECTOR(obj);
+
+ return s->vnet_hdr;
+}
+
+static void filter_redirector_set_vnet_hdr(Object *obj,
+ bool value,
+ Error **errp)
+{
+ MirrorState *s = FILTER_REDIRECTOR(obj);
+
+ s->vnet_hdr = value;
+}
+
static void filter_mirror_init(Object *obj)
{
+ MirrorState *s = FILTER_MIRROR(obj);
+
object_property_add_str(obj, "outdev", filter_mirror_get_outdev,
filter_mirror_set_outdev, NULL);
+
+ s->vnet_hdr = false;
+ object_property_add_bool(obj, "vnet_hdr_support",
+ filter_mirror_get_vnet_hdr,
+ filter_mirror_set_vnet_hdr, NULL);
}
static void filter_redirector_init(Object *obj)
{
+ MirrorState *s = FILTER_REDIRECTOR(obj);
+
object_property_add_str(obj, "indev", filter_redirector_get_indev,
filter_redirector_set_indev, NULL);
object_property_add_str(obj, "outdev", filter_redirector_get_outdev,
filter_redirector_set_outdev, NULL);
+
+ s->vnet_hdr = false;
+ object_property_add_bool(obj, "vnet_hdr_support",
+ filter_redirector_get_vnet_hdr,
+ filter_redirector_set_vnet_hdr, NULL);
}
static void filter_mirror_fini(Object *obj)
diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c
index afa06e8..55a6cf5 100644
--- a/net/filter-rewriter.c
+++ b/net/filter-rewriter.c
@@ -17,6 +17,7 @@
#include "qemu-common.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
+#include "qemu/error-report.h"
#include "qapi-visit.h"
#include "qom/object.h"
#include "qemu/main-loop.h"
@@ -33,6 +34,7 @@
NetQueue *incoming_queue;
/* hashtable to save connection */
GHashTable *connection_track_table;
+ bool vnet_hdr;
} RewriterState;
static void filter_rewriter_flush(NetFilterState *nf)
@@ -155,10 +157,16 @@
ConnectionKey key;
Packet *pkt;
ssize_t size = iov_size(iov, iovcnt);
+ ssize_t vnet_hdr_len = 0;
char *buf = g_malloc0(size);
iov_to_buf(iov, iovcnt, 0, buf, size);
- pkt = packet_new(buf, size);
+
+ if (s->vnet_hdr) {
+ vnet_hdr_len = nf->netdev->vnet_hdr_len;
+ }
+
+ pkt = packet_new(buf, size, vnet_hdr_len);
g_free(buf);
/*
@@ -237,6 +245,32 @@
s->incoming_queue = qemu_new_net_queue(qemu_netfilter_pass_to_next, nf);
}
+static bool filter_rewriter_get_vnet_hdr(Object *obj, Error **errp)
+{
+ RewriterState *s = FILTER_COLO_REWRITER(obj);
+
+ return s->vnet_hdr;
+}
+
+static void filter_rewriter_set_vnet_hdr(Object *obj,
+ bool value,
+ Error **errp)
+{
+ RewriterState *s = FILTER_COLO_REWRITER(obj);
+
+ s->vnet_hdr = value;
+}
+
+static void filter_rewriter_init(Object *obj)
+{
+ RewriterState *s = FILTER_COLO_REWRITER(obj);
+
+ s->vnet_hdr = false;
+ object_property_add_bool(obj, "vnet_hdr_support",
+ filter_rewriter_get_vnet_hdr,
+ filter_rewriter_set_vnet_hdr, NULL);
+}
+
static void colo_rewriter_class_init(ObjectClass *oc, void *data)
{
NetFilterClass *nfc = NETFILTER_CLASS(oc);
@@ -250,6 +284,7 @@
.name = TYPE_FILTER_REWRITER,
.parent = TYPE_NETFILTER,
.class_init = colo_rewriter_class_init,
+ .instance_init = filter_rewriter_init,
.instance_size = sizeof(RewriterState),
};
diff --git a/net/net.c b/net/net.c
index 6235aab..0e28099 100644
--- a/net/net.c
+++ b/net/net.c
@@ -492,6 +492,7 @@
return;
}
+ nc->vnet_hdr_len = len;
nc->info->set_vnet_hdr_len(nc, len);
}
@@ -1615,11 +1616,14 @@
};
void net_socket_rs_init(SocketReadState *rs,
- SocketReadStateFinalize *finalize)
+ SocketReadStateFinalize *finalize,
+ bool vnet_hdr)
{
rs->state = 0;
+ rs->vnet_hdr = vnet_hdr;
rs->index = 0;
rs->packet_len = 0;
+ rs->vnet_hdr_len = 0;
memset(rs->buf, 0, sizeof(rs->buf));
rs->finalize = finalize;
}
@@ -1634,8 +1638,12 @@
unsigned int l;
while (size > 0) {
- /* reassemble a packet from the network */
- switch (rs->state) { /* 0 = getting length, 1 = getting data */
+ /* Reassemble a packet from the network.
+ * 0 = getting length.
+ * 1 = getting vnet header length.
+ * 2 = getting data.
+ */
+ switch (rs->state) {
case 0:
l = 4 - rs->index;
if (l > size) {
@@ -1649,10 +1657,31 @@
/* got length */
rs->packet_len = ntohl(*(uint32_t *)rs->buf);
rs->index = 0;
- rs->state = 1;
+ if (rs->vnet_hdr) {
+ rs->state = 1;
+ } else {
+ rs->state = 2;
+ rs->vnet_hdr_len = 0;
+ }
}
break;
case 1:
+ l = 4 - rs->index;
+ if (l > size) {
+ l = size;
+ }
+ memcpy(rs->buf + rs->index, buf, l);
+ buf += l;
+ size -= l;
+ rs->index += l;
+ if (rs->index == 4) {
+ /* got vnet header length */
+ rs->vnet_hdr_len = ntohl(*(uint32_t *)rs->buf);
+ rs->index = 0;
+ rs->state = 2;
+ }
+ break;
+ case 2:
l = rs->packet_len - rs->index;
if (l > size) {
l = size;
diff --git a/net/socket.c b/net/socket.c
index dcae1ae..f85ef7d 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -174,7 +174,7 @@
closesocket(s->fd);
s->fd = -1;
- net_socket_rs_init(&s->rs, net_socket_rs_finalize);
+ net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
s->nc.link_down = true;
memset(s->nc.info_str, 0, sizeof(s->nc.info_str));
@@ -366,7 +366,7 @@
s->fd = fd;
s->listen_fd = -1;
s->send_fn = net_socket_send_dgram;
- net_socket_rs_init(&s->rs, net_socket_rs_finalize);
+ net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
net_socket_read_poll(s, true);
/* mcast: save bound address as dst */
@@ -417,7 +417,7 @@
s->fd = fd;
s->listen_fd = -1;
- net_socket_rs_init(&s->rs, net_socket_rs_finalize);
+ net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
/* Disable Nagle algorithm on TCP sockets to reduce latency */
socket_set_nodelay(fd);
@@ -522,7 +522,7 @@
s->fd = -1;
s->listen_fd = fd;
s->nc.link_down = true;
- net_socket_rs_init(&s->rs, net_socket_rs_finalize);
+ net_socket_rs_init(&s->rs, net_socket_rs_finalize, false);
qemu_set_fd_handler(s->listen_fd, net_socket_accept, NULL, s);
return 0;
diff --git a/qapi-schema.json b/qapi-schema.json
index ab438ea..8b015be 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3051,10 +3051,15 @@
#
# @name: the type name found in the search
#
+# @abstract: the type is abstract and can't be directly instantiated.
+# Omitted if false. (since 2.10)
+#
+# @parent: Name of parent type, if any (since 2.10)
+#
# Since: 1.1
##
{ 'struct': 'ObjectTypeInfo',
- 'data': { 'name': 'str' } }
+ 'data': { 'name': 'str', '*abstract': 'bool', '*parent': 'str' } }
##
# @qom-list-types:
diff --git a/qemu-options.hx b/qemu-options.hx
index 9bd6bf0..746b5fa 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4249,26 +4249,25 @@
@option{tx}: the filter is attached to the transmit queue of the netdev,
where it will receive packets sent by the netdev.
-@item -object filter-mirror,id=@var{id},netdev=@var{netdevid},outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
+@item -object filter-mirror,id=@var{id},netdev=@var{netdevid},outdev=@var{chardevid},queue=@var{all|rx|tx}[,vnet_hdr_support]
-filter-mirror on netdev @var{netdevid},mirror net packet to chardev
-@var{chardevid}
+filter-mirror on netdev @var{netdevid},mirror net packet to chardev@var{chardevid}, if it has the vnet_hdr_support flag, filter-mirror will mirror packet with vnet_hdr_len.
-@item -object filter-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid},
-outdev=@var{chardevid}[,queue=@var{all|rx|tx}]
+@item -object filter-redirector,id=@var{id},netdev=@var{netdevid},indev=@var{chardevid},outdev=@var{chardevid},queue=@var{all|rx|tx}[,vnet_hdr_support]
filter-redirector on netdev @var{netdevid},redirect filter's net packet to chardev
-@var{chardevid},and redirect indev's packet to filter.
+@var{chardevid},and redirect indev's packet to filter.if it has the vnet_hdr_support flag,
+filter-redirector will redirect packet with vnet_hdr_len.
Create a filter-redirector we need to differ outdev id from indev id, id can not
be the same. we can just use indev or outdev, but at least one of indev or outdev
need to be specified.
-@item -object filter-rewriter,id=@var{id},netdev=@var{netdevid}[,queue=@var{all|rx|tx}]
+@item -object filter-rewriter,id=@var{id},netdev=@var{netdevid},queue=@var{all|rx|tx},[vnet_hdr_support]
Filter-rewriter is a part of COLO project.It will rewrite tcp packet to
secondary from primary to keep secondary tcp connection,and rewrite
tcp packet to primary from secondary make tcp packet can be handled by
-client.
+client.if it has the vnet_hdr_support flag, we can parse packet with vnet header.
usage:
colo secondary:
@@ -4283,13 +4282,13 @@
The file format is libpcap, so it can be analyzed with tools such as tcpdump
or Wireshark.
-@item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid},
-outdev=@var{chardevid}
+@item -object colo-compare,id=@var{id},primary_in=@var{chardevid},secondary_in=@var{chardevid},outdev=@var{chardevid}[,vnet_hdr_support]
Colo-compare gets packet from primary_in@var{chardevid} and secondary_in@var{chardevid}, than compare primary packet with
secondary packet. If the packets are same, we will output primary
packet to outdev@var{chardevid}, else we will notify colo-frame
do checkpoint and send primary packet to outdev@var{chardevid}.
+if it has the vnet_hdr_support flag, colo compare will send/recv packet with vnet_hdr_len.
we must use it with the help of filter-mirror and filter-redirector.
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index d8e4122..ab0c63d 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -13,9 +13,9 @@
#include "qemu/osdep.h"
#include <sys/ioctl.h>
+#include <sys/utsname.h>
#include <sys/wait.h>
#include <dirent.h>
-#include <utmpx.h>
#include "qga/guest-agent-core.h"
#include "qga-qmp-commands.h"
#include "qapi/qmp/qerror.h"
@@ -25,6 +25,10 @@
#include "qemu/base64.h"
#include "qemu/cutils.h"
+#ifdef HAVE_UTMPX
+#include <utmpx.h>
+#endif
+
#ifndef CONFIG_HAS_ENVIRON
#ifdef __APPLE__
#include <crt_externs.h>
@@ -2519,6 +2523,8 @@
#endif
}
+#ifdef HAVE_UTMPX
+
#define QGA_MICRO_SECOND_TO_SECOND 1000000
static double ga_get_login_time(struct utmpx *user_info)
@@ -2577,3 +2583,152 @@
g_hash_table_destroy(cache);
return head;
}
+
+#else
+
+GuestUserList *qmp_guest_get_users(Error **errp)
+{
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
+}
+
+#endif
+
+/* Replace escaped special characters with theire real values. The replacement
+ * is done in place -- returned value is in the original string.
+ */
+static void ga_osrelease_replace_special(gchar *value)
+{
+ gchar *p, *p2, quote;
+
+ /* Trim the string at first space or semicolon if it is not enclosed in
+ * single or double quotes. */
+ if ((value[0] != '"') || (value[0] == '\'')) {
+ p = strchr(value, ' ');
+ if (p != NULL) {
+ *p = 0;
+ }
+ p = strchr(value, ';');
+ if (p != NULL) {
+ *p = 0;
+ }
+ return;
+ }
+
+ quote = value[0];
+ p2 = value;
+ p = value + 1;
+ while (*p != 0) {
+ if (*p == '\\') {
+ p++;
+ switch (*p) {
+ case '$':
+ case '\'':
+ case '"':
+ case '\\':
+ case '`':
+ break;
+ default:
+ /* Keep literal backslash followed by whatever is there */
+ p--;
+ break;
+ }
+ } else if (*p == quote) {
+ *p2 = 0;
+ break;
+ }
+ *(p2++) = *(p++);
+ }
+}
+
+static GKeyFile *ga_parse_osrelease(const char *fname)
+{
+ gchar *content = NULL;
+ gchar *content2 = NULL;
+ GError *err = NULL;
+ GKeyFile *keys = g_key_file_new();
+ const char *group = "[os-release]\n";
+
+ if (!g_file_get_contents(fname, &content, NULL, &err)) {
+ slog("failed to read '%s', error: %s", fname, err->message);
+ goto fail;
+ }
+
+ if (!g_utf8_validate(content, -1, NULL)) {
+ slog("file is not utf-8 encoded: %s", fname);
+ goto fail;
+ }
+ content2 = g_strdup_printf("%s%s", group, content);
+
+ if (!g_key_file_load_from_data(keys, content2, -1, G_KEY_FILE_NONE,
+ &err)) {
+ slog("failed to parse file '%s', error: %s", fname, err->message);
+ goto fail;
+ }
+
+ g_free(content);
+ g_free(content2);
+ return keys;
+
+fail:
+ g_error_free(err);
+ g_free(content);
+ g_free(content2);
+ g_key_file_free(keys);
+ return NULL;
+}
+
+GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
+{
+ GuestOSInfo *info = NULL;
+ struct utsname kinfo;
+ GKeyFile *osrelease = NULL;
+ const char *qga_os_release = g_getenv("QGA_OS_RELEASE");
+
+ info = g_new0(GuestOSInfo, 1);
+
+ if (uname(&kinfo) != 0) {
+ error_setg_errno(errp, errno, "uname failed");
+ } else {
+ info->has_kernel_version = true;
+ info->kernel_version = g_strdup(kinfo.version);
+ info->has_kernel_release = true;
+ info->kernel_release = g_strdup(kinfo.release);
+ info->has_machine = true;
+ info->machine = g_strdup(kinfo.machine);
+ }
+
+ if (qga_os_release != NULL) {
+ osrelease = ga_parse_osrelease(qga_os_release);
+ } else {
+ osrelease = ga_parse_osrelease("/etc/os-release");
+ if (osrelease == NULL) {
+ osrelease = ga_parse_osrelease("/usr/lib/os-release");
+ }
+ }
+
+ if (osrelease != NULL) {
+ char *value;
+
+#define GET_FIELD(field, osfield) do { \
+ value = g_key_file_get_value(osrelease, "os-release", osfield, NULL); \
+ if (value != NULL) { \
+ ga_osrelease_replace_special(value); \
+ info->has_ ## field = true; \
+ info->field = value; \
+ } \
+} while (0)
+ GET_FIELD(id, "ID");
+ GET_FIELD(name, "NAME");
+ GET_FIELD(pretty_name, "PRETTY_NAME");
+ GET_FIELD(version, "VERSION");
+ GET_FIELD(version_id, "VERSION_ID");
+ GET_FIELD(variant, "VARIANT");
+ GET_FIELD(variant_id, "VARIANT_ID");
+#undef GET_FIELD
+
+ g_key_file_free(osrelease);
+ }
+
+ return info;
+}
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 6f16457..619dbd2 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1642,3 +1642,194 @@
return NULL;
#endif
}
+
+typedef struct _ga_matrix_lookup_t {
+ int major;
+ int minor;
+ char const *version;
+ char const *version_id;
+} ga_matrix_lookup_t;
+
+static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = {
+ {
+ /* Desktop editions */
+ { 5, 0, "Microsoft Windows 2000", "2000"},
+ { 5, 1, "Microsoft Windows XP", "xp"},
+ { 6, 0, "Microsoft Windows Vista", "vista"},
+ { 6, 1, "Microsoft Windows 7" "7"},
+ { 6, 2, "Microsoft Windows 8", "8"},
+ { 6, 3, "Microsoft Windows 8.1", "8.1"},
+ {10, 0, "Microsoft Windows 10", "10"},
+ { 0, 0, 0}
+ },{
+ /* Server editions */
+ { 5, 2, "Microsoft Windows Server 2003", "2003"},
+ { 6, 0, "Microsoft Windows Server 2008", "2008"},
+ { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"},
+ { 6, 2, "Microsoft Windows Server 2012", "2012"},
+ { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"},
+ {10, 0, "Microsoft Windows Server 2016", "2016"},
+ { 0, 0, 0},
+ { 0, 0, 0}
+ }
+};
+
+static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp)
+{
+ typedef NTSTATUS(WINAPI * rtl_get_version_t)(
+ RTL_OSVERSIONINFOEXW *os_version_info_ex);
+
+ info->dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
+
+ HMODULE module = GetModuleHandle("ntdll");
+ PVOID fun = GetProcAddress(module, "RtlGetVersion");
+ if (fun == NULL) {
+ error_setg(errp, QERR_QGA_COMMAND_FAILED,
+ "Failed to get address of RtlGetVersion");
+ return;
+ }
+
+ rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun;
+ rtl_get_version(info);
+ return;
+}
+
+static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id)
+{
+ DWORD major = os_version->dwMajorVersion;
+ DWORD minor = os_version->dwMinorVersion;
+ int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION);
+ ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx];
+ while (table->version != NULL) {
+ if (major == table->major && minor == table->minor) {
+ if (id) {
+ return g_strdup(table->version_id);
+ } else {
+ return g_strdup(table->version);
+ }
+ }
+ ++table;
+ }
+ slog("failed to lookup Windows version: major=%lu, minor=%lu",
+ major, minor);
+ return g_strdup("N/A");
+}
+
+static char *ga_get_win_product_name(Error **errp)
+{
+ HKEY key = NULL;
+ DWORD size = 128;
+ char *result = g_malloc0(size);
+ LONG err = ERROR_SUCCESS;
+
+ err = RegOpenKeyA(HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ &key);
+ if (err != ERROR_SUCCESS) {
+ error_setg_win32(errp, err, "failed to open registry key");
+ goto fail;
+ }
+
+ err = RegQueryValueExA(key, "ProductName", NULL, NULL,
+ (LPBYTE)result, &size);
+ if (err == ERROR_MORE_DATA) {
+ slog("ProductName longer than expected (%lu bytes), retrying",
+ size);
+ g_free(result);
+ result = NULL;
+ if (size > 0) {
+ result = g_malloc0(size);
+ err = RegQueryValueExA(key, "ProductName", NULL, NULL,
+ (LPBYTE)result, &size);
+ }
+ }
+ if (err != ERROR_SUCCESS) {
+ error_setg_win32(errp, err, "failed to retrive ProductName");
+ goto fail;
+ }
+
+ return result;
+
+fail:
+ g_free(result);
+ return NULL;
+}
+
+static char *ga_get_current_arch(void)
+{
+ SYSTEM_INFO info;
+ GetNativeSystemInfo(&info);
+ char *result = NULL;
+ switch (info.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ result = g_strdup("x86_64");
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ result = g_strdup("arm");
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ result = g_strdup("ia64");
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ result = g_strdup("x86");
+ break;
+ case PROCESSOR_ARCHITECTURE_UNKNOWN:
+ default:
+ slog("unknown processor architecture 0x%0x",
+ info.wProcessorArchitecture);
+ result = g_strdup("unknown");
+ break;
+ }
+ return result;
+}
+
+GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
+{
+ Error *local_err = NULL;
+ OSVERSIONINFOEXW os_version = {0};
+ bool server;
+ char *product_name;
+ GuestOSInfo *info;
+
+ ga_get_win_version(&os_version, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ server = os_version.wProductType != VER_NT_WORKSTATION;
+ product_name = ga_get_win_product_name(&local_err);
+ if (product_name == NULL) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ info = g_new0(GuestOSInfo, 1);
+
+ info->has_kernel_version = true;
+ info->kernel_version = g_strdup_printf("%lu.%lu",
+ os_version.dwMajorVersion,
+ os_version.dwMinorVersion);
+ info->has_kernel_release = true;
+ info->kernel_release = g_strdup_printf("%lu",
+ os_version.dwBuildNumber);
+ info->has_machine = true;
+ info->machine = ga_get_current_arch();
+
+ info->has_id = true;
+ info->id = g_strdup("mswindows");
+ info->has_name = true;
+ info->name = g_strdup("Microsoft Windows");
+ info->has_pretty_name = true;
+ info->pretty_name = product_name;
+ info->has_version = true;
+ info->version = ga_get_win_name(&os_version, false);
+ info->has_version_id = true;
+ info->version_id = ga_get_win_name(&os_version, true);
+ info->has_variant = true;
+ info->variant = g_strdup(server ? "server" : "client");
+ info->has_variant_id = true;
+ info->variant_id = g_strdup(server ? "server" : "client");
+
+ return info;
+}
diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs
index fa2260c..5af1162 100644
--- a/qga/installer/qemu-ga.wxs
+++ b/qga/installer/qemu-ga.wxs
@@ -125,6 +125,9 @@
<Component Id="libwinpthread" Guid="{6C117C78-0F47-4B07-8F34-6BEE11643829}">
<File Id="libwinpthread_1.dll" Name="libwinpthread-1.dll" Source="$(var.Mingw_bin)/libwinpthread-1.dll" KeyPath="yes" DiskId="1"/>
</Component>
+ <Component Id="libpcre" Guid="{7A86B45E-A009-489A-A849-CE3BACF03CD0}">
+ <File Id="libpcre_1.dll" Name="libpcre-1.dll" Source="$(var.Mingw_bin)/libpcre-1.dll" KeyPath="yes" DiskId="1"/>
+ </Component>
<Component Id="registry_entries" Guid="{D075D109-51CA-11E3-9F8B-000C29858960}">
<RegistryKey Root="HKLM"
Key="Software\$(env.QEMU_GA_MANUFACTURER)\$(env.QEMU_GA_DISTRO)\Tools\QemuGA">
@@ -173,6 +176,7 @@
<ComponentRef Id="libssp" />
<ComponentRef Id="libwinpthread" />
<ComponentRef Id="registry_entries" />
+ <ComponentRef Id="libpcre" />
</Feature>
<InstallExecuteSequence>
diff --git a/qga/main.c b/qga/main.c
index 405c129..1b381d0 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1074,7 +1074,12 @@
g_free(tmp);
tmp = g_key_file_to_data(keyfile, NULL, &error);
- printf("%s", tmp);
+ if (error) {
+ g_critical("Failed to dump keyfile: %s", error->message);
+ g_clear_error(&error);
+ } else {
+ printf("%s", tmp);
+ }
g_free(tmp);
g_key_file_free(keyfile);
@@ -1314,7 +1319,7 @@
ga_command_state_init(s, s->command_state);
ga_command_state_init_all(s->command_state);
json_message_parser_init(&s->parser, process_event);
- ga_state = s;
+
#ifndef _WIN32
if (!register_signal_handlers()) {
g_critical("failed to register signal handlers");
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 03743ab..90a0c86 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1126,3 +1126,68 @@
##
{ 'command': 'guest-get-timezone',
'returns': 'GuestTimezone' }
+
+##
+# @GuestOSInfo:
+#
+# @kernel-release:
+# * POSIX: release field returned by uname(2)
+# * Windows: version number of the OS
+# @kernel-version:
+# * POSIX: version field returned by uname(2)
+# * Windows: build number of the OS
+# @machine:
+# * POSIX: machine field returned by uname(2)
+# * Windows: one of x86, x86_64, arm, ia64
+# @id:
+# * POSIX: as defined by os-release(5)
+# * Windows: contains string "mswindows"
+# @name:
+# * POSIX: as defined by os-release(5)
+# * Windows: contains string "Microsoft Windows"
+# @pretty-name:
+# * POSIX: as defined by os-release(5)
+# * Windows: product name, e.g. "Microsoft Windows 10 Enterprise"
+# @version:
+# * POSIX: as defined by os-release(5)
+# * Windows: long version string, e.g. "Microsoft Windows Server 2008"
+# @version-id:
+# * POSIX: as defined by os-release(5)
+# * Windows: short version identifier, e.g. "7" or "20012r2"
+# @variant:
+# * POSIX: as defined by os-release(5)
+# * Windows: contains string "server" or "client"
+# @variant-id:
+# * POSIX: as defined by os-release(5)
+# * Windows: contains string "server" or "client"
+#
+# Notes:
+#
+# On POSIX systems the fields @id, @name, @pretty-name, @version, @version-id,
+# @variant and @variant-id follow the definition specified in os-release(5).
+# Refer to the manual page for exact description of the fields. Their values
+# are taken from the os-release file. If the file is not present in the system,
+# or the values are not present in the file, the fields are not included.
+#
+# On Windows the values are filled from information gathered from the system.
+#
+# Since: 2.10
+##
+{ 'struct': 'GuestOSInfo',
+ 'data': {
+ '*kernel-release': 'str', '*kernel-version': 'str',
+ '*machine': 'str', '*id': 'str', '*name': 'str',
+ '*pretty-name': 'str', '*version': 'str', '*version-id': 'str',
+ '*variant': 'str', '*variant-id': 'str' } }
+
+##
+# @guest-get-osinfo:
+#
+# Retrieve guest operating system information
+#
+# Returns: @GuestOSInfo
+#
+# Since: 2.10
+##
+{ 'command': 'guest-get-osinfo',
+ 'returns': 'GuestOSInfo' }
diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp
index f41fcdf..ba7c94e 100644
--- a/qga/vss-win32/install.cpp
+++ b/qga/vss-win32/install.cpp
@@ -18,6 +18,9 @@
#include <wbemidl.h>
#include <comdef.h>
#include <comutil.h>
+#include <sddl.h>
+
+#define BUFFER_SIZE 1024
extern HINSTANCE g_hinstDll;
@@ -135,6 +138,27 @@
return hr;
}
+/* Acquire group or user name by SID */
+static HRESULT getNameByStringSID(
+ const wchar_t *sid, LPWSTR buffer, LPDWORD bufferLen)
+{
+ HRESULT hr = S_OK;
+ PSID psid = NULL;
+ SID_NAME_USE groupType;
+ DWORD domainNameLen = BUFFER_SIZE;
+ wchar_t domainName[BUFFER_SIZE];
+
+ chk(ConvertStringSidToSidW(sid, &psid));
+ LookupAccountSidW(NULL, psid, buffer, bufferLen,
+ domainName, &domainNameLen, &groupType);
+ hr = HRESULT_FROM_WIN32(GetLastError());
+
+ LocalFree(psid);
+
+out:
+ return hr;
+}
+
/* Find and iterate QGA VSS provider in COM+ Application Catalog */
static HRESULT QGAProviderFind(
HRESULT (*found)(ICatalogCollection *, int, void *), void *arg)
@@ -216,6 +240,10 @@
CHAR dllPath[MAX_PATH], tlbPath[MAX_PATH];
bool unregisterOnFailure = false;
int count = 0;
+ DWORD bufferLen = BUFFER_SIZE;
+ wchar_t buffer[BUFFER_SIZE];
+ const wchar_t *administratorsGroupSID = L"S-1-5-32-544";
+ const wchar_t *systemUserSID = L"S-1-5-18";
if (!g_hinstDll) {
errmsg(E_FAIL, "Failed to initialize DLL");
@@ -284,11 +312,12 @@
/* Setup roles of the applicaion */
+ chk(getNameByStringSID(administratorsGroupSID, buffer, &bufferLen));
chk(pApps->GetCollection(_bstr_t(L"Roles"), key,
(IDispatch **)pRoles.replace()));
chk(pRoles->Populate());
chk(pRoles->Add((IDispatch **)pObj.replace()));
- chk(put_Value(pObj, L"Name", L"Administrators"));
+ chk(put_Value(pObj, L"Name", buffer));
chk(put_Value(pObj, L"Description", L"Administrators group"));
chk(pRoles->SaveChanges(&n));
chk(pObj->get_Key(&key));
@@ -303,8 +332,10 @@
chk(GetAdminName(&name));
chk(put_Value(pObj, L"User", _bstr_t(".\\") + name));
+ bufferLen = BUFFER_SIZE;
+ chk(getNameByStringSID(systemUserSID, buffer, &bufferLen));
chk(pUsersInRole->Add((IDispatch **)pObj.replace()));
- chk(put_Value(pObj, L"User", L"SYSTEM"));
+ chk(put_Value(pObj, L"User", buffer));
chk(pUsersInRole->SaveChanges(&n));
out:
diff --git a/qmp.c b/qmp.c
index 2cd40c3..b86201e 100644
--- a/qmp.c
+++ b/qmp.c
@@ -430,9 +430,15 @@
{
ObjectTypeInfoList *e, **pret = data;
ObjectTypeInfo *info;
+ ObjectClass *parent = object_class_get_parent(klass);
info = g_malloc0(sizeof(*info));
info->name = g_strdup(object_class_get_name(klass));
+ info->has_abstract = info->abstract = object_class_is_abstract(klass);
+ if (parent) {
+ info->has_parent = true;
+ info->parent = g_strdup(object_class_get_name(parent));
+ }
e = g_malloc0(sizeof(*e));
e->value = info;
diff --git a/qom/cpu.c b/qom/cpu.c
index a39ff6c..4f38db0 100644
--- a/qom/cpu.c
+++ b/qom/cpu.c
@@ -380,7 +380,6 @@
static void cpu_common_initfn(Object *obj)
{
- uint32_t count;
CPUState *cpu = CPU(obj);
CPUClass *cc = CPU_GET_CLASS(obj);
@@ -395,18 +394,11 @@
QTAILQ_INIT(&cpu->breakpoints);
QTAILQ_INIT(&cpu->watchpoints);
- count = trace_get_vcpu_event_count();
- if (count) {
- cpu->trace_dstate = bitmap_new(count);
- }
-
cpu_exec_initfn(cpu);
}
static void cpu_common_finalize(Object *obj)
{
- CPUState *cpu = CPU(obj);
- g_free(cpu->trace_dstate);
}
static int64_t cpu_common_get_arch_id(CPUState *cpu)
diff --git a/qom/object.c b/qom/object.c
index dfdbd50..fe6e744 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1712,15 +1712,13 @@
typename, ambiguous);
if (found) {
if (obj) {
- if (ambiguous) {
- *ambiguous = true;
- }
+ *ambiguous = true;
return NULL;
}
obj = found;
}
- if (ambiguous && *ambiguous) {
+ if (*ambiguous) {
return NULL;
}
}
@@ -1729,7 +1727,7 @@
}
Object *object_resolve_path_type(const char *path, const char *typename,
- bool *ambiguous)
+ bool *ambiguousp)
{
Object *obj;
gchar **parts;
@@ -1738,11 +1736,12 @@
assert(parts);
if (parts[0] == NULL || strcmp(parts[0], "") != 0) {
- if (ambiguous) {
- *ambiguous = false;
- }
+ bool ambiguous = false;
obj = object_resolve_partial_path(object_get_root(), parts,
- typename, ambiguous);
+ typename, &ambiguous);
+ if (ambiguousp) {
+ *ambiguousp = ambiguous;
+ }
} else {
obj = object_resolve_abs_path(object_get_root(), parts, typename, 1);
}
diff --git a/scripts/device-crash-test b/scripts/device-crash-test
index 5f90e9b..e77b693 100755
--- a/scripts/device-crash-test
+++ b/scripts/device-crash-test
@@ -219,7 +219,7 @@
{'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR},
{'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR},
{'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR},
- {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat || se->instance_id == 0' failed", 'loglevel':logging.ERROR},
+ {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR},
{'exitcode':-11, 'device':'stm32f205-soc', 'loglevel':logging.ERROR, 'expected':True},
{'exitcode':-11, 'device':'xlnx,zynqmp', 'loglevel':logging.ERROR, 'expected':True},
{'exitcode':-11, 'device':'mips-cps', 'loglevel':logging.ERROR, 'expected':True},
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
index 1ffbc1d..d4c204a 100644
--- a/scripts/tracetool/__init__.py
+++ b/scripts/tracetool/__init__.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -268,6 +268,7 @@
return self._FMT.findall(self.fmt)
QEMU_TRACE = "trace_%(name)s"
+ QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE
QEMU_TRACE_TCG = QEMU_TRACE + "_tcg"
QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE"
QEMU_EVENT = "_TRACE_%(NAME)s_EVENT"
diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py
index c469cbd..c6812b7 100644
--- a/scripts/tracetool/backend/dtrace.py
+++ b/scripts/tracetool/backend/dtrace.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -46,6 +46,6 @@
def generate_h(event, group):
- out(' QEMU_%(uppername)s(%(argnames)s);',
+ out(' QEMU_%(uppername)s(%(argnames)s);',
uppername=event.name.upper(),
argnames=", ".join(event.args.names()))
diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py
index db9fe7a..dd0eda4 100644
--- a/scripts/tracetool/backend/ftrace.py
+++ b/scripts/tracetool/backend/ftrace.py
@@ -29,17 +29,17 @@
if len(event.args) > 0:
argnames = ", " + argnames
- out(' {',
- ' char ftrace_buf[MAX_TRACE_STRLEN];',
- ' int unused __attribute__ ((unused));',
- ' int trlen;',
- ' if (trace_event_get_state(%(event_id)s)) {',
- ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,',
- ' "%(name)s " %(fmt)s "\\n" %(argnames)s);',
- ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);',
- ' unused = write(trace_marker_fd, ftrace_buf, trlen);',
- ' }',
+ out(' {',
+ ' char ftrace_buf[MAX_TRACE_STRLEN];',
+ ' int unused __attribute__ ((unused));',
+ ' int trlen;',
+ ' if (trace_event_get_state(%(event_id)s)) {',
+ ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,',
+ ' "%(name)s " %(fmt)s "\\n" %(argnames)s);',
+ ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);',
+ ' unused = write(trace_marker_fd, ftrace_buf, trlen);',
' }',
+ ' }',
name=event.name,
args=event.args,
event_id="TRACE_" + event.name.upper(),
diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py
index 4f4a4d3..54f0a69 100644
--- a/scripts/tracetool/backend/log.py
+++ b/scripts/tracetool/backend/log.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -35,14 +35,15 @@
else:
cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper())
- out(' if (%(cond)s) {',
- ' struct timeval _now;',
- ' gettimeofday(&_now, NULL);',
- ' qemu_log_mask(LOG_TRACE, "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",',
- ' getpid(),',
- ' (size_t)_now.tv_sec, (size_t)_now.tv_usec',
- ' %(argnames)s);',
- ' }',
+ out(' if (%(cond)s) {',
+ ' struct timeval _now;',
+ ' gettimeofday(&_now, NULL);',
+ ' qemu_log_mask(LOG_TRACE,',
+ ' "%%d@%%zd.%%06zd:%(name)s " %(fmt)s "\\n",',
+ ' getpid(),',
+ ' (size_t)_now.tv_sec, (size_t)_now.tv_usec',
+ ' %(argnames)s);',
+ ' }',
cond=cond,
name=event.name,
fmt=event.fmt.rstrip("\n"),
diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py
index 4acc06e..f983670 100644
--- a/scripts/tracetool/backend/simple.py
+++ b/scripts/tracetool/backend/simple.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -37,7 +37,7 @@
def generate_h(event, group):
- out(' _simple_%(api)s(%(args)s);',
+ out(' _simple_%(api)s(%(args)s);',
api=event.api(),
args=", ".join(event.args.names()))
diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py
index b8ff279..1ce627f 100644
--- a/scripts/tracetool/backend/syslog.py
+++ b/scripts/tracetool/backend/syslog.py
@@ -35,9 +35,9 @@
else:
cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper())
- out(' if (%(cond)s) {',
- ' syslog(LOG_INFO, "%(name)s " %(fmt)s %(argnames)s);',
- ' }',
+ out(' if (%(cond)s) {',
+ ' syslog(LOG_INFO, "%(name)s " %(fmt)s %(argnames)s);',
+ ' }',
cond=cond,
name=event.name,
fmt=event.fmt.rstrip("\n"),
diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py
index 52ce892..2adaf54 100644
--- a/scripts/tracetool/backend/ust.py
+++ b/scripts/tracetool/backend/ust.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -35,6 +35,6 @@
if len(event.args) > 0:
argnames = ", " + argnames
- out(' tracepoint(qemu, %(name)s%(tp_args)s);',
+ out(' tracepoint(qemu, %(name)s%(tp_args)s);',
name=event.name,
tp_args=argnames)
diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py
index 3682f4e..aecf249 100644
--- a/scripts/tracetool/format/h.py
+++ b/scripts/tracetool/format/h.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -49,6 +49,19 @@
backend.generate_begin(events, group)
for e in events:
+ # tracer without checks
+ out('',
+ 'static inline void %(api)s(%(args)s)',
+ '{',
+ api=e.api(e.QEMU_TRACE_NOCHECK),
+ args=e.args)
+
+ if "disable" not in e.properties:
+ backend.generate(e, group)
+
+ out('}')
+
+ # tracer wrapper with checks (per-vCPU tracing)
if "vcpu" in e.properties:
trace_cpu = next(iter(e.args))[1]
cond = "trace_event_get_vcpu_state(%(cpu)s,"\
@@ -63,16 +76,15 @@
'static inline void %(api)s(%(args)s)',
'{',
' if (%(cond)s) {',
+ ' %(api_nocheck)s(%(names)s);',
+ ' }',
+ '}',
api=e.api(),
+ api_nocheck=e.api(e.QEMU_TRACE_NOCHECK),
args=e.args,
+ names=", ".join(e.args.names()),
cond=cond)
- if "disable" not in e.properties:
- backend.generate(e, group)
-
- out(' }',
- '}')
-
backend.generate_end(events, group)
out('#endif /* TRACE_%s_GENERATED_TRACERS_H */' % group.upper())
diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py
index db55f52..1651cc3 100644
--- a/scripts/tracetool/format/tcg_h.py
+++ b/scripts/tracetool/format/tcg_h.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -46,7 +46,7 @@
for e in events:
# just keep one of them
- if "tcg-trans" not in e.properties:
+ if "tcg-exec" not in e.properties:
continue
out('static inline void %(name_tcg)s(%(args)s)',
@@ -58,12 +58,25 @@
args_trans = e.original.event_trans.args
args_exec = tracetool.vcpu.transform_args(
"tcg_helper_c", e.original.event_exec, "wrapper")
+ if "vcpu" in e.properties:
+ trace_cpu = e.args.names()[0]
+ cond = "trace_event_get_vcpu_state(%(cpu)s,"\
+ " TRACE_%(id)s)"\
+ % dict(
+ cpu=trace_cpu,
+ id=e.original.event_exec.name.upper())
+ else:
+ cond = "true"
+
out(' %(name_trans)s(%(argnames_trans)s);',
- ' gen_helper_%(name_exec)s(%(argnames_exec)s);',
+ ' if (%(cond)s) {',
+ ' gen_helper_%(name_exec)s(%(argnames_exec)s);',
+ ' }',
name_trans=e.original.event_trans.api(e.QEMU_TRACE),
name_exec=e.original.event_exec.api(e.QEMU_TRACE),
argnames_trans=", ".join(args_trans.names()),
- argnames_exec=", ".join(args_exec.names()))
+ argnames_exec=", ".join(args_exec.names()),
+ cond=cond)
out('}')
diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py
index ec7acbe..bbbd6ad 100644
--- a/scripts/tracetool/format/tcg_helper_c.py
+++ b/scripts/tracetool/format/tcg_helper_c.py
@@ -6,7 +6,7 @@
"""
__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
-__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
__license__ = "GPL version 2 or (at your option) any later version"
__maintainer__ = "Stefan Hajnoczi"
@@ -71,10 +71,11 @@
out('void %(name_tcg)s(%(args_api)s)',
'{',
+ # NOTE: the check was already performed at TCG-generation time
' %(name)s(%(args_call)s);',
'}',
name_tcg="helper_%s_proxy" % e.api(),
- name=e.api(),
+ name=e.api(e.QEMU_TRACE_NOCHECK),
args_api=e_args_api,
args_call=", ".join(e_args_call.casted()),
)
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index 28a9141..96d1f84 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -543,8 +543,15 @@
static Property arm_cpu_has_mpu_property =
DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true);
+/* This is like DEFINE_PROP_UINT32 but it doesn't set the default value,
+ * because the CPU initfn will have already set cpu->pmsav7_dregion to
+ * the right value for that particular CPU type, and we don't want
+ * to override that with an incorrect constant value.
+ */
static Property arm_cpu_pmsav7_dregion_property =
- DEFINE_PROP_UINT32("pmsav7-dregion", ARMCPU, pmsav7_dregion, 16);
+ DEFINE_PROP_UNSIGNED_NODEFAULT("pmsav7-dregion", ARMCPU,
+ pmsav7_dregion,
+ qdev_prop_uint32, uint32_t);
static void arm_cpu_post_init(Object *obj)
{
@@ -1054,6 +1061,7 @@
set_feature(&cpu->env, ARM_FEATURE_V7);
set_feature(&cpu->env, ARM_FEATURE_M);
cpu->midr = 0x410fc231;
+ cpu->pmsav7_dregion = 8;
}
static void cortex_m4_initfn(Object *obj)
@@ -1064,6 +1072,7 @@
set_feature(&cpu->env, ARM_FEATURE_M);
set_feature(&cpu->env, ARM_FEATURE_THUMB_DSP);
cpu->midr = 0x410fc240; /* r0p0 */
+ cpu->pmsav7_dregion = 8;
}
static void arm_v7m_class_init(ObjectClass *oc, void *data)
{
@@ -1112,6 +1121,7 @@
cpu->id_isar4 = 0x0010142;
cpu->id_isar5 = 0x0;
cpu->mp_is_up = true;
+ cpu->pmsav7_dregion = 16;
define_arm_cp_regs(cpu, cortexr5_cp_reginfo);
}
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index e55547d..3fa3902 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -1393,7 +1393,7 @@
* a self-modified code correctly and also to take
* any pending interrupts immediately.
*/
- s->is_jmp = DISAS_UPDATE;
+ gen_goto_tb(s, 0, s->pc);
return;
default:
unallocated_encoding(s);
@@ -1788,7 +1788,8 @@
return;
}
gen_helper_exception_return(cpu_env);
- s->is_jmp = DISAS_JUMP;
+ /* Must exit loop to check un-masked IRQs */
+ s->is_jmp = DISAS_EXIT;
return;
case 5: /* DRPS */
if (rn != 0x1f) {
@@ -11364,16 +11365,9 @@
case DISAS_NEXT:
gen_goto_tb(dc, 1, dc->pc);
break;
- default:
- case DISAS_UPDATE:
- gen_a64_set_pc_im(dc->pc);
- /* fall through */
case DISAS_JUMP:
tcg_gen_lookup_and_goto_ptr(cpu_pc);
break;
- case DISAS_EXIT:
- tcg_gen_exit_tb(0);
- break;
case DISAS_TB_JUMP:
case DISAS_EXC:
case DISAS_SWI:
@@ -11397,6 +11391,13 @@
*/
tcg_gen_exit_tb(0);
break;
+ case DISAS_UPDATE:
+ gen_a64_set_pc_im(dc->pc);
+ /* fall through */
+ case DISAS_EXIT:
+ default:
+ tcg_gen_exit_tb(0);
+ break;
}
}
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 0862f9e..e27736c 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -4158,6 +4158,10 @@
tcg_temp_free(addr);
}
+/* This will end the TB but doesn't guarantee we'll return to
+ * cpu_loop_exec. Any live exit_requests will be processed as we
+ * enter the next TB.
+ */
static void gen_goto_tb(DisasContext *s, int n, target_ulong dest)
{
if (use_goto_tb(s, dest)) {
@@ -4168,6 +4172,7 @@
gen_set_pc_im(s, dest);
gen_goto_ptr();
}
+ s->is_jmp = DISAS_TB_JUMP;
}
static inline void gen_jmp (DisasContext *s, uint32_t dest)
@@ -4179,7 +4184,6 @@
gen_bx_im(s, dest);
} else {
gen_goto_tb(s, 0, dest);
- s->is_jmp = DISAS_TB_JUMP;
}
}
@@ -4475,7 +4479,8 @@
*/
gen_helper_cpsr_write_eret(cpu_env, cpsr);
tcg_temp_free_i32(cpsr);
- s->is_jmp = DISAS_JUMP;
+ /* Must exit loop to check un-masked IRQs */
+ s->is_jmp = DISAS_EXIT;
}
/* Generate an old-style exception return. Marks pc as dead. */
@@ -8165,7 +8170,7 @@
* self-modifying code correctly and also to take
* any pending interrupts immediately.
*/
- gen_lookup_tb(s);
+ gen_goto_tb(s, 0, s->pc & ~1);
return;
default:
goto illegal_op;
@@ -9519,7 +9524,8 @@
tmp = load_cpu_field(spsr);
gen_helper_cpsr_write_eret(cpu_env, tmp);
tcg_temp_free_i32(tmp);
- s->is_jmp = DISAS_JUMP;
+ /* Must exit loop to check un-masked IRQs */
+ s->is_jmp = DISAS_EXIT;
}
}
break;
@@ -10558,7 +10564,7 @@
* and also to take any pending interrupts
* immediately.
*/
- gen_lookup_tb(s);
+ gen_goto_tb(s, 0, s->pc & ~1);
break;
default:
goto illegal_op;
@@ -12095,12 +12101,12 @@
case DISAS_NEXT:
gen_goto_tb(dc, 1, dc->pc);
break;
- case DISAS_UPDATE:
- gen_set_pc_im(dc, dc->pc);
- /* fall through */
case DISAS_JUMP:
gen_goto_ptr();
break;
+ case DISAS_UPDATE:
+ gen_set_pc_im(dc, dc->pc);
+ /* fall through */
default:
/* indicate that the hash table must be used to find the next TB */
tcg_gen_exit_tb(0);
diff --git a/target/arm/translate.h b/target/arm/translate.h
index 15d383d..12fd79b 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -140,7 +140,10 @@
*/
#define DISAS_BX_EXCRET 11
/* For instructions which want an immediate exit to the main loop,
- * as opposed to attempting to use lookup_and_goto_ptr.
+ * as opposed to attempting to use lookup_and_goto_ptr. Unlike
+ * DISAS_UPDATE this doesn't write the PC on exiting the translation
+ * loop so you need to ensure something (gen_a64_set_pc_im or runtime
+ * helper) has done so before we reach return from cpu_tb_exec.
*/
#define DISAS_EXIT 12
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 4de91d5..0bbda76 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -1331,7 +1331,7 @@
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.6).
+ * 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.
@@ -1345,6 +1345,54 @@
.model_id = "Intel Core Processor (Skylake)",
},
{
+ .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 = "Opteron_G1",
.level = 5,
.vendor = CPUID_VENDOR_AMD,
@@ -2632,12 +2680,15 @@
CPUState *cs = CPU(cpu);
uint32_t pkg_offset;
uint32_t limit;
+ uint32_t signature[3];
/* Calculate & apply limits for different index ranges */
if (index >= 0xC0000000) {
limit = env->cpuid_xlevel2;
} else if (index >= 0x80000000) {
limit = env->cpuid_xlevel;
+ } else if (index >= 0x40000000) {
+ limit = 0x40000001;
} else {
limit = env->cpuid_level;
}
@@ -2872,6 +2923,30 @@
}
break;
}
+ case 0x40000000:
+ /*
+ * CPUID code in kvm_arch_init_vcpu() ignores stuff
+ * set here, but we restrict to TCG none the less.
+ */
+ if (tcg_enabled() && cpu->expose_tcg) {
+ memcpy(signature, "TCGTCGTCGTCG", 12);
+ *eax = 0x40000001;
+ *ebx = signature[0];
+ *ecx = signature[1];
+ *edx = signature[2];
+ } else {
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ }
+ break;
+ case 0x40000001:
+ *eax = 0;
+ *ebx = 0;
+ *ecx = 0;
+ *edx = 0;
+ break;
case 0x80000000:
*eax = env->cpuid_xlevel;
*ebx = env->cpuid_vendor1;
@@ -4018,6 +4093,7 @@
DEFINE_PROP_BOOL("kvm-no-smi-migration", X86CPU, kvm_no_smi_migration,
false),
DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, true),
+ DEFINE_PROP_BOOL("tcg-cpuid", X86CPU, expose_tcg, true),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 7a228af..0518673 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1218,6 +1218,7 @@
bool check_cpuid;
bool enforce_cpuid;
bool expose_kvm;
+ bool expose_tcg;
bool migratable;
bool max_features; /* Enable all supported features automatically */
uint32_t apic_id;
diff --git a/target/mips/helper.c b/target/mips/helper.c
index e359ca3..166f0d1 100644
--- a/target/mips/helper.c
+++ b/target/mips/helper.c
@@ -627,6 +627,8 @@
goto set_DEPC;
case EXCP_DBp:
env->CP0_Debug |= 1 << CP0DB_DBp;
+ /* Setup DExcCode - SDBBP instruction */
+ env->CP0_Debug = (env->CP0_Debug & ~(0x1fULL << CP0DB_DEC)) | 9 << CP0DB_DEC;
goto set_DEPC;
case EXCP_DDBS:
env->CP0_Debug |= 1 << CP0DB_DDBS;
diff --git a/target/mips/translate.c b/target/mips/translate.c
index befb87f..fe44f2f 100644
--- a/target/mips/translate.c
+++ b/target/mips/translate.c
@@ -4572,12 +4572,14 @@
case OPC_WSBH:
{
TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_const_tl(0x00FF00FF);
tcg_gen_shri_tl(t1, t0, 8);
- tcg_gen_andi_tl(t1, t1, 0x00FF00FF);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_gen_and_tl(t0, t0, t2);
tcg_gen_shli_tl(t0, t0, 8);
- tcg_gen_andi_tl(t0, t0, ~0x00FF00FF);
tcg_gen_or_tl(t0, t0, t1);
+ tcg_temp_free(t2);
tcg_temp_free(t1);
tcg_gen_ext32s_tl(cpu_gpr[rd], t0);
}
@@ -4592,27 +4594,31 @@
case OPC_DSBH:
{
TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_const_tl(0x00FF00FF00FF00FFULL);
tcg_gen_shri_tl(t1, t0, 8);
- tcg_gen_andi_tl(t1, t1, 0x00FF00FF00FF00FFULL);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_gen_and_tl(t0, t0, t2);
tcg_gen_shli_tl(t0, t0, 8);
- tcg_gen_andi_tl(t0, t0, ~0x00FF00FF00FF00FFULL);
tcg_gen_or_tl(cpu_gpr[rd], t0, t1);
+ tcg_temp_free(t2);
tcg_temp_free(t1);
}
break;
case OPC_DSHD:
{
TCGv t1 = tcg_temp_new();
+ TCGv t2 = tcg_const_tl(0x0000FFFF0000FFFFULL);
tcg_gen_shri_tl(t1, t0, 16);
- tcg_gen_andi_tl(t1, t1, 0x0000FFFF0000FFFFULL);
+ tcg_gen_and_tl(t1, t1, t2);
+ tcg_gen_and_tl(t0, t0, t2);
tcg_gen_shli_tl(t0, t0, 16);
- tcg_gen_andi_tl(t0, t0, ~0x0000FFFF0000FFFFULL);
tcg_gen_or_tl(t0, t0, t1);
tcg_gen_shri_tl(t1, t0, 32);
tcg_gen_shli_tl(t0, t0, 32);
tcg_gen_or_tl(cpu_gpr[rd], t0, t1);
+ tcg_temp_free(t2);
tcg_temp_free(t1);
}
break;
diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c
index e2ccc32..f4e5bb6 100644
--- a/target/s390x/cpu_models.c
+++ b/target/s390x/cpu_models.c
@@ -780,14 +780,19 @@
{
static const int feats[] = {
S390_FEAT_DAT_ENH,
+ S390_FEAT_IDTE_SEGMENT,
S390_FEAT_STFLE,
S390_FEAT_EXTENDED_IMMEDIATE,
S390_FEAT_EXTENDED_TRANSLATION_2,
+ S390_FEAT_EXTENDED_TRANSLATION_3,
S390_FEAT_LONG_DISPLACEMENT,
S390_FEAT_LONG_DISPLACEMENT_FAST,
S390_FEAT_ETF2_ENH,
S390_FEAT_STORE_CLOCK_FAST,
S390_FEAT_MOVE_WITH_OPTIONAL_SPEC,
+ S390_FEAT_ETF3_ENH,
+ S390_FEAT_COMPARE_AND_SWAP_AND_STORE,
+ S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2,
S390_FEAT_GENERAL_INSTRUCTIONS_EXT,
S390_FEAT_EXECUTE_EXT,
S390_FEAT_FLOATING_POINT_SUPPPORT_ENH,
diff --git a/target/s390x/helper.h b/target/s390x/helper.h
index 964097b..4b02907 100644
--- a/target/s390x/helper.h
+++ b/target/s390x/helper.h
@@ -12,7 +12,8 @@
DEF_HELPER_FLAGS_3(divu32, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(divs64, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_4(divu64, TCG_CALL_NO_WG, i64, env, i64, i64, i64)
-DEF_HELPER_4(srst, i64, env, i64, i64, i64)
+DEF_HELPER_3(srst, void, env, i32, i32)
+DEF_HELPER_3(srstu, void, env, i32, i32)
DEF_HELPER_4(clst, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_4(mvn, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_FLAGS_4(mvo, TCG_CALL_NO_WG, void, env, i32, i64, i64)
@@ -33,6 +34,7 @@
DEF_HELPER_3(cdlgb, i64, env, i64, i32)
DEF_HELPER_3(cxlgb, i64, env, i64, i32)
DEF_HELPER_4(cdsg, void, env, i64, i32, i32)
+DEF_HELPER_4(csst, i32, env, i32, i64, i64)
DEF_HELPER_FLAGS_3(aeb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_3(adb, TCG_CALL_NO_WG, i64, env, i64, i64)
DEF_HELPER_FLAGS_5(axb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64)
@@ -95,6 +97,7 @@
DEF_HELPER_FLAGS_4(tr, TCG_CALL_NO_WG, void, env, i32, i64, i64)
DEF_HELPER_4(tre, i64, env, i64, i64, i64)
DEF_HELPER_4(trt, i32, env, i32, i64, i64)
+DEF_HELPER_4(trtr, i32, env, i32, i64, i64)
DEF_HELPER_5(trXX, i32, env, i32, i32, i32, i32)
DEF_HELPER_4(cksm, i64, env, i64, i64, i64)
DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64)
@@ -106,6 +109,12 @@
DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_4(stpq, TCG_CALL_NO_WG, void, env, i64, i64, i64)
DEF_HELPER_4(mvcos, i32, env, i64, i64, i64)
+DEF_HELPER_4(cu12, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu14, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu21, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu24, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu41, i32, env, i32, i32, i32)
+DEF_HELPER_4(cu42, i32, env, i32, i32, i32)
#ifndef CONFIG_USER_ONLY
DEF_HELPER_3(servc, i32, env, i64, i64)
diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def
index d3bb851..ad84c74 100644
--- a/target/s390x/insn-data.def
+++ b/target/s390x/insn-data.def
@@ -265,6 +265,8 @@
D(0xbb00, CDS, RS_a, Z, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ)
D(0xeb31, CDSY, RSY_a, LD, r3_D32, r1_D32, new, r1_D32, cs, 0, MO_TEQ)
C(0xeb3e, CDSG, RSY_a, Z, 0, 0, 0, 0, cdsg, 0)
+/* COMPARE AND SWAP AND STORE */
+ C(0xc802, CSST, SSF, CASS, la1, a2, 0, 0, csst, 0)
/* COMPARE AND TRAP */
D(0xb972, CRT, RRF_c, GIE, r1_32s, r2_32s, 0, 0, ct, 0, 0)
@@ -311,6 +313,19 @@
C(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, f1, 0, cdlgb, 0)
C(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, x1, 0, cxlgb, 0)
+/* CONVERT UTF-8 TO UTF-16 */
+ D(0xb2a7, CU12, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 12)
+/* CONVERT UTF-8 TO UTF-32 */
+ D(0xb9b0, CU14, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 14)
+/* CONVERT UTF-16 to UTF-8 */
+ D(0xb2a6, CU21, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 21)
+/* CONVERT UTF-16 to UTF-32 */
+ D(0xb9b1, CU24, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 24)
+/* CONVERT UTF-32 to UTF-8 */
+ D(0xb9b2, CU41, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 41)
+/* CONVERT UTF-32 to UTF-16 */
+ D(0xb9b3, CU42, RRF_c, ETF3, 0, 0, 0, 0, cuXX, 0, 42)
+
/* DIVIDE */
C(0x1d00, DR, RR_a, Z, r1_D32, r2_32s, new_P, r1_P32, divs32, 0)
C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0)
@@ -721,7 +736,9 @@
C(0xec57, RXSBG, RIE_f, GIE, 0, r2, r1, 0, rosbg, 0)
/* SEARCH STRING */
- C(0xb25e, SRST, RRE, Z, r1_o, r2_o, 0, 0, srst, 0)
+ C(0xb25e, SRST, RRE, Z, 0, 0, 0, 0, srst, 0)
+/* SEARCH STRING UNICODE */
+ C(0xb9be, SRSTU, RRE, ETF3, 0, 0, 0, 0, srstu, 0)
/* SET ACCESS */
C(0xb24e, SAR, RRE, Z, 0, r2_o, 0, 0, sar, 0)
@@ -899,6 +916,8 @@
C(0xdc00, TR, SS_a, Z, la1, a2, 0, 0, tr, 0)
/* TRANSLATE AND TEST */
C(0xdd00, TRT, SS_a, Z, la1, a2, 0, 0, trt, 0)
+/* TRANSLATE AND TEST REVERSE */
+ C(0xd000, TRTR, SS_a, ETF3, la1, a2, 0, 0, trtr, 0)
/* TRANSLATE EXTENDED */
C(0xb2a5, TRE, RRE, Z, 0, r2, r1_P, 0, tre, 0)
diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c
index ede8471..cdc78aa 100644
--- a/target/s390x/mem_helper.c
+++ b/target/s390x/mem_helper.c
@@ -538,18 +538,21 @@
}
/* search string (c is byte to search, r2 is string, r1 end of string) */
-uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
- uint64_t str)
+void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
uintptr_t ra = GETPC();
+ uint64_t end, str;
uint32_t len;
- uint8_t v, c = r0;
+ uint8_t v, c = env->regs[0];
- str = wrap_address(env, str);
- end = wrap_address(env, end);
+ /* Bits 32-55 must contain all 0. */
+ if (env->regs[0] & 0xffffff00u) {
+ cpu_restore_state(ENV_GET_CPU(env), ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ }
- /* Assume for now that R2 is unmodified. */
- env->retxl = str;
+ str = get_address(env, r2);
+ end = get_address(env, r1);
/* Lest we fail to service interrupts in a timely manner, limit the
amount of work we're willing to do. For now, let's cap at 8k. */
@@ -557,20 +560,61 @@
if (str + len == end) {
/* Character not found. R1 & R2 are unmodified. */
env->cc_op = 2;
- return end;
+ return;
}
v = cpu_ldub_data_ra(env, str + len, ra);
if (v == c) {
/* Character found. Set R1 to the location; R2 is unmodified. */
env->cc_op = 1;
- return str + len;
+ set_address(env, r1, str + len);
+ return;
}
}
/* CPU-determined bytes processed. Advance R2 to next byte to process. */
- env->retxl = str + len;
env->cc_op = 3;
- return end;
+ set_address(env, r2, str + len);
+}
+
+void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2)
+{
+ uintptr_t ra = GETPC();
+ uint32_t len;
+ uint16_t v, c = env->regs[0];
+ uint64_t end, str, adj_end;
+
+ /* Bits 32-47 of R0 must be zero. */
+ if (env->regs[0] & 0xffff0000u) {
+ cpu_restore_state(ENV_GET_CPU(env), ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ }
+
+ str = get_address(env, r2);
+ end = get_address(env, r1);
+
+ /* If the LSB of the two addresses differ, use one extra byte. */
+ adj_end = end + ((str ^ end) & 1);
+
+ /* Lest we fail to service interrupts in a timely manner, limit the
+ amount of work we're willing to do. For now, let's cap at 8k. */
+ for (len = 0; len < 0x2000; len += 2) {
+ if (str + len == adj_end) {
+ /* End of input found. */
+ env->cc_op = 2;
+ return;
+ }
+ v = cpu_lduw_data_ra(env, str + len, ra);
+ if (v == c) {
+ /* Character found. Set R1 to the location; R2 is unmodified. */
+ env->cc_op = 1;
+ set_address(env, r1, str + len);
+ return;
+ }
+ }
+
+ /* CPU-determined bytes processed. Advance R2 to next byte to process. */
+ env->cc_op = 3;
+ set_address(env, r2, str + len);
}
/* unsigned string compare (c is string terminator) */
@@ -1233,17 +1277,18 @@
return array + i;
}
-static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
- uint64_t trans, uintptr_t ra)
+static inline uint32_t do_helper_trt(CPUS390XState *env, int len,
+ uint64_t array, uint64_t trans,
+ int inc, uintptr_t ra)
{
- uint32_t i;
+ int i;
for (i = 0; i <= len; i++) {
- uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
+ uint8_t byte = cpu_ldub_data_ra(env, array + i * inc, ra);
uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra);
if (sbyte != 0) {
- set_address(env, 1, array + i);
+ set_address(env, 1, array + i * inc);
env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte);
return (i == len) ? 2 : 1;
}
@@ -1255,7 +1300,13 @@
uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
uint64_t trans)
{
- return do_helper_trt(env, len, array, trans, GETPC());
+ return do_helper_trt(env, len, array, trans, 1, GETPC());
+}
+
+uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array,
+ uint64_t trans)
+{
+ return do_helper_trt(env, len, array, trans, -1, GETPC());
}
/* Translate one/two to one/two */
@@ -1353,6 +1404,195 @@
env->regs[r1 + 1] = int128_getlo(oldv);
}
+uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)
+{
+#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128)
+ uint32_t mem_idx = cpu_mmu_index(env, false);
+#endif
+ uintptr_t ra = GETPC();
+ uint32_t fc = extract32(env->regs[0], 0, 8);
+ uint32_t sc = extract32(env->regs[0], 8, 8);
+ uint64_t pl = get_address(env, 1) & -16;
+ uint64_t svh, svl;
+ uint32_t cc;
+
+ /* Sanity check the function code and storage characteristic. */
+ if (fc > 1 || sc > 3) {
+ if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) {
+ goto spec_exception;
+ }
+ if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) {
+ goto spec_exception;
+ }
+ }
+
+ /* Sanity check the alignments. */
+ if (extract32(a1, 0, 4 << fc) || extract32(a2, 0, 1 << sc)) {
+ goto spec_exception;
+ }
+
+ /* Sanity check writability of the store address. */
+#ifndef CONFIG_USER_ONLY
+ probe_write(env, a2, mem_idx, ra);
+#endif
+
+ /* Note that the compare-and-swap is atomic, and the store is atomic, but
+ the complete operation is not. Therefore we do not need to assert serial
+ context in order to implement this. That said, restart early if we can't
+ support either operation that is supposed to be atomic. */
+ if (parallel_cpus) {
+ int mask = 0;
+#if !defined(CONFIG_ATOMIC64)
+ mask = -8;
+#elif !defined(CONFIG_ATOMIC128)
+ mask = -16;
+#endif
+ if (((4 << fc) | (1 << sc)) & mask) {
+ cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
+ }
+ }
+
+ /* All loads happen before all stores. For simplicity, load the entire
+ store value area from the parameter list. */
+ svh = cpu_ldq_data_ra(env, pl + 16, ra);
+ svl = cpu_ldq_data_ra(env, pl + 24, ra);
+
+ switch (fc) {
+ case 0:
+ {
+ uint32_t nv = cpu_ldl_data_ra(env, pl, ra);
+ uint32_t cv = env->regs[r3];
+ uint32_t ov;
+
+ if (parallel_cpus) {
+#ifdef CONFIG_USER_ONLY
+ uint32_t *haddr = g2h(a1);
+ ov = atomic_cmpxchg__nocheck(haddr, cv, nv);
+#else
+ TCGMemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mem_idx);
+ ov = helper_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi, ra);
+#endif
+ } else {
+ ov = cpu_ldl_data_ra(env, a1, ra);
+ cpu_stl_data_ra(env, a1, (ov == cv ? nv : ov), ra);
+ }
+ cc = (ov != cv);
+ env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov);
+ }
+ break;
+
+ case 1:
+ {
+ uint64_t nv = cpu_ldq_data_ra(env, pl, ra);
+ uint64_t cv = env->regs[r3];
+ uint64_t ov;
+
+ if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC64
+# ifdef CONFIG_USER_ONLY
+ uint64_t *haddr = g2h(a1);
+ ov = atomic_cmpxchg__nocheck(haddr, cv, nv);
+# else
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN, mem_idx);
+ ov = helper_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi, ra);
+# endif
+#else
+ /* Note that we asserted !parallel_cpus above. */
+ g_assert_not_reached();
+#endif
+ } else {
+ ov = cpu_ldq_data_ra(env, a1, ra);
+ cpu_stq_data_ra(env, a1, (ov == cv ? nv : ov), ra);
+ }
+ cc = (ov != cv);
+ env->regs[r3] = ov;
+ }
+ break;
+
+ case 2:
+ {
+ uint64_t nvh = cpu_ldq_data_ra(env, pl, ra);
+ uint64_t nvl = cpu_ldq_data_ra(env, pl + 8, ra);
+ Int128 nv = int128_make128(nvl, nvh);
+ Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
+ Int128 ov;
+
+ if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC128
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra);
+ cc = !int128_eq(ov, cv);
+#else
+ /* Note that we asserted !parallel_cpus above. */
+ g_assert_not_reached();
+#endif
+ } else {
+ uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra);
+ uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra);
+
+ ov = int128_make128(ol, oh);
+ cc = !int128_eq(ov, cv);
+ if (cc) {
+ nv = ov;
+ }
+
+ cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra);
+ cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra);
+ }
+
+ env->regs[r3 + 0] = int128_gethi(ov);
+ env->regs[r3 + 1] = int128_getlo(ov);
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ /* Store only if the comparison succeeded. Note that above we use a pair
+ of 64-bit big-endian loads, so for sc < 3 we must extract the value
+ from the most-significant bits of svh. */
+ if (cc == 0) {
+ switch (sc) {
+ case 0:
+ cpu_stb_data_ra(env, a2, svh >> 56, ra);
+ break;
+ case 1:
+ cpu_stw_data_ra(env, a2, svh >> 48, ra);
+ break;
+ case 2:
+ cpu_stl_data_ra(env, a2, svh >> 32, ra);
+ break;
+ case 3:
+ cpu_stq_data_ra(env, a2, svh, ra);
+ break;
+ case 4:
+ if (parallel_cpus) {
+#ifdef CONFIG_ATOMIC128
+ TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
+ Int128 sv = int128_make128(svl, svh);
+ helper_atomic_sto_be_mmu(env, a2, sv, oi, ra);
+#else
+ /* Note that we asserted !parallel_cpus above. */
+ g_assert_not_reached();
+#endif
+ } else {
+ cpu_stq_data_ra(env, a2 + 0, svh, ra);
+ cpu_stq_data_ra(env, a2 + 8, svl, ra);
+ }
+ default:
+ g_assert_not_reached();
+ }
+ }
+
+ return cc;
+
+ spec_exception:
+ cpu_restore_state(ENV_GET_CPU(env), ra);
+ program_interrupt(env, PGM_SPECIFICATION, 6);
+ g_assert_not_reached();
+}
+
#if !defined(CONFIG_USER_ONLY)
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
{
@@ -1886,7 +2126,6 @@
[0x6] = do_helper_oc,
[0x7] = do_helper_xc,
[0xc] = do_helper_tr,
- [0xd] = do_helper_trt,
};
dx_helper helper = dx[opc & 0xf];
@@ -2007,3 +2246,313 @@
return cc;
}
+
+/* Decode a Unicode character. A return value < 0 indicates success, storing
+ the UTF-32 result into OCHAR and the input length into OLEN. A return
+ value >= 0 indicates failure, and the CC value to be returned. */
+typedef int (*decode_unicode_fn)(CPUS390XState *env, uint64_t addr,
+ uint64_t ilen, bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen);
+
+/* Encode a Unicode character. A return value < 0 indicates success, storing
+ the bytes into ADDR and the output length into OLEN. A return value >= 0
+ indicates failure, and the CC value to be returned. */
+typedef int (*encode_unicode_fn)(CPUS390XState *env, uint64_t addr,
+ uint64_t ilen, uintptr_t ra, uint32_t c,
+ uint32_t *olen);
+
+static int decode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen)
+{
+ uint8_t s0, s1, s2, s3;
+ uint32_t c, l;
+
+ if (ilen < 1) {
+ return 0;
+ }
+ s0 = cpu_ldub_data_ra(env, addr, ra);
+ if (s0 <= 0x7f) {
+ /* one byte character */
+ l = 1;
+ c = s0;
+ } else if (s0 <= (enh_check ? 0xc1 : 0xbf)) {
+ /* invalid character */
+ return 2;
+ } else if (s0 <= 0xdf) {
+ /* two byte character */
+ l = 2;
+ if (ilen < 2) {
+ return 0;
+ }
+ s1 = cpu_ldub_data_ra(env, addr + 1, ra);
+ c = s0 & 0x1f;
+ c = (c << 6) | (s1 & 0x3f);
+ if (enh_check && (s1 & 0xc0) != 0x80) {
+ return 2;
+ }
+ } else if (s0 <= 0xef) {
+ /* three byte character */
+ l = 3;
+ if (ilen < 3) {
+ return 0;
+ }
+ s1 = cpu_ldub_data_ra(env, addr + 1, ra);
+ s2 = cpu_ldub_data_ra(env, addr + 2, ra);
+ c = s0 & 0x0f;
+ c = (c << 6) | (s1 & 0x3f);
+ c = (c << 6) | (s2 & 0x3f);
+ /* Fold the byte-by-byte range descriptions in the PoO into
+ tests against the complete value. It disallows encodings
+ that could be smaller, and the UTF-16 surrogates. */
+ if (enh_check
+ && ((s1 & 0xc0) != 0x80
+ || (s2 & 0xc0) != 0x80
+ || c < 0x1000
+ || (c >= 0xd800 && c <= 0xdfff))) {
+ return 2;
+ }
+ } else if (s0 <= (enh_check ? 0xf4 : 0xf7)) {
+ /* four byte character */
+ l = 4;
+ if (ilen < 4) {
+ return 0;
+ }
+ s1 = cpu_ldub_data_ra(env, addr + 1, ra);
+ s2 = cpu_ldub_data_ra(env, addr + 2, ra);
+ s3 = cpu_ldub_data_ra(env, addr + 3, ra);
+ c = s0 & 0x07;
+ c = (c << 6) | (s1 & 0x3f);
+ c = (c << 6) | (s2 & 0x3f);
+ c = (c << 6) | (s3 & 0x3f);
+ /* See above. */
+ if (enh_check
+ && ((s1 & 0xc0) != 0x80
+ || (s2 & 0xc0) != 0x80
+ || (s3 & 0xc0) != 0x80
+ || c < 0x010000
+ || c > 0x10ffff)) {
+ return 2;
+ }
+ } else {
+ /* invalid character */
+ return 2;
+ }
+
+ *ochar = c;
+ *olen = l;
+ return -1;
+}
+
+static int decode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen)
+{
+ uint16_t s0, s1;
+ uint32_t c, l;
+
+ if (ilen < 2) {
+ return 0;
+ }
+ s0 = cpu_lduw_data_ra(env, addr, ra);
+ if ((s0 & 0xfc00) != 0xd800) {
+ /* one word character */
+ l = 2;
+ c = s0;
+ } else {
+ /* two word character */
+ l = 4;
+ if (ilen < 4) {
+ return 0;
+ }
+ s1 = cpu_lduw_data_ra(env, addr + 2, ra);
+ c = extract32(s0, 6, 4) + 1;
+ c = (c << 6) | (s0 & 0x3f);
+ c = (c << 10) | (s1 & 0x3ff);
+ if (enh_check && (s1 & 0xfc00) != 0xdc00) {
+ /* invalid surrogate character */
+ return 2;
+ }
+ }
+
+ *ochar = c;
+ *olen = l;
+ return -1;
+}
+
+static int decode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ bool enh_check, uintptr_t ra,
+ uint32_t *ochar, uint32_t *olen)
+{
+ uint32_t c;
+
+ if (ilen < 4) {
+ return 0;
+ }
+ c = cpu_ldl_data_ra(env, addr, ra);
+ if ((c >= 0xd800 && c <= 0xdbff) || c > 0x10ffff) {
+ /* invalid unicode character */
+ return 2;
+ }
+
+ *ochar = c;
+ *olen = 4;
+ return -1;
+}
+
+static int encode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ uintptr_t ra, uint32_t c, uint32_t *olen)
+{
+ uint8_t d[4];
+ uint32_t l, i;
+
+ if (c <= 0x7f) {
+ /* one byte character */
+ l = 1;
+ d[0] = c;
+ } else if (c <= 0x7ff) {
+ /* two byte character */
+ l = 2;
+ d[1] = 0x80 | extract32(c, 0, 6);
+ d[0] = 0xc0 | extract32(c, 6, 5);
+ } else if (c <= 0xffff) {
+ /* three byte character */
+ l = 3;
+ d[2] = 0x80 | extract32(c, 0, 6);
+ d[1] = 0x80 | extract32(c, 6, 6);
+ d[0] = 0xe0 | extract32(c, 12, 4);
+ } else {
+ /* four byte character */
+ l = 4;
+ d[3] = 0x80 | extract32(c, 0, 6);
+ d[2] = 0x80 | extract32(c, 6, 6);
+ d[1] = 0x80 | extract32(c, 12, 6);
+ d[0] = 0xf0 | extract32(c, 18, 3);
+ }
+
+ if (ilen < l) {
+ return 1;
+ }
+ for (i = 0; i < l; ++i) {
+ cpu_stb_data_ra(env, addr + i, d[i], ra);
+ }
+
+ *olen = l;
+ return -1;
+}
+
+static int encode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ uintptr_t ra, uint32_t c, uint32_t *olen)
+{
+ uint16_t d0, d1;
+
+ if (c <= 0xffff) {
+ /* one word character */
+ if (ilen < 2) {
+ return 1;
+ }
+ cpu_stw_data_ra(env, addr, c, ra);
+ *olen = 2;
+ } else {
+ /* two word character */
+ if (ilen < 4) {
+ return 1;
+ }
+ d1 = 0xdc00 | extract32(c, 0, 10);
+ d0 = 0xd800 | extract32(c, 10, 6);
+ d0 = deposit32(d0, 6, 4, extract32(c, 16, 5) - 1);
+ cpu_stw_data_ra(env, addr + 0, d0, ra);
+ cpu_stw_data_ra(env, addr + 2, d1, ra);
+ *olen = 4;
+ }
+
+ return -1;
+}
+
+static int encode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
+ uintptr_t ra, uint32_t c, uint32_t *olen)
+{
+ if (ilen < 4) {
+ return 1;
+ }
+ cpu_stl_data_ra(env, addr, c, ra);
+ *olen = 4;
+ return -1;
+}
+
+static inline uint32_t convert_unicode(CPUS390XState *env, uint32_t r1,
+ uint32_t r2, uint32_t m3, uintptr_t ra,
+ decode_unicode_fn decode,
+ encode_unicode_fn encode)
+{
+ uint64_t dst = get_address(env, r1);
+ uint64_t dlen = get_length(env, r1 + 1);
+ uint64_t src = get_address(env, r2);
+ uint64_t slen = get_length(env, r2 + 1);
+ bool enh_check = m3 & 1;
+ int cc, i;
+
+ /* Lest we fail to service interrupts in a timely manner, limit the
+ amount of work we're willing to do. For now, let's cap at 256. */
+ for (i = 0; i < 256; ++i) {
+ uint32_t c, ilen, olen;
+
+ cc = decode(env, src, slen, enh_check, ra, &c, &ilen);
+ if (unlikely(cc >= 0)) {
+ break;
+ }
+ cc = encode(env, dst, dlen, ra, c, &olen);
+ if (unlikely(cc >= 0)) {
+ break;
+ }
+
+ src += ilen;
+ slen -= ilen;
+ dst += olen;
+ dlen -= olen;
+ cc = 3;
+ }
+
+ set_address(env, r1, dst);
+ set_length(env, r1 + 1, dlen);
+ set_address(env, r2, src);
+ set_length(env, r2 + 1, slen);
+
+ return cc;
+}
+
+uint32_t HELPER(cu12)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf8, encode_utf16);
+}
+
+uint32_t HELPER(cu14)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf8, encode_utf32);
+}
+
+uint32_t HELPER(cu21)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf16, encode_utf8);
+}
+
+uint32_t HELPER(cu24)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf16, encode_utf32);
+}
+
+uint32_t HELPER(cu41)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf32, encode_utf8);
+}
+
+uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
+{
+ return convert_unicode(env, r1, r2, m3, GETPC(),
+ decode_utf32, encode_utf16);
+}
diff --git a/target/s390x/translate.c b/target/s390x/translate.c
index 592d6b0..1dffcee 100644
--- a/target/s390x/translate.c
+++ b/target/s390x/translate.c
@@ -2033,6 +2033,18 @@
return NO_EXIT;
}
+static ExitStatus op_csst(DisasContext *s, DisasOps *o)
+{
+ int r3 = get_field(s->fields, r3);
+ TCGv_i32 t_r3 = tcg_const_i32(r3);
+
+ gen_helper_csst(cc_op, cpu_env, t_r3, o->in1, o->in2);
+ tcg_temp_free_i32(t_r3);
+
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
#ifndef CONFIG_USER_ONLY
static ExitStatus op_csp(DisasContext *s, DisasOps *o)
{
@@ -2110,6 +2122,56 @@
return NO_EXIT;
}
+static ExitStatus op_cuXX(DisasContext *s, DisasOps *o)
+{
+ int m3 = get_field(s->fields, m3);
+ int r1 = get_field(s->fields, r1);
+ int r2 = get_field(s->fields, r2);
+ TCGv_i32 tr1, tr2, chk;
+
+ /* R1 and R2 must both be even. */
+ if ((r1 | r2) & 1) {
+ gen_program_exception(s, PGM_SPECIFICATION);
+ return EXIT_NORETURN;
+ }
+ if (!s390_has_feat(S390_FEAT_ETF3_ENH)) {
+ m3 = 0;
+ }
+
+ tr1 = tcg_const_i32(r1);
+ tr2 = tcg_const_i32(r2);
+ chk = tcg_const_i32(m3);
+
+ switch (s->insn->data) {
+ case 12:
+ gen_helper_cu12(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 14:
+ gen_helper_cu14(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 21:
+ gen_helper_cu21(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 24:
+ gen_helper_cu24(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 41:
+ gen_helper_cu41(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ case 42:
+ gen_helper_cu42(cc_op, cpu_env, tr1, tr2, chk);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ tcg_temp_free_i32(tr1);
+ tcg_temp_free_i32(tr2);
+ tcg_temp_free_i32(chk);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
#ifndef CONFIG_USER_ONLY
static ExitStatus op_diag(DisasContext *s, DisasOps *o)
{
@@ -3417,8 +3479,8 @@
}
/* In some cases we can implement this with extract. */
- if (imask == 0 && pos == 0 && len > 0 && rot + len <= 64) {
- tcg_gen_extract_i64(o->out, o->in2, rot, len);
+ if (imask == 0 && pos == 0 && len > 0 && len <= rot) {
+ tcg_gen_extract_i64(o->out, o->in2, 64 - rot, len);
return NO_EXIT;
}
@@ -4225,9 +4287,27 @@
static ExitStatus op_srst(DisasContext *s, DisasOps *o)
{
- gen_helper_srst(o->in1, cpu_env, regs[0], o->in1, o->in2);
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
+
+ gen_helper_srst(cpu_env, r1, r2);
+
+ tcg_temp_free_i32(r1);
+ tcg_temp_free_i32(r2);
set_cc_static(s);
- return_low128(o->in2);
+ return NO_EXIT;
+}
+
+static ExitStatus op_srstu(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
+ TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2));
+
+ gen_helper_srstu(cpu_env, r1, r2);
+
+ tcg_temp_free_i32(r1);
+ tcg_temp_free_i32(r2);
+ set_cc_static(s);
return NO_EXIT;
}
@@ -4367,6 +4447,15 @@
return NO_EXIT;
}
+static ExitStatus op_trtr(DisasContext *s, DisasOps *o)
+{
+ TCGv_i32 l = tcg_const_i32(get_field(s->fields, l1));
+ gen_helper_trtr(cc_op, cpu_env, l, o->addr1, o->in2);
+ tcg_temp_free_i32(l);
+ set_cc_static(s);
+ return NO_EXIT;
+}
+
static ExitStatus op_trXX(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
@@ -5437,7 +5526,6 @@
/* Give smaller names to the various facilities. */
#define FAC_Z S390_FEAT_ZARCH
#define FAC_CASS S390_FEAT_COMPARE_AND_SWAP_AND_STORE
-#define FAC_CASS2 S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2
#define FAC_DFP S390_FEAT_DFP
#define FAC_DFPR S390_FEAT_FLOATING_POINT_SUPPPORT_ENH /* DFP-rounding */
#define FAC_DO S390_FEAT_STFLE_45 /* distinct-operands */
@@ -5466,6 +5554,7 @@
#define FAC_EH S390_FEAT_STFLE_49 /* execution-hint */
#define FAC_PPA S390_FEAT_STFLE_49 /* processor-assist */
#define FAC_LZRB S390_FEAT_STFLE_53 /* load-and-zero-rightmost-byte */
+#define FAC_ETF3 S390_FEAT_EXTENDED_TRANSLATION_3
static const DisasInsn insn_info[] = {
#include "insn-data.def"
diff --git a/tcg/tcg-runtime.c b/tcg/tcg-runtime.c
index ec3a34e..3e23649 100644
--- a/tcg/tcg-runtime.c
+++ b/tcg/tcg-runtime.c
@@ -158,7 +158,8 @@
if (unlikely(!(tb
&& tb->pc == addr
&& tb->cs_base == cs_base
- && tb->flags == flags))) {
+ && tb->flags == flags
+ && tb->trace_vcpu_dstate == *cpu->trace_dstate))) {
tb = tb_htable_lookup(cpu, addr, cs_base, flags);
if (!tb) {
return tcg_ctx.code_gen_epilogue;
diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c
index 8e432e9..432b665 100644
--- a/tests/check-qom-proplist.c
+++ b/tests/check-qom-proplist.c
@@ -568,6 +568,47 @@
object_unparent(OBJECT(dev));
}
+static void test_qom_partial_path(void)
+{
+ Object *root = object_get_objects_root();
+ Object *cont1 = container_get(root, "/cont1");
+ Object *obj1 = object_new(TYPE_DUMMY);
+ Object *obj2a = object_new(TYPE_DUMMY);
+ Object *obj2b = object_new(TYPE_DUMMY);
+ bool ambiguous;
+
+ /* Objects created:
+ * /cont1
+ * /cont1/obj1
+ * /cont1/obj2 (obj2a)
+ * /obj2 (obj2b)
+ */
+ object_property_add_child(cont1, "obj1", obj1, &error_abort);
+ object_unref(obj1);
+ object_property_add_child(cont1, "obj2", obj2a, &error_abort);
+ object_unref(obj2a);
+ object_property_add_child(root, "obj2", obj2b, &error_abort);
+ object_unref(obj2b);
+
+ ambiguous = false;
+ g_assert(!object_resolve_path_type("", TYPE_DUMMY, &ambiguous));
+ g_assert(ambiguous);
+ g_assert(!object_resolve_path_type("", TYPE_DUMMY, NULL));
+
+ ambiguous = false;
+ g_assert(!object_resolve_path("obj2", &ambiguous));
+ g_assert(ambiguous);
+ g_assert(!object_resolve_path("obj2", NULL));
+
+ ambiguous = false;
+ g_assert(object_resolve_path("obj1", &ambiguous) == obj1);
+ g_assert(!ambiguous);
+ g_assert(object_resolve_path("obj1", NULL) == obj1);
+
+ object_unparent(obj2b);
+ object_unparent(cont1);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
@@ -585,6 +626,7 @@
g_test_add_func("/qom/proplist/getenum", test_dummy_getenum);
g_test_add_func("/qom/proplist/iterator", test_dummy_iterator);
g_test_add_func("/qom/proplist/delchild", test_dummy_delchild);
+ g_test_add_func("/qom/resolve/partial", test_qom_partial_path);
return g_test_run();
}
diff --git a/tests/data/test-qga-os-release b/tests/data/test-qga-os-release
new file mode 100644
index 0000000..70664eb
--- /dev/null
+++ b/tests/data/test-qga-os-release
@@ -0,0 +1,7 @@
+ID=qemu-ga-test
+NAME=QEMU-GA
+PRETTY_NAME="QEMU Guest Agent test"
+VERSION="Test 1"
+VERSION_ID=1
+VARIANT="Unit test \"\'\$\`\\ and \\\\ etc."
+VARIANT_ID=unit-test
diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c
index b1abb2a..f7162c0 100644
--- a/tests/device-introspect-test.c
+++ b/tests/device-introspect-test.c
@@ -45,6 +45,56 @@
return ret;
}
+/* Build a name -> ObjectTypeInfo index from a ObjectTypeInfo list */
+static QDict *qom_type_index(QList *types)
+{
+ QDict *index = qdict_new();
+ QListEntry *e;
+
+ QLIST_FOREACH_ENTRY(types, e) {
+ QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ const char *name = qdict_get_str(d, "name");
+ QINCREF(d);
+ qdict_put(index, name, d);
+ }
+ return index;
+}
+
+/* Check if @parent is present in the parent chain of @type */
+static bool qom_has_parent(QDict *index, const char *type, const char *parent)
+{
+ while (type) {
+ QDict *d = qdict_get_qdict(index, type);
+ const char *p = d && qdict_haskey(d, "parent") ?
+ qdict_get_str(d, "parent") :
+ NULL;
+
+ if (!strcmp(type, parent)) {
+ return true;
+ }
+
+ type = p;
+ }
+
+ return false;
+}
+
+/* Find an entry on a list returned by qom-list-types */
+static QDict *type_list_find(QList *types, const char *name)
+{
+ QListEntry *e;
+
+ QLIST_FOREACH_ENTRY(types, e) {
+ QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ const char *ename = qdict_get_str(d, "name");
+ if (!strcmp(ename, name)) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+
static QList *device_type_list(bool abstract)
{
return qom_list_types("device", abstract);
@@ -87,6 +137,61 @@
qtest_end();
}
+/*
+ * Ensure all entries returned by qom-list-types implements=<parent>
+ * have <parent> as a parent.
+ */
+static void test_qom_list_parents(const char *parent)
+{
+ QList *types;
+ QListEntry *e;
+ QDict *index;
+
+ types = qom_list_types(parent, true);
+ index = qom_type_index(types);
+
+ QLIST_FOREACH_ENTRY(types, e) {
+ QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ const char *name = qdict_get_str(d, "name");
+
+ g_assert(qom_has_parent(index, name, parent));
+ }
+
+ QDECREF(types);
+ QDECREF(index);
+}
+
+static void test_qom_list_fields(void)
+{
+ QList *all_types;
+ QList *non_abstract;
+ QListEntry *e;
+
+ qtest_start(common_args);
+
+ all_types = qom_list_types(NULL, true);
+ non_abstract = qom_list_types(NULL, false);
+
+ QLIST_FOREACH_ENTRY(all_types, e) {
+ QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ const char *name = qdict_get_str(d, "name");
+ bool abstract = qdict_haskey(d, "abstract") ?
+ qdict_get_bool(d, "abstract") :
+ false;
+ bool expected_abstract = !type_list_find(non_abstract, name);
+
+ g_assert(abstract == expected_abstract);
+ }
+
+ test_qom_list_parents("object");
+ test_qom_list_parents("device");
+ test_qom_list_parents("sys-bus-device");
+
+ QDECREF(all_types);
+ QDECREF(non_abstract);
+ qtest_end();
+}
+
static void test_device_intro_none(void)
{
qtest_start(common_args);
@@ -124,42 +229,34 @@
static void test_abstract_interfaces(void)
{
QList *all_types;
- QList *obj_types;
- QListEntry *ae;
+ QListEntry *e;
+ QDict *index;
qtest_start(common_args);
- /* qom-list-types implements=interface would return any type
- * that implements _any_ interface (not just interface types),
- * so use a trick to find the interface type names:
- * - list all object types
- * - list all types, and look for items that are not
- * on the first list
- */
- all_types = qom_list_types(NULL, false);
- obj_types = qom_list_types("object", false);
- QLIST_FOREACH_ENTRY(all_types, ae) {
- QDict *at = qobject_to_qdict(qlist_entry_obj(ae));
- const char *aname = qdict_get_str(at, "name");
- QListEntry *oe;
- const char *found = NULL;
+ all_types = qom_list_types("interface", true);
+ index = qom_type_index(all_types);
- QLIST_FOREACH_ENTRY(obj_types, oe) {
- QDict *ot = qobject_to_qdict(qlist_entry_obj(oe));
- const char *oname = qdict_get_str(ot, "name");
- if (!strcmp(aname, oname)) {
- found = oname;
- break;
- }
+ QLIST_FOREACH_ENTRY(all_types, e) {
+ QDict *d = qobject_to_qdict(qlist_entry_obj(e));
+ const char *name = qdict_get_str(d, "name");
+
+ /*
+ * qom-list-types implements=interface returns all types
+ * that implement _any_ interface (not just interface
+ * types), so skip the ones that don't have "interface"
+ * on the parent type chain.
+ */
+ if (!qom_has_parent(index, name, "interface")) {
+ /* Not an interface type */
+ continue;
}
- /* Using g_assert_cmpstr() will give more useful failure
- * messages than g_assert(found) */
- g_assert_cmpstr(aname, ==, found);
+ g_assert(qdict_haskey(d, "abstract") && qdict_get_bool(d, "abstract"));
}
QDECREF(all_types);
- QDECREF(obj_types);
+ QDECREF(index);
qtest_end();
}
@@ -168,6 +265,7 @@
g_test_init(&argc, &argv, NULL);
qtest_add_func("device/introspect/list", test_device_intro_list);
+ qtest_add_func("device/introspect/list-fields", test_qom_list_fields);
qtest_add_func("device/introspect/none", test_device_intro_none);
qtest_add_func("device/introspect/abstract", test_device_intro_abstract);
qtest_add_func("device/introspect/concrete", test_device_intro_concrete);
diff --git a/tests/qemu-iotests/140.out b/tests/qemu-iotests/140.out
index 0689b2b..7295b3d 100644
--- a/tests/qemu-iotests/140.out
+++ b/tests/qemu-iotests/140.out
@@ -8,7 +8,8 @@
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": {}}
-can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: No export with name 'drv' available
+can't open device nbd+unix:///drv?socket=TEST_DIR/nbd: Requested export not available
+server reported: export 'drv' not present
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
*** done
diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out
index 0978b89..1c7fb45 100644
--- a/tests/qemu-iotests/143.out
+++ b/tests/qemu-iotests/143.out
@@ -1,7 +1,8 @@
QA output created by 143
{"return": {}}
{"return": {}}
-can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: No export with name 'no_such_export' available
+can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Requested export not available
+server reported: export 'no_such_export' not present
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
*** done
diff --git a/tests/qht-bench.c b/tests/qht-bench.c
index 2afa09d..11c1cec 100644
--- a/tests/qht-bench.c
+++ b/tests/qht-bench.c
@@ -103,7 +103,7 @@
static inline uint32_t h(unsigned long v)
{
- return tb_hash_func5(v, 0, 0);
+ return tb_hash_func6(v, 0, 0, 0);
}
/*
diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c
index b25fe89..d81b086 100644
--- a/tests/test-qdev-global-props.c
+++ b/tests/test-qdev-global-props.c
@@ -33,6 +33,8 @@
#define STATIC_TYPE(obj) \
OBJECT_CHECK(MyType, (obj), TYPE_STATIC_PROPS)
+#define TYPE_SUBCLASS "static_prop_subtype"
+
#define PROP_DEFAULT 100
typedef struct MyType {
@@ -63,6 +65,11 @@
.class_init = static_prop_class_init,
};
+static const TypeInfo subclass_type = {
+ .name = TYPE_SUBCLASS,
+ .parent = TYPE_STATIC_PROPS,
+};
+
/* Test simple static property setting to default value */
static void test_static_prop_subprocess(void)
{
@@ -279,12 +286,35 @@
g_test_trap_assert_stdout("");
}
+/* Test if global props affecting subclasses are applied in the right order */
+static void test_subclass_global_props(void)
+{
+ MyType *mt;
+ /* Global properties must be applied in the order they were registered */
+ static GlobalProperty props[] = {
+ { TYPE_STATIC_PROPS, "prop1", "101" },
+ { TYPE_SUBCLASS, "prop1", "102" },
+ { TYPE_SUBCLASS, "prop2", "103" },
+ { TYPE_STATIC_PROPS, "prop2", "104" },
+ {}
+ };
+
+ qdev_prop_register_global_list(props);
+
+ mt = STATIC_TYPE(object_new(TYPE_SUBCLASS));
+ qdev_init_nofail(DEVICE(mt));
+
+ g_assert_cmpuint(mt->prop1, ==, 102);
+ g_assert_cmpuint(mt->prop2, ==, 104);
+}
+
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
module_call_init(MODULE_INIT_QOM);
type_register_static(&static_prop_type);
+ type_register_static(&subclass_type);
type_register_static(&dynamic_prop_type);
type_register_static(&hotplug_type);
type_register_static(&nohotplug_type);
@@ -310,6 +340,9 @@
g_test_add_func("/qdev/properties/dynamic/global/nouser",
test_dynamic_globalprop_nouser);
+ g_test_add_func("/qdev/properties/global/subclass",
+ test_subclass_global_props);
+
g_test_run();
return 0;
diff --git a/tests/test-qga.c b/tests/test-qga.c
index c77f241..06783e7 100644
--- a/tests/test-qga.c
+++ b/tests/test-qga.c
@@ -46,7 +46,7 @@
}
static void
-fixture_setup(TestFixture *fixture, gconstpointer data)
+fixture_setup(TestFixture *fixture, gconstpointer data, gchar **envp)
{
const gchar *extra_arg = data;
GError *error = NULL;
@@ -67,7 +67,7 @@
g_shell_parse_argv(cmd, NULL, &argv, &error);
g_assert_no_error(error);
- g_spawn_async(fixture->test_dir, argv, NULL,
+ g_spawn_async(fixture->test_dir, argv, envp,
G_SPAWN_SEARCH_PATH|G_SPAWN_DO_NOT_REAP_CHILD,
NULL, NULL, &fixture->pid, &error);
g_assert_no_error(error);
@@ -707,7 +707,7 @@
QDict *ret, *error;
const gchar *class, *desc;
- fixture_setup(&fix, "-b guest-ping,guest-get-time");
+ fixture_setup(&fix, "-b guest-ping,guest-get-time", NULL);
/* check blacklist */
ret = qmp_fd(fix.fd, "{'execute': 'guest-ping'}");
@@ -936,6 +936,60 @@
QDECREF(ret);
}
+static void test_qga_guest_get_osinfo(gconstpointer data)
+{
+ TestFixture fixture;
+ const gchar *str;
+ gchar *cwd, *env[2];
+ QDict *ret, *val;
+
+ cwd = g_get_current_dir();
+ env[0] = g_strdup_printf(
+ "QGA_OS_RELEASE=%s%ctests%cdata%ctest-qga-os-release",
+ cwd, G_DIR_SEPARATOR, G_DIR_SEPARATOR, G_DIR_SEPARATOR);
+ env[1] = NULL;
+ g_free(cwd);
+ fixture_setup(&fixture, NULL, env);
+
+ ret = qmp_fd(fixture.fd, "{'execute': 'guest-get-osinfo'}");
+ g_assert_nonnull(ret);
+ qmp_assert_no_error(ret);
+
+ val = qdict_get_qdict(ret, "return");
+
+ str = qdict_get_try_str(val, "id");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "qemu-ga-test");
+
+ str = qdict_get_try_str(val, "name");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "QEMU-GA");
+
+ str = qdict_get_try_str(val, "pretty-name");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "QEMU Guest Agent test");
+
+ str = qdict_get_try_str(val, "version");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "Test 1");
+
+ str = qdict_get_try_str(val, "version-id");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "1");
+
+ str = qdict_get_try_str(val, "variant");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "Unit test \"'$`\\ and \\\\ etc.");
+
+ str = qdict_get_try_str(val, "variant-id");
+ g_assert_nonnull(str);
+ g_assert_cmpstr(str, ==, "unit-test");
+
+ QDECREF(ret);
+ g_free(env[0]);
+ fixture_tear_down(&fixture, NULL);
+}
+
int main(int argc, char **argv)
{
TestFixture fix;
@@ -943,7 +997,7 @@
setlocale (LC_ALL, "");
g_test_init(&argc, &argv, NULL);
- fixture_setup(&fix, NULL);
+ fixture_setup(&fix, NULL, NULL);
g_test_add_data_func("/qga/sync-delimited", &fix, test_qga_sync_delimited);
g_test_add_data_func("/qga/sync", &fix, test_qga_sync);
@@ -972,6 +1026,8 @@
g_test_add_data_func("/qga/guest-exec", &fix, test_qga_guest_exec);
g_test_add_data_func("/qga/guest-exec-invalid", &fix,
test_qga_guest_exec_invalid);
+ g_test_add_data_func("/qga/guest-get-osinfo", &fix,
+ test_qga_guest_get_osinfo);
if (g_getenv("QGA_TEST_SIDE_EFFECTING")) {
g_test_add_data_func("/qga/fsfreeze-and-thaw", &fix,
diff --git a/trace-events b/trace-events
index bae63fd..f9dbd7f 100644
--- a/trace-events
+++ b/trace-events
@@ -106,7 +106,7 @@
#
# Mode: user, softmmu
# Targets: TCG(all)
-disable vcpu tcg guest_mem_before(TCGv vaddr, uint8_t info) "info=%d", "vaddr=0x%016"PRIx64" info=%d"
+vcpu tcg guest_mem_before(TCGv vaddr, uint8_t info) "info=%d", "vaddr=0x%016"PRIx64" info=%d"
# @num: System call number.
# @arg*: System call argument value.
@@ -115,7 +115,7 @@
#
# Mode: user
# Targets: TCG(all)
-disable vcpu guest_user_syscall(uint64_t num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7, uint64_t arg8) "num=0x%016"PRIx64" arg1=0x%016"PRIx64" arg2=0x%016"PRIx64" arg3=0x%016"PRIx64" arg4=0x%016"PRIx64" arg5=0x%016"PRIx64" arg6=0x%016"PRIx64" arg7=0x%016"PRIx64" arg8=0x%016"PRIx64
+vcpu guest_user_syscall(uint64_t num, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, uint64_t arg7, uint64_t arg8) "num=0x%016"PRIx64" arg1=0x%016"PRIx64" arg2=0x%016"PRIx64" arg3=0x%016"PRIx64" arg4=0x%016"PRIx64" arg5=0x%016"PRIx64" arg6=0x%016"PRIx64" arg7=0x%016"PRIx64" arg8=0x%016"PRIx64
# @num: System call number.
# @ret: System call result value.
@@ -124,4 +124,4 @@
#
# Mode: user
# Targets: TCG(all)
-disable vcpu guest_user_syscall_ret(uint64_t num, uint64_t ret) "num=0x%016"PRIx64" ret=0x%016"PRIx64
+vcpu guest_user_syscall_ret(uint64_t num, uint64_t ret) "num=0x%016"PRIx64" ret=0x%016"PRIx64
diff --git a/trace/control-target.c b/trace/control-target.c
index 99a8ed5..4e36101 100644
--- a/trace/control-target.c
+++ b/trace/control-target.c
@@ -61,6 +61,14 @@
}
}
+static void trace_event_synchronize_vcpu_state_dynamic(
+ CPUState *vcpu, run_on_cpu_data ignored)
+{
+ bitmap_copy(vcpu->trace_dstate, vcpu->trace_dstate_delayed,
+ CPU_TRACE_DSTATE_MAX_EVENTS);
+ cpu_tb_jmp_cache_clear(vcpu);
+}
+
void trace_event_set_vcpu_state_dynamic(CPUState *vcpu,
TraceEvent *ev, bool state)
{
@@ -73,13 +81,20 @@
if (state_pre != state) {
if (state) {
trace_events_enabled_count++;
- set_bit(vcpu_id, vcpu->trace_dstate);
+ set_bit(vcpu_id, vcpu->trace_dstate_delayed);
(*ev->dstate)++;
} else {
trace_events_enabled_count--;
- clear_bit(vcpu_id, vcpu->trace_dstate);
+ clear_bit(vcpu_id, vcpu->trace_dstate_delayed);
(*ev->dstate)--;
}
+ /*
+ * Delay changes until next TB; we want all TBs to be built from a
+ * single set of dstate values to ensure consistency of generated
+ * tracing code.
+ */
+ async_run_on_cpu(vcpu, trace_event_synchronize_vcpu_state_dynamic,
+ RUN_ON_CPU_NULL);
}
}
diff --git a/trace/control.c b/trace/control.c
index f5fb11d..82d8989 100644
--- a/trace/control.c
+++ b/trace/control.c
@@ -65,8 +65,15 @@
size_t i;
for (i = 0; events[i] != NULL; i++) {
events[i]->id = next_id++;
- if (events[i]->vcpu_id != TRACE_VCPU_EVENT_NONE) {
+ if (events[i]->vcpu_id == TRACE_VCPU_EVENT_NONE) {
+ continue;
+ }
+
+ if (likely(next_vcpu_id < CPU_TRACE_DSTATE_MAX_EVENTS)) {
events[i]->vcpu_id = next_vcpu_id++;
+ } else {
+ error_report("WARNING: too many vcpu trace events; dropping '%s'",
+ events[i]->name);
}
}
event_groups = g_renew(TraceEventGroup, event_groups, nevent_groups + 1);
diff --git a/trace/control.h b/trace/control.h
index 4ea53e2..b931824 100644
--- a/trace/control.h
+++ b/trace/control.h
@@ -165,6 +165,9 @@
* Set the dynamic tracing state of an event for the given vCPU.
*
* Pre-condition: trace_event_get_vcpu_state_static(ev) == true
+ *
+ * Note: Changes for execution-time events with the 'tcg' property will not be
+ * propagated until the next TB is executed (iff executing in TCG mode).
*/
void trace_event_set_vcpu_state_dynamic(CPUState *vcpu,
TraceEvent *ev, bool state);
diff --git a/util/aio-win32.c b/util/aio-win32.c
index bca496a..d6d5e02 100644
--- a/util/aio-win32.c
+++ b/util/aio-win32.c
@@ -71,6 +71,7 @@
}
} else {
HANDLE event;
+ long bitmask = 0;
if (node == NULL) {
/* Alloc and insert if it's not already there */
@@ -95,10 +96,16 @@
node->io_write = io_write;
node->is_external = is_external;
+ if (io_read) {
+ bitmask |= FD_READ | FD_ACCEPT | FD_CLOSE;
+ }
+
+ if (io_write) {
+ bitmask |= FD_WRITE | FD_CONNECT;
+ }
+
event = event_notifier_get_handle(&ctx->notifier);
- WSAEventSelect(node->pfd.fd, event,
- FD_READ | FD_ACCEPT | FD_CLOSE |
- FD_CONNECT | FD_WRITE | FD_OOB);
+ WSAEventSelect(node->pfd.fd, event, bitmask);
}
qemu_lockcnt_unlock(&ctx->list_lock);