summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MAINTAINERS4
-rw-r--r--block.c9
-rw-r--r--block/sheepdog.c9
-rw-r--r--blockdev.c16
-rw-r--r--device_tree.c4
-rw-r--r--docs/devel/clocks.rst391
-rw-r--r--docs/devel/index.rst1
-rw-r--r--docs/devel/qapi-code-gen.txt4
-rw-r--r--docs/system/s390x/protvirt.rst60
-rw-r--r--docs/system/target-s390x.rst5
-rw-r--r--hw/acpi/cpu.c2
-rw-r--r--hw/arm/msf2-soc.c26
-rw-r--r--hw/arm/virt.c20
-rw-r--r--hw/arm/xilinx_zynq.c57
-rw-r--r--hw/arm/xlnx-versal.c2
-rw-r--r--hw/arm/xlnx-zcu102.c39
-rw-r--r--hw/char/cadence_uart.c73
-rw-r--r--hw/char/trace-events3
-rw-r--r--hw/core/Makefile.objs2
-rw-r--r--hw/core/clock-vmstate.c25
-rw-r--r--hw/core/clock.c130
-rw-r--r--hw/core/machine-hmp-cmds.c2
-rw-r--r--hw/core/qdev-clock.c185
-rw-r--r--hw/core/qdev.c12
-rw-r--r--hw/core/trace-events7
-rw-r--r--hw/dma/xlnx-zdma.c25
-rw-r--r--hw/intc/arm_gicv3_kvm.c4
-rw-r--r--hw/misc/zynq_slcr.c172
-rw-r--r--hw/net/Makefile.objs1
-rw-r--r--hw/net/cadence_gem.c16
-rw-r--r--hw/net/msf2-emac.c589
-rw-r--r--hw/net/virtio-net.c4
-rw-r--r--hw/riscv/boot.c13
-rw-r--r--hw/riscv/sifive_e.c5
-rw-r--r--hw/riscv/sifive_u.c142
-rw-r--r--hw/riscv/spike.c30
-rw-r--r--hw/riscv/virt.c6
-rw-r--r--hw/s390x/Makefile.objs1
-rw-r--r--hw/s390x/ipl.c59
-rw-r--r--hw/s390x/ipl.h103
-rw-r--r--hw/s390x/pv.c113
-rw-r--r--hw/s390x/s390-virtio-ccw.c137
-rw-r--r--hw/s390x/sclp.c56
-rw-r--r--include/hw/arm/msf2-soc.h2
-rw-r--r--include/hw/char/cadence_uart.h1
-rw-r--r--include/hw/clock.h225
-rw-r--r--include/hw/gpio/nrf51_gpio.h2
-rw-r--r--include/hw/net/msf2-emac.h53
-rw-r--r--include/hw/qdev-clock.h159
-rw-r--r--include/hw/qdev-core.h12
-rw-r--r--include/hw/riscv/boot.h6
-rw-r--r--include/hw/riscv/sifive_u.h3
-rw-r--r--include/hw/s390x/pv.h58
-rw-r--r--include/hw/s390x/s390-virtio-ccw.h1
-rw-r--r--include/hw/s390x/sclp.h2
-rw-r--r--include/qapi/qmp/qdict.h3
-rw-r--r--include/qapi/qmp/qlist.h10
-rw-r--r--include/qapi/visitor-impl.h9
-rw-r--r--include/qapi/visitor.h192
-rw-r--r--include/standard-headers/linux/ethtool.h10
-rw-r--r--include/standard-headers/linux/input-event-codes.h5
-rw-r--r--include/standard-headers/linux/pci_regs.h2
-rw-r--r--include/standard-headers/linux/vhost_types.h8
-rw-r--r--include/standard-headers/linux/virtio_balloon.h12
-rw-r--r--include/standard-headers/linux/virtio_ids.h1
-rw-r--r--include/standard-headers/linux/virtio_net.h102
-rw-r--r--include/sysemu/device_tree.h5
-rw-r--r--linux-headers/COPYING2
-rw-r--r--linux-headers/asm-x86/kvm.h1
-rw-r--r--linux-headers/asm-x86/unistd_32.h1
-rw-r--r--linux-headers/asm-x86/unistd_64.h1
-rw-r--r--linux-headers/asm-x86/unistd_x32.h1
-rw-r--r--linux-headers/linux/kvm.h47
-rw-r--r--linux-headers/linux/mman.h5
-rw-r--r--linux-headers/linux/userfaultfd.h40
-rw-r--r--linux-headers/linux/vfio.h37
-rw-r--r--linux-headers/linux/vhost.h24
-rw-r--r--linux-user/riscv/signal.c3
-rw-r--r--monitor/hmp-cmds.c3
-rw-r--r--monitor/qmp.c5
-rw-r--r--pc-bios/opensbi-riscv32-sifive_u-fw_jump.binbin49472 -> 49520 bytes
-rw-r--r--pc-bios/opensbi-riscv32-virt-fw_jump.binbin41280 -> 49504 bytes
-rw-r--r--pc-bios/opensbi-riscv64-sifive_u-fw_jump.binbin53760 -> 57936 bytes
-rw-r--r--pc-bios/opensbi-riscv64-virt-fw_jump.binbin49664 -> 57920 bytes
-rw-r--r--qapi/qapi-dealloc-visitor.c7
-rw-r--r--qapi/qapi-visit-core.c20
-rw-r--r--qapi/qobject-input-visitor.c21
-rw-r--r--qdev-monitor.c9
-rw-r--r--qobject/qdict.c19
-rw-r--r--qobject/qjson.c107
-rw-r--r--qobject/qlist.c44
-rw-r--r--qom/object.c4
m---------roms/opensbi0
-rw-r--r--scripts/qapi/commands.py60
-rw-r--r--scripts/qapi/visit.py8
-rw-r--r--target/arm/cpu-qom.h9
-rw-r--r--target/arm/cpu.c19
-rw-r--r--target/arm/cpu64.c8
-rw-r--r--target/arm/helper.c17
-rw-r--r--target/arm/helper.h27
-rw-r--r--target/arm/neon_helper.c24
-rw-r--r--target/arm/translate-a64.c64
-rw-r--r--target/arm/translate.c256
-rw-r--r--target/arm/translate.h5
-rw-r--r--target/arm/vec_helper.c25
-rw-r--r--target/riscv/cpu.c10
-rw-r--r--target/riscv/cpu.h1
-rw-r--r--target/riscv/cpu_helper.c18
-rw-r--r--target/s390x/cpu.c27
-rw-r--r--target/s390x/cpu.h7
-rw-r--r--target/s390x/cpu_features_def.inc.h1
-rw-r--r--target/s390x/diag.c75
-rw-r--r--target/s390x/gen-features.c1
-rw-r--r--target/s390x/helper.c6
-rw-r--r--target/s390x/ioinst.c96
-rw-r--r--target/s390x/kvm-stub.c5
-rw-r--r--target/s390x/kvm.c79
-rw-r--r--target/s390x/kvm_s390x.h3
-rw-r--r--target/s390x/mmu_helper.c14
-rw-r--r--tests/Makefile.include1
-rw-r--r--tests/acceptance/boot_linux_console.py15
-rw-r--r--tests/check-qlist.c37
-rw-r--r--tests/test-qobject-output-visitor.c39
-rw-r--r--tests/test-string-output-visitor.c19
-rw-r--r--util/qemu-option.c43
125 files changed, 4078 insertions, 789 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 8cbc1fac2b..8aa8efaf1d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -396,6 +396,8 @@ F: target/s390x/machine.c
F: target/s390x/sigp.c
F: target/s390x/cpu_features*.[ch]
F: target/s390x/cpu_models.[ch]
+F: hw/s390x/pv.c
+F: include/hw/s390x/pv.h
F: hw/intc/s390_flic.c
F: hw/intc/s390_flic_kvm.c
F: include/hw/s390x/s390_flic.h
@@ -919,6 +921,8 @@ F: include/hw/arm/msf2-soc.h
F: include/hw/misc/msf2-sysreg.h
F: include/hw/timer/mss-timer.h
F: include/hw/ssi/mss-spi.h
+F: hw/net/msf2-emac.c
+F: include/hw/net/msf2-emac.h
Emcraft M2S-FG484
M: Subbaraya Sundeep <sundeep.lkml@gmail.com>
diff --git a/block.c b/block.c
index 2e3905c99e..c11385ae05 100644
--- a/block.c
+++ b/block.c
@@ -2982,7 +2982,6 @@ BdrvChild *bdrv_open_child(const char *filename,
BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
{
BlockDriverState *bs = NULL;
- Error *local_err = NULL;
QObject *obj = NULL;
QDict *qdict = NULL;
const char *reference = NULL;
@@ -2995,11 +2994,7 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
assert(ref->type == QTYPE_QDICT);
v = qobject_output_visitor_new(&obj);
- visit_type_BlockdevOptions(v, NULL, &options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto fail;
- }
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
visit_complete(v, &obj);
qdict = qobject_to(QDict, obj);
@@ -3017,8 +3012,6 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
obj = NULL;
-
-fail:
qobject_unref(obj);
visit_free(v);
return bs;
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 59f7ebb171..5f3aead038 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -1854,19 +1854,12 @@ static int sd_create_prealloc(BlockdevOptionsSheepdog *location, int64_t size,
Visitor *v;
QObject *obj = NULL;
QDict *qdict;
- Error *local_err = NULL;
int ret;
v = qobject_output_visitor_new(&obj);
- visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &local_err);
+ visit_type_BlockdevOptionsSheepdog(v, NULL, &location, &error_abort);
visit_free(v);
- if (local_err) {
- error_propagate(errp, local_err);
- qobject_unref(obj);
- return -EINVAL;
- }
-
qdict = qobject_to(QDict, obj);
qdict_flatten(qdict);
diff --git a/blockdev.c b/blockdev.c
index 5faddaa705..9da960b1e7 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -3725,14 +3725,8 @@ void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
QDict *qdict;
- Error *local_err = NULL;
-
- visit_type_BlockdevOptions(v, NULL, &options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto fail;
- }
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
visit_complete(v, &obj);
qdict = qobject_to(QDict, obj);
@@ -3760,7 +3754,6 @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
AioContext *ctx;
QObject *obj;
Visitor *v = qobject_output_visitor_new(&obj);
- Error *local_err = NULL;
BlockReopenQueue *queue;
QDict *qdict;
@@ -3777,12 +3770,7 @@ void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp)
}
/* Put all options in a QDict and flatten it */
- visit_type_BlockdevOptions(v, NULL, &options, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
- goto fail;
- }
-
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
visit_complete(v, &obj);
qdict = qobject_to(QDict, obj);
diff --git a/device_tree.c b/device_tree.c
index bba6cc2164..b335dae707 100644
--- a/device_tree.c
+++ b/device_tree.c
@@ -291,7 +291,7 @@ char **qemu_fdt_node_unit_path(void *fdt, const char *name, Error **errp)
return path_array;
}
-char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
+char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
Error **errp)
{
int offset, len, ret;
@@ -308,7 +308,7 @@ char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
offset = len;
break;
}
- if (!strcmp(iter_name, name)) {
+ if (!name || !strcmp(iter_name, name)) {
char *path;
path = g_malloc(path_len);
diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
new file mode 100644
index 0000000000..e5da28e211
--- /dev/null
+++ b/docs/devel/clocks.rst
@@ -0,0 +1,391 @@
+Modelling a clock tree in QEMU
+==============================
+
+What are clocks?
+----------------
+
+Clocks are QOM objects developed for the purpose of modelling the
+distribution of clocks in QEMU.
+
+They allow us to model the clock distribution of a platform and detect
+configuration errors in the clock tree such as badly configured PLL, clock
+source selection or disabled clock.
+
+The object is *Clock* and its QOM name is ``clock`` (in C code, the macro
+``TYPE_CLOCK``).
+
+Clocks are typically used with devices where they are used to model inputs
+and outputs. They are created in a similar way to GPIOs. Inputs and outputs
+of different devices can be connected together.
+
+In these cases a Clock object is a child of a Device object, but this
+is not a requirement. Clocks can be independent of devices. For
+example it is possible to create a clock outside of any device to
+model the main clock source of a machine.
+
+Here is an example of clocks::
+
+ +---------+ +----------------------+ +--------------+
+ | Clock 1 | | Device B | | Device C |
+ | | | +-------+ +-------+ | | +-------+ |
+ | |>>-+-->>|Clock 2| |Clock 3|>>--->>|Clock 6| |
+ +---------+ | | | (in) | | (out) | | | | (in) | |
+ | | +-------+ +-------+ | | +-------+ |
+ | | +-------+ | +--------------+
+ | | |Clock 4|>>
+ | | | (out) | | +--------------+
+ | | +-------+ | | Device D |
+ | | +-------+ | | +-------+ |
+ | | |Clock 5|>>--->>|Clock 7| |
+ | | | (out) | | | | (in) | |
+ | | +-------+ | | +-------+ |
+ | +----------------------+ | |
+ | | +-------+ |
+ +----------------------------->>|Clock 8| |
+ | | (in) | |
+ | +-------+ |
+ +--------------+
+
+Clocks are defined in the ``include/hw/clock.h`` header and device
+related functions are defined in the ``include/hw/qdev-clock.h``
+header.
+
+The clock state
+---------------
+
+The state of a clock is its period; it is stored as an integer
+representing it in units of 2 :sup:`-32` ns. The special value of 0 is used to
+represent the clock being inactive or gated. The clocks do not model
+the signal itself (pin toggling) or other properties such as the duty
+cycle.
+
+All clocks contain this state: outputs as well as inputs. This allows
+the current period of a clock to be fetched at any time. When a clock
+is updated, the value is immediately propagated to all connected
+clocks in the tree.
+
+To ease interaction with clocks, helpers with a unit suffix are defined for
+every clock state setter or getter. The suffixes are:
+
+- ``_ns`` for handling periods in nanoseconds
+- ``_hz`` for handling frequencies in hertz
+
+The 0 period value is converted to 0 in hertz and vice versa. 0 always means
+that the clock is disabled.
+
+Adding a new clock
+------------------
+
+Adding clocks to a device must be done during the init method of the Device
+instance.
+
+To add an input clock to a device, the function ``qdev_init_clock_in()``
+must be used. It takes the name, a callback and an opaque parameter
+for the callback (this will be explained in a following section).
+Output is simpler; only the name is required. Typically::
+
+ qdev_init_clock_in(DEVICE(dev), "clk_in", clk_in_callback, dev);
+ qdev_init_clock_out(DEVICE(dev), "clk_out");
+
+Both functions return the created Clock pointer, which should be saved in the
+device's state structure for further use.
+
+These objects will be automatically deleted by the QOM reference mechanism.
+
+Note that it is possible to create a static array describing clock inputs and
+outputs. The function ``qdev_init_clocks()`` must be called with the array as
+parameter to initialize the clocks: it has the same behaviour as calling the
+``qdev_init_clock_in/out()`` for each clock in the array. To ease the array
+construction, some macros are defined in ``include/hw/qdev-clock.h``.
+As an example, the following creates 2 clocks to a device: one input and one
+output.
+
+.. code-block:: c
+
+ /* device structure containing pointers to the clock objects */
+ typedef struct MyDeviceState {
+ DeviceState parent_obj;
+ Clock *clk_in;
+ Clock *clk_out;
+ } MyDeviceState;
+
+ /*
+ * callback for the input clock (see "Callback on input clock
+ * change" section below for more information).
+ */
+ static void clk_in_callback(void *opaque);
+
+ /*
+ * static array describing clocks:
+ * + a clock input named "clk_in", whose pointer is stored in
+ * the clk_in field of a MyDeviceState structure with callback
+ * clk_in_callback.
+ * + a clock output named "clk_out" whose pointer is stored in
+ * the clk_out field of a MyDeviceState structure.
+ */
+ static const ClockPortInitArray mydev_clocks = {
+ QDEV_CLOCK_IN(MyDeviceState, clk_in, clk_in_callback),
+ QDEV_CLOCK_OUT(MyDeviceState, clk_out),
+ QDEV_CLOCK_END
+ };
+
+ /* device initialization function */
+ static void mydev_init(Object *obj)
+ {
+ /* cast to MyDeviceState */
+ MyDeviceState *mydev = MYDEVICE(obj);
+ /* create and fill the pointer fields in the MyDeviceState */
+ qdev_init_clocks(mydev, mydev_clocks);
+ [...]
+ }
+
+An alternative way to create a clock is to simply call
+``object_new(TYPE_CLOCK)``. In that case the clock will neither be an
+input nor an output of a device. After the whole QOM hierarchy of the
+clock has been set ``clock_setup_canonical_path()`` should be called.
+
+At creation, the period of the clock is 0: the clock is disabled. You can
+change it using ``clock_set_ns()`` or ``clock_set_hz()``.
+
+Note that if you are creating a clock with a fixed period which will never
+change (for example the main clock source of a board), then you'll have
+nothing else to do. This value will be propagated to other clocks when
+connecting the clocks together and devices will fetch the right value during
+the first reset.
+
+Retrieving clocks from a device
+-------------------------------
+
+``qdev_get_clock_in()`` and ``dev_get_clock_out()`` are available to
+get the clock inputs or outputs of a device. For example:
+
+.. code-block:: c
+
+ Clock *clk = qdev_get_clock_in(DEVICE(mydev), "clk_in");
+
+or:
+
+.. code-block:: c
+
+ Clock *clk = qdev_get_clock_out(DEVICE(mydev), "clk_out");
+
+Connecting two clocks together
+------------------------------
+
+To connect two clocks together, use the ``clock_set_source()`` function.
+Given two clocks ``clk1``, and ``clk2``, ``clock_set_source(clk2, clk1);``
+configures ``clk2`` to follow the ``clk1`` period changes. Every time ``clk1``
+is updated, ``clk2`` will be updated too.
+
+When connecting clock between devices, prefer using the
+``qdev_connect_clock_in()`` function to set the source of an input
+device clock. For example, to connect the input clock ``clk2`` of
+``devB`` to the output clock ``clk1`` of ``devA``, do:
+
+.. code-block:: c
+
+ qdev_connect_clock_in(devB, "clk2", qdev_get_clock_out(devA, "clk1"))
+
+We used ``qdev_get_clock_out()`` above, but any clock can drive an
+input clock, even another input clock. The following diagram shows
+some examples of connections. Note also that a clock can drive several
+other clocks.
+
+::
+
+ +------------+ +--------------------------------------------------+
+ | Device A | | Device B |
+ | | | +---------------------+ |
+ | | | | Device C | |
+ | +-------+ | | +-------+ | +-------+ +-------+ | +-------+ |
+ | |Clock 1|>>-->>|Clock 2|>>+-->>|Clock 3| |Clock 5|>>>>|Clock 6|>>
+ | | (out) | | | | (in) | | | | (in) | | (out) | | | (out) | |
+ | +-------+ | | +-------+ | | +-------+ +-------+ | +-------+ |
+ +------------+ | | +---------------------+ |
+ | | |
+ | | +--------------+ |
+ | | | Device D | |
+ | | | +-------+ | |
+ | +-->>|Clock 4| | |
+ | | | (in) | | |
+ | | +-------+ | |
+ | +--------------+ |
+ +--------------------------------------------------+
+
+In the above example, when *Clock 1* is updated by *Device A*, three
+clocks get the new clock period value: *Clock 2*, *Clock 3* and *Clock 4*.
+
+It is not possible to disconnect a clock or to change the clock connection
+after it is connected.
+
+Unconnected input clocks
+------------------------
+
+A newly created input clock is disabled (period of 0). This means the
+clock will be considered as disabled until the period is updated. If
+the clock remains unconnected it will always keep its initial value
+of 0. If this is not the desired behaviour, ``clock_set()``,
+``clock_set_ns()`` or ``clock_set_hz()`` should be called on the Clock
+object during device instance init. For example:
+
+.. code-block:: c
+
+ clk = qdev_init_clock_in(DEVICE(dev), "clk-in", clk_in_callback,
+ dev);
+ /* set initial value to 10ns / 100MHz */
+ clock_set_ns(clk, 10);
+
+Fetching clock frequency/period
+-------------------------------
+
+To get the current state of a clock, use the functions ``clock_get()``,
+``clock_get_ns()`` or ``clock_get_hz()``.
+
+It is also possible to register a callback on clock frequency changes.
+Here is an example:
+
+.. code-block:: c
+
+ void clock_callback(void *opaque) {
+ MyDeviceState *s = (MyDeviceState *) opaque;
+ /*
+ * 'opaque' is the argument passed to qdev_init_clock_in();
+ * usually this will be the device state pointer.
+ */
+
+ /* do something with the new period */
+ fprintf(stdout, "device new period is %" PRIu64 "ns\n",
+ clock_get_ns(dev->my_clk_input));
+ }
+
+Changing a clock period
+-----------------------
+
+A device can change its outputs using the ``clock_update()``,
+``clock_update_ns()`` or ``clock_update_hz()`` function. It will trigger
+updates on every connected input.
+
+For example, let's say that we have an output clock *clkout* and we
+have a pointer to it in the device state because we did the following
+in init phase:
+
+.. code-block:: c
+
+ dev->clkout = qdev_init_clock_out(DEVICE(dev), "clkout");
+
+Then at any time (apart from the cases listed below), it is possible to
+change the clock value by doing:
+
+.. code-block:: c
+
+ clock_update_hz(dev->clkout, 1000 * 1000 * 1000); /* 1GHz */
+
+Because updating a clock may trigger any side effects through
+connected clocks and their callbacks, this operation must be done
+while holding the qemu io lock.
+
+For the same reason, one can update clocks only when it is allowed to have
+side effects on other objects. In consequence, it is forbidden:
+
+* during migration,
+* and in the enter phase of reset.
+
+Note that calling ``clock_update[_ns|_hz]()`` is equivalent to calling
+``clock_set[_ns|_hz]()`` (with the same arguments) then
+``clock_propagate()`` on the clock. Thus, setting the clock value can
+be separated from triggering the side-effects. This is often required
+to factorize code to handle reset and migration in devices.
+
+Aliasing clocks
+---------------
+
+Sometimes, one needs to forward, or inherit, a clock from another
+device. Typically, when doing device composition, a device might
+expose a sub-device's clock without interfering with it. The function
+``qdev_alias_clock()`` can be used to achieve this behaviour. Note
+that it is possible to expose the clock under a different name.
+``qdev_alias_clock()`` works for both input and output clocks.
+
+For example, if device B is a child of device A,
+``device_a_instance_init()`` may do something like this:
+
+.. code-block:: c
+
+ void device_a_instance_init(Object *obj)
+ {
+ AState *A = DEVICE_A(obj);
+ BState *B;
+ /* create object B as child of A */
+ [...]
+ qdev_alias_clock(B, "clk", A, "b_clk");
+ /*
+ * Now A has a clock "b_clk" which is an alias to
+ * the clock "clk" of its child B.
+ */
+ }
+
+This function does not return any clock object. The new clock has the
+same direction (input or output) as the original one. This function
+only adds a link to the existing clock. In the above example, object B
+remains the only object allowed to use the clock and device A must not
+try to change the clock period or set a callback to the clock. This
+diagram describes the example with an input clock::
+
+ +--------------------------+
+ | Device A |
+ | +--------------+ |
+ | | Device B | |
+ | | +-------+ | |
+ >>"b_clk">>>| "clk" | | |
+ | (in) | | (in) | | |
+ | | +-------+ | |
+ | +--------------+ |
+ +--------------------------+
+
+Migration
+---------
+
+Clock state is not migrated automatically. Every device must handle its
+clock migration. Alias clocks must not be migrated.
+
+To ensure clock states are restored correctly during migration, there
+are two solutions.
+
+Clock states can be migrated by adding an entry into the device
+vmstate description. You should use the ``VMSTATE_CLOCK`` macro for this.
+This is typically used to migrate an input clock state. For example:
+
+.. code-block:: c
+
+ MyDeviceState {
+ DeviceState parent_obj;
+ [...] /* some fields */
+ Clock *clk;
+ };
+
+ VMStateDescription my_device_vmstate = {
+ .name = "my_device",
+ .fields = (VMStateField[]) {
+ [...], /* other migrated fields */
+ VMSTATE_CLOCK(clk, MyDeviceState),
+ VMSTATE_END_OF_LIST()
+ }
+ };
+
+The second solution is to restore the clock state using information already
+at our disposal. This can be used to restore output clock states using the
+device state. The functions ``clock_set[_ns|_hz]()`` can be used during the
+``post_load()`` migration callback.
+
+When adding clock support to an existing device, if you care about
+migration compatibility you will need to be careful, as simply adding
+a ``VMSTATE_CLOCK()`` line will break compatibility. Instead, you can
+put the ``VMSTATE_CLOCK()`` line into a vmstate subsection with a
+suitable ``needed`` function, and use ``clock_set()`` in a
+``pre_load()`` function to set the default value that will be used if
+the source virtual machine in the migration does not send the clock
+state.
+
+Care should be taken not to use ``clock_update[_ns|_hz]()`` or
+``clock_propagate()`` during the whole migration procedure because it
+will trigger side effects to other devices in an unknown state.
diff --git a/docs/devel/index.rst b/docs/devel/index.rst
index a9e1200dff..bb8238c5d6 100644
--- a/docs/devel/index.rst
+++ b/docs/devel/index.rst
@@ -27,3 +27,4 @@ Contents:
bitops
reset
s390-dasd-ipl
+ clocks
diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt
index 1967adfa92..a7794ef658 100644
--- a/docs/devel/qapi-code-gen.txt
+++ b/docs/devel/qapi-code-gen.txt
@@ -1446,6 +1446,8 @@ Example:
goto out;
}
if (!*obj) {
+ /* incomplete */
+ assert(visit_is_dealloc(v));
goto out_obj;
}
visit_type_UserDefOne_members(v, *obj, &err);
@@ -1577,8 +1579,8 @@ Example:
void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp)
{
Error *err = NULL;
- UserDefOne *retval;
Visitor *v;
+ UserDefOne *retval;
q_obj_my_command_arg arg = {0};
v = qobject_input_visitor_new(QOBJECT(args));
diff --git a/docs/system/s390x/protvirt.rst b/docs/system/s390x/protvirt.rst
new file mode 100644
index 0000000000..712974ad87
--- /dev/null
+++ b/docs/system/s390x/protvirt.rst
@@ -0,0 +1,60 @@
+Protected Virtualization on s390x
+=================================
+
+The memory and most of the registers of Protected Virtual Machines
+(PVMs) are encrypted or inaccessible to the hypervisor, effectively
+prohibiting VM introspection when the VM is running. At rest, PVMs are
+encrypted and can only be decrypted by the firmware, represented by an
+entity called Ultravisor, of specific IBM Z machines.
+
+
+Prerequisites
+-------------
+
+To run PVMs, a machine with the Protected Virtualization feature, as
+indicated by the Ultravisor Call facility (stfle bit 158), is
+required. The Ultravisor needs to be initialized at boot by setting
+`prot_virt=1` on the host's kernel command line.
+
+Running PVMs requires using the KVM hypervisor.
+
+If those requirements are met, the capability `KVM_CAP_S390_PROTECTED`
+will indicate that KVM can support PVMs on that LPAR.
+
+
+QEMU Settings
+-------------
+
+To indicate to the VM that it can transition into protected mode, the
+`Unpack facility` (stfle bit 161 represented by the feature
+`unpack`/`S390_FEAT_UNPACK`) needs to be part of the cpu model of
+the VM.
+
+All I/O devices need to use the IOMMU.
+Passthrough (vfio) devices are currently not supported.
+
+Host huge page backings are not supported. However guests can use huge
+pages as indicated by its facilities.
+
+
+Boot Process
+------------
+
+A secure guest image can either be loaded from disk or supplied on the
+QEMU command line. Booting from disk is done by the unmodified
+s390-ccw BIOS. I.e., the bootmap is interpreted, multiple components
+are read into memory and control is transferred to one of the
+components (zipl stage3). Stage3 does some fixups and then transfers
+control to some program residing in guest memory, which is normally
+the OS kernel. The secure image has another component prepended
+(stage3a) that uses the new diag308 subcodes 8 and 10 to trigger the
+transition into secure mode.
+
+Booting from the image supplied on the QEMU command line requires that
+the file passed via -kernel has the same memory layout as would result
+from the disk boot. This memory layout includes the encrypted
+components (kernel, initrd, cmdline), the stage3a loader and
+metadata. In case this boot method is used, the command line
+options -initrd and -cmdline are ineffective. The preparation of a PVM
+image is done via the `genprotimg` tool from the s390-tools
+collection.
diff --git a/docs/system/target-s390x.rst b/docs/system/target-s390x.rst
index 4c8b7cdd66..7d76ae97b4 100644
--- a/docs/system/target-s390x.rst
+++ b/docs/system/target-s390x.rst
@@ -24,3 +24,8 @@ or vfio-ap is also available.
.. toctree::
s390x/vfio-ap
+Architectural features
+======================
+
+.. toctree::
+ s390x/protvirt
diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c
index e2c957ce00..3d6a500fb7 100644
--- a/hw/acpi/cpu.c
+++ b/hw/acpi/cpu.c
@@ -222,7 +222,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner,
state->devs[i].arch_id = id_list->cpus[i].arch_id;
}
memory_region_init_io(&state->ctrl_reg, owner, &cpu_hotplug_ops, state,
- "acpi-mem-hotplug", ACPI_CPU_HOTPLUG_REG_LEN);
+ "acpi-cpu-hotplug", ACPI_CPU_HOTPLUG_REG_LEN);
memory_region_add_subregion(as, base_addr, &state->ctrl_reg);
}
diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c
index 588d643b8d..a455b8831f 100644
--- a/hw/arm/msf2-soc.c
+++ b/hw/arm/msf2-soc.c
@@ -1,7 +1,7 @@
/*
* SmartFusion2 SoC emulation.
*
- * Copyright (c) 2017 Subbaraya Sundeep <sundeep.lkml@gmail.com>
+ * Copyright (c) 2017-2020 Subbaraya Sundeep <sundeep.lkml@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -35,11 +35,14 @@
#define MSF2_TIMER_BASE 0x40004000
#define MSF2_SYSREG_BASE 0x40038000
+#define MSF2_EMAC_BASE 0x40041000
#define ENVM_BASE_ADDRESS 0x60000000
#define SRAM_BASE_ADDRESS 0x20000000
+#define MSF2_EMAC_IRQ 12
+
#define MSF2_ENVM_MAX_SIZE (512 * KiB)
/*
@@ -81,6 +84,13 @@ static void m2sxxx_soc_initfn(Object *obj)
sysbus_init_child_obj(obj, "spi[*]", &s->spi[i], sizeof(s->spi[i]),
TYPE_MSS_SPI);
}
+
+ sysbus_init_child_obj(obj, "emac", &s->emac, sizeof(s->emac),
+ TYPE_MSS_EMAC);
+ if (nd_table[0].used) {
+ qemu_check_nic_model(&nd_table[0], TYPE_MSS_EMAC);
+ qdev_set_nic_properties(DEVICE(&s->emac), &nd_table[0]);
+ }
}
static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
@@ -192,6 +202,19 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
g_free(bus_name);
}
+ dev = DEVICE(&s->emac);
+ object_property_set_link(OBJECT(&s->emac), OBJECT(get_system_memory()),
+ "ahb-bus", &error_abort);
+ object_property_set_bool(OBJECT(&s->emac), true, "realized", &err);
+ if (err != NULL) {
+ error_propagate(errp, err);
+ return;
+ }
+ busdev = SYS_BUS_DEVICE(dev);
+ sysbus_mmio_map(busdev, 0, MSF2_EMAC_BASE);
+ sysbus_connect_irq(busdev, 0,
+ qdev_get_gpio_in(armv7m, MSF2_EMAC_IRQ));
+
/* Below devices are not modelled yet. */
create_unimplemented_device("i2c_0", 0x40002000, 0x1000);
create_unimplemented_device("dma", 0x40003000, 0x1000);
@@ -202,7 +225,6 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("can", 0x40015000, 0x1000);
create_unimplemented_device("rtc", 0x40017000, 0x1000);
create_unimplemented_device("apb_config", 0x40020000, 0x10000);
- create_unimplemented_device("emac", 0x40041000, 0x1000);
create_unimplemented_device("usb", 0x40043000, 0x1000);
}
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index cca5316256..626822554d 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -77,6 +77,7 @@
#include "hw/acpi/generic_event_device.h"
#include "hw/virtio/virtio-iommu.h"
#include "hw/char/pl011.h"
+#include "qemu/guest-random.h"
#define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \
static void virt_##major##_##minor##_class_init(ObjectClass *oc, \
@@ -213,6 +214,18 @@ static bool cpu_type_valid(const char *cpu)
return false;
}
+static void create_kaslr_seed(VirtMachineState *vms, const char *node)
+{
+ Error *err = NULL;
+ uint64_t seed;
+
+ if (qemu_guest_getrandom(&seed, sizeof(seed), &err)) {
+ error_free(err);
+ return;
+ }
+ qemu_fdt_setprop_u64(vms->fdt, node, "kaslr-seed", seed);
+}
+
static void create_fdt(VirtMachineState *vms)
{
MachineState *ms = MACHINE(vms);
@@ -233,6 +246,12 @@ static void create_fdt(VirtMachineState *vms)
/* /chosen must exist for load_dtb to fill in necessary properties later */
qemu_fdt_add_subnode(fdt, "/chosen");
+ create_kaslr_seed(vms, "/chosen");
+
+ if (vms->secure) {
+ qemu_fdt_add_subnode(fdt, "/secure-chosen");
+ create_kaslr_seed(vms, "/secure-chosen");
+ }
/* Clock node, for the benefit of the UART. The kernel device tree
* binding documentation claims the PL011 node clock properties are
@@ -761,7 +780,6 @@ static void create_uart(const VirtMachineState *vms, int uart,
qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled");
qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay");
- qemu_fdt_add_subnode(vms->fdt, "/secure-chosen");
qemu_fdt_setprop_string(vms->fdt, "/secure-chosen", "stdout-path",
nodename);
}
diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c
index 571cdcd599..91b498dd5d 100644
--- a/hw/arm/xilinx_zynq.c
+++ b/hw/arm/xilinx_zynq.c
@@ -35,6 +35,15 @@
#include "hw/char/cadence_uart.h"
#include "hw/net/cadence_gem.h"
#include "hw/cpu/a9mpcore.h"
+#include "hw/qdev-clock.h"
+#include "sysemu/reset.h"
+
+#define TYPE_ZYNQ_MACHINE MACHINE_TYPE_NAME("xilinx-zynq-a9")
+#define ZYNQ_MACHINE(obj) \
+ OBJECT_CHECK(ZynqMachineState, (obj), TYPE_ZYNQ_MACHINE)
+
+/* board base frequency: 33.333333 MHz */
+#define PS_CLK_FREQUENCY (100 * 1000 * 1000 / 3)
#define NUM_SPI_FLASHES 4
#define NUM_QSPI_FLASHES 2
@@ -75,6 +84,11 @@ static const int dma_irqs[8] = {
0xe3401000 + ARMV7_IMM16(extract32((val), 16, 16)), /* movt r1 ... */ \
0xe5801000 + (addr)
+typedef struct ZynqMachineState {
+ MachineState parent;
+ Clock *ps_clk;
+} ZynqMachineState;
+
static void zynq_write_board_setup(ARMCPU *cpu,
const struct arm_boot_info *info)
{
@@ -159,10 +173,11 @@ static inline void zynq_init_spi_flashes(uint32_t base_addr, qemu_irq irq,
static void zynq_init(MachineState *machine)
{
+ ZynqMachineState *zynq_machine = ZYNQ_MACHINE(machine);
ARMCPU *cpu;
MemoryRegion *address_space_mem = get_system_memory();
MemoryRegion *ocm_ram = g_new(MemoryRegion, 1);
- DeviceState *dev;
+ DeviceState *dev, *slcr;
SysBusDevice *busdev;
qemu_irq pic[64];
int n;
@@ -206,9 +221,18 @@ static void zynq_init(MachineState *machine)
1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa,
0);
- dev = qdev_create(NULL, "xilinx,zynq_slcr");
- qdev_init_nofail(dev);
- sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0xF8000000);
+ /* Create slcr, keep a pointer to connect clocks */
+ slcr = qdev_create(NULL, "xilinx,zynq_slcr");
+ qdev_init_nofail(slcr);
+ sysbus_mmio_map(SYS_BUS_DEVICE(slcr), 0, 0xF8000000);
+
+ /* Create the main clock source, and feed slcr with it */
+ zynq_machine->ps_clk = CLOCK(object_new(TYPE_CLOCK));
+ object_property_add_child(OBJECT(zynq_machine), "ps_clk",
+ OBJECT(zynq_machine->ps_clk), &error_abort);
+ object_unref(OBJECT(zynq_machine->ps_clk));
+ clock_set_hz(zynq_machine->ps_clk, PS_CLK_FREQUENCY);
+ qdev_connect_clock_in(slcr, "ps_clk", zynq_machine->ps_clk);
dev = qdev_create(NULL, TYPE_A9MPCORE_PRIV);
qdev_prop_set_uint32(dev, "num-cpu", 1);
@@ -229,8 +253,12 @@ static void zynq_init(MachineState *machine)
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0002000, pic[53 - IRQ_OFFSET]);
sysbus_create_simple(TYPE_CHIPIDEA, 0xE0003000, pic[76 - IRQ_OFFSET]);
- cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0));
- cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1));
+ dev = cadence_uart_create(0xE0000000, pic[59 - IRQ_OFFSET], serial_hd(0));
+ qdev_connect_clock_in(dev, "refclk",
+ qdev_get_clock_out(slcr, "uart0_ref_clk"));
+ dev = cadence_uart_create(0xE0001000, pic[82 - IRQ_OFFSET], serial_hd(1));
+ qdev_connect_clock_in(dev, "refclk",
+ qdev_get_clock_out(slcr, "uart1_ref_clk"));
sysbus_create_varargs("cadence_ttc", 0xF8001000,
pic[42-IRQ_OFFSET], pic[43-IRQ_OFFSET], pic[44-IRQ_OFFSET], NULL);
@@ -308,8 +336,9 @@ static void zynq_init(MachineState *machine)
arm_load_kernel(ARM_CPU(first_cpu), machine, &zynq_binfo);
}
-static void zynq_machine_init(MachineClass *mc)
+static void zynq_machine_class_init(ObjectClass *oc, void *data)
{
+ MachineClass *mc = MACHINE_CLASS(oc);
mc->desc = "Xilinx Zynq Platform Baseboard for Cortex-A9";
mc->init = zynq_init;
mc->max_cpus = 1;
@@ -319,4 +348,16 @@ static void zynq_machine_init(MachineClass *mc)
mc->default_ram_id = "zynq.ext_ram";
}
-DEFINE_MACHINE("xilinx-zynq-a9", zynq_machine_init)
+static const TypeInfo zynq_machine_type = {
+ .name = TYPE_ZYNQ_MACHINE,
+ .parent = TYPE_MACHINE,
+ .class_init = zynq_machine_class_init,
+ .instance_size = sizeof(ZynqMachineState),
+};
+
+static void zynq_machine_register_types(void)
+{
+ type_register_static(&zynq_machine_type);
+}
+
+type_init(zynq_machine_register_types)
diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c
index cb0122a3a6..94460f2343 100644
--- a/hw/arm/xlnx-versal.c
+++ b/hw/arm/xlnx-versal.c
@@ -205,6 +205,8 @@ static void versal_create_admas(Versal *s, qemu_irq *pic)
dev = qdev_create(NULL, "xlnx.zdma");
s->lpd.iou.adma[i] = SYS_BUS_DEVICE(dev);
+ object_property_set_int(OBJECT(s->lpd.iou.adma[i]), 128, "bus-width",
+ &error_abort);
object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal);
qdev_init_nofail(dev);
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index bd645ad818..a798e228b7 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -23,6 +23,7 @@
#include "qemu/error-report.h"
#include "qemu/log.h"
#include "sysemu/qtest.h"
+#include "sysemu/device_tree.h"
typedef struct XlnxZCU102 {
MachineState parent_obj;
@@ -31,13 +32,14 @@ typedef struct XlnxZCU102 {
bool secure;
bool virt;
+
+ struct arm_boot_info binfo;
} XlnxZCU102;
#define TYPE_ZCU102_MACHINE MACHINE_TYPE_NAME("xlnx-zcu102")
#define ZCU102_MACHINE(obj) \
OBJECT_CHECK(XlnxZCU102, (obj), TYPE_ZCU102_MACHINE)
-static struct arm_boot_info xlnx_zcu102_binfo;
static bool zcu102_get_secure(Object *obj, Error **errp)
{
@@ -67,6 +69,34 @@ static void zcu102_set_virt(Object *obj, bool value, Error **errp)
s->virt = value;
}
+static void zcu102_modify_dtb(const struct arm_boot_info *binfo, void *fdt)
+{
+ XlnxZCU102 *s = container_of(binfo, XlnxZCU102, binfo);
+ bool method_is_hvc;
+ char **node_path;
+ const char *r;
+ int prop_len;
+ int i;
+
+ /* If EL3 is enabled, we keep all firmware nodes active. */
+ if (!s->secure) {
+ node_path = qemu_fdt_node_path(fdt, NULL, "xlnx,zynqmp-firmware",
+ &error_fatal);
+
+ for (i = 0; node_path && node_path[i]; i++) {
+ r = qemu_fdt_getprop(fdt, node_path[i], "method", &prop_len, NULL);
+ method_is_hvc = r && !strcmp("hvc", r);
+
+ /* Allow HVC based firmware if EL2 is enabled. */
+ if (method_is_hvc && s->virt) {
+ continue;
+ }
+ qemu_fdt_setprop_string(fdt, node_path[i], "status", "disabled");
+ }
+ g_strfreev(node_path);
+ }
+}
+
static void xlnx_zcu102_init(MachineState *machine)
{
XlnxZCU102 *s = ZCU102_MACHINE(machine);
@@ -166,9 +196,10 @@ static void xlnx_zcu102_init(MachineState *machine)
/* TODO create and connect IDE devices for ide_drive_get() */
- xlnx_zcu102_binfo.ram_size = ram_size;
- xlnx_zcu102_binfo.loader_start = 0;
- arm_load_kernel(s->soc.boot_cpu_ptr, machine, &xlnx_zcu102_binfo);
+ s->binfo.ram_size = ram_size;
+ s->binfo.loader_start = 0;
+ s->binfo.modify_dtb = zcu102_modify_dtb;
+ arm_load_kernel(s->soc.boot_cpu_ptr, machine, &s->binfo);
}
static void xlnx_zcu102_machine_instance_init(Object *obj)
diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c
index 22e47972f1..e196906c92 100644
--- a/hw/char/cadence_uart.c
+++ b/hw/char/cadence_uart.c
@@ -31,6 +31,8 @@
#include "qemu/module.h"
#include "hw/char/cadence_uart.h"
#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "trace.h"
#ifdef CADENCE_UART_ERR_DEBUG
#define DB_PRINT(...) do { \
@@ -97,7 +99,7 @@
#define LOCAL_LOOPBACK (0x2 << UART_MR_CHMODE_SH)
#define REMOTE_LOOPBACK (0x3 << UART_MR_CHMODE_SH)
-#define UART_INPUT_CLK 50000000
+#define UART_DEFAULT_REF_CLK (50 * 1000 * 1000)
#define R_CR (0x00/4)
#define R_MR (0x04/4)
@@ -171,12 +173,15 @@ static void uart_send_breaks(CadenceUARTState *s)
static void uart_parameters_setup(CadenceUARTState *s)
{
QEMUSerialSetParams ssp;
- unsigned int baud_rate, packet_size;
+ unsigned int baud_rate, packet_size, input_clk;
+ input_clk = clock_get_hz(s->refclk);
- baud_rate = (s->r[R_MR] & UART_MR_CLKS) ?
- UART_INPUT_CLK / 8 : UART_INPUT_CLK;
+ baud_rate = (s->r[R_MR] & UART_MR_CLKS) ? input_clk / 8 : input_clk;
+ baud_rate /= (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
+ trace_cadence_uart_baudrate(baud_rate);
+
+ ssp.speed = baud_rate;
- ssp.speed = baud_rate / (s->r[R_BRGR] * (s->r[R_BDIV] + 1));
packet_size = 1;
switch (s->r[R_MR] & UART_MR_PAR) {
@@ -215,6 +220,13 @@ static void uart_parameters_setup(CadenceUARTState *s)
}
packet_size += ssp.data_bits + ssp.stop_bits;
+ if (ssp.speed == 0) {
+ /*
+ * Avoid division-by-zero below.
+ * TODO: find something better
+ */
+ ssp.speed = 1;
+ }
s->char_tx_time = (NANOSECONDS_PER_SECOND / ssp.speed) * packet_size;
qemu_chr_fe_ioctl(&s->chr, CHR_IOCTL_SERIAL_SET_PARAMS, &ssp);
}
@@ -340,6 +352,11 @@ static void uart_receive(void *opaque, const uint8_t *buf, int size)
CadenceUARTState *s = opaque;
uint32_t ch_mode = s->r[R_MR] & UART_MR_CHMODE;
+ /* ignore characters when unclocked or in reset */
+ if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) {
+ return;
+ }
+
if (ch_mode == NORMAL_MODE || ch_mode == ECHO_MODE) {
uart_write_rx_fifo(opaque, buf, size);
}
@@ -353,6 +370,11 @@ static void uart_event(void *opaque, QEMUChrEvent event)
CadenceUARTState *s = opaque;
uint8_t buf = '\0';
+ /* ignore characters when unclocked or in reset */
+ if (!clock_is_enabled(s->refclk) || device_is_in_reset(DEVICE(s))) {
+ return;
+ }
+
if (event == CHR_EVENT_BREAK) {
uart_write_rx_fifo(opaque, &buf, 1);
}
@@ -462,9 +484,9 @@ static const MemoryRegionOps uart_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void cadence_uart_reset(DeviceState *dev)
+static void cadence_uart_reset_init(Object *obj, ResetType type)
{
- CadenceUARTState *s = CADENCE_UART(dev);
+ CadenceUARTState *s = CADENCE_UART(obj);
s->r[R_CR] = 0x00000128;
s->r[R_IMR] = 0;
@@ -473,6 +495,11 @@ static void cadence_uart_reset(DeviceState *dev)
s->r[R_BRGR] = 0x0000028B;
s->r[R_BDIV] = 0x0000000F;
s->r[R_TTRIG] = 0x00000020;
+}
+
+static void cadence_uart_reset_hold(Object *obj)
+{
+ CadenceUARTState *s = CADENCE_UART(obj);
uart_rx_reset(s);
uart_tx_reset(s);
@@ -491,6 +518,14 @@ static void cadence_uart_realize(DeviceState *dev, Error **errp)
uart_event, NULL, s, NULL, true);
}
+static void cadence_uart_refclk_update(void *opaque)
+{
+ CadenceUARTState *s = opaque;
+
+ /* recompute uart's speed on clock change */
+ uart_parameters_setup(s);
+}
+
static void cadence_uart_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -500,9 +535,23 @@ static void cadence_uart_init(Object *obj)
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->irq);
+ s->refclk = qdev_init_clock_in(DEVICE(obj), "refclk",
+ cadence_uart_refclk_update, s);
+ /* initialize the frequency in case the clock remains unconnected */
+ clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
+
s->char_tx_time = (NANOSECONDS_PER_SECOND / 9600) * 10;
}
+static int cadence_uart_pre_load(void *opaque)
+{
+ CadenceUARTState *s = opaque;
+
+ /* the frequency will be overriden if the refclk field is present */
+ clock_set_hz(s->refclk, UART_DEFAULT_REF_CLK);
+ return 0;
+}
+
static int cadence_uart_post_load(void *opaque, int version_id)
{
CadenceUARTState *s = opaque;
@@ -521,8 +570,9 @@ static int cadence_uart_post_load(void *opaque, int version_id)
static const VMStateDescription vmstate_cadence_uart = {
.name = "cadence_uart",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 2,
+ .pre_load = cadence_uart_pre_load,
.post_load = cadence_uart_post_load,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(r, CadenceUARTState, CADENCE_UART_R_MAX),
@@ -534,8 +584,9 @@ static const VMStateDescription vmstate_cadence_uart = {
VMSTATE_UINT32(tx_count, CadenceUARTState),
VMSTATE_UINT32(rx_wpos, CadenceUARTState),
VMSTATE_TIMER_PTR(fifo_trigger_handle, CadenceUARTState),
+ VMSTATE_CLOCK_V(refclk, CadenceUARTState, 3),
VMSTATE_END_OF_LIST()
- }
+ },
};
static Property cadence_uart_properties[] = {
@@ -546,10 +597,12 @@ static Property cadence_uart_properties[] = {
static void cadence_uart_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->realize = cadence_uart_realize;
dc->vmsd = &vmstate_cadence_uart;
- dc->reset = cadence_uart_reset;
+ rc->phases.enter = cadence_uart_reset_init;
+ rc->phases.hold = cadence_uart_reset_hold;
device_class_set_props(dc, cadence_uart_properties);
}
diff --git a/hw/char/trace-events b/hw/char/trace-events
index 6f938301d9..d20eafd56f 100644
--- a/hw/char/trace-events
+++ b/hw/char/trace-events
@@ -97,3 +97,6 @@ exynos_uart_wo_read(uint32_t channel, const char *name, uint32_t reg) "UART%d: T
exynos_uart_rxsize(uint32_t channel, uint32_t size) "UART%d: Rx FIFO size: %d"
exynos_uart_channel_error(uint32_t channel) "Wrong UART channel number: %d"
exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) "UART%d: Rx timeout stat=0x%x intsp=0x%x"
+
+# hw/char/cadence_uart.c
+cadence_uart_baudrate(unsigned baudrate) "baudrate %u"
diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs
index 6215e7c208..1d540ed6e7 100644
--- a/hw/core/Makefile.objs
+++ b/hw/core/Makefile.objs
@@ -7,6 +7,7 @@ common-obj-y += hotplug.o
common-obj-y += vmstate-if.o
# irq.o needed for qdev GPIO handling:
common-obj-y += irq.o
+common-obj-y += clock.o qdev-clock.o
common-obj-$(CONFIG_SOFTMMU) += reset.o
common-obj-$(CONFIG_SOFTMMU) += qdev-fw.o
@@ -20,6 +21,7 @@ common-obj-$(CONFIG_SOFTMMU) += null-machine.o
common-obj-$(CONFIG_SOFTMMU) += loader.o
common-obj-$(CONFIG_SOFTMMU) += machine-hmp-cmds.o
common-obj-$(CONFIG_SOFTMMU) += numa.o
+common-obj-$(CONFIG_SOFTMMU) += clock-vmstate.o
obj-$(CONFIG_SOFTMMU) += machine-qmp-cmds.o
common-obj-$(CONFIG_EMPTY_SLOT) += empty_slot.o
diff --git a/hw/core/clock-vmstate.c b/hw/core/clock-vmstate.c
new file mode 100644
index 0000000000..260b13fc2c
--- /dev/null
+++ b/hw/core/clock-vmstate.c
@@ -0,0 +1,25 @@
+/*
+ * Clock migration structure
+ *
+ * Copyright GreenSocs 2019-2020
+ *
+ * Authors:
+ * Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "migration/vmstate.h"
+#include "hw/clock.h"
+
+const VMStateDescription vmstate_clock = {
+ .name = "clock",
+ .version_id = 0,
+ .minimum_version_id = 0,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT64(period, Clock),
+ VMSTATE_END_OF_LIST()
+ }
+};
diff --git a/hw/core/clock.c b/hw/core/clock.c
new file mode 100644
index 0000000000..3c0daf7d4c
--- /dev/null
+++ b/hw/core/clock.c
@@ -0,0 +1,130 @@
+/*
+ * Hardware Clocks
+ *
+ * Copyright GreenSocs 2016-2020
+ *
+ * Authors:
+ * Frederic Konrad
+ * Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/clock.h"
+#include "trace.h"
+
+#define CLOCK_PATH(_clk) (_clk->canonical_path)
+
+void clock_setup_canonical_path(Clock *clk)
+{
+ g_free(clk->canonical_path);
+ clk->canonical_path = object_get_canonical_path(OBJECT(clk));
+}
+
+void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque)
+{
+ clk->callback = cb;
+ clk->callback_opaque = opaque;
+}
+
+void clock_clear_callback(Clock *clk)
+{
+ clock_set_callback(clk, NULL, NULL);
+}
+
+void clock_set(Clock *clk, uint64_t period)
+{
+ trace_clock_set(CLOCK_PATH(clk), CLOCK_PERIOD_TO_NS(clk->period),
+ CLOCK_PERIOD_TO_NS(period));
+ clk->period = period;
+}
+
+static void clock_propagate_period(Clock *clk, bool call_callbacks)
+{
+ Clock *child;
+
+ QLIST_FOREACH(child, &clk->children, sibling) {
+ if (child->period != clk->period) {
+ child->period = clk->period;
+ trace_clock_update(CLOCK_PATH(child), CLOCK_PATH(clk),
+ CLOCK_PERIOD_TO_NS(clk->period),
+ call_callbacks);
+ if (call_callbacks && child->callback) {
+ child->callback(child->callback_opaque);
+ }
+ clock_propagate_period(child, call_callbacks);
+ }
+ }
+}
+
+void clock_propagate(Clock *clk)
+{
+ assert(clk->source == NULL);
+ trace_clock_propagate(CLOCK_PATH(clk));
+ clock_propagate_period(clk, true);
+}
+
+void clock_set_source(Clock *clk, Clock *src)
+{
+ /* changing clock source is not supported */
+ assert(!clk->source);
+
+ trace_clock_set_source(CLOCK_PATH(clk), CLOCK_PATH(src));
+
+ clk->period = src->period;
+ QLIST_INSERT_HEAD(&src->children, clk, sibling);
+ clk->source = src;
+ clock_propagate_period(clk, false);
+}
+
+static void clock_disconnect(Clock *clk)
+{
+ if (clk->source == NULL) {
+ return;
+ }
+
+ trace_clock_disconnect(CLOCK_PATH(clk));
+
+ clk->source = NULL;
+ QLIST_REMOVE(clk, sibling);
+}
+
+static void clock_initfn(Object *obj)
+{
+ Clock *clk = CLOCK(obj);
+
+ QLIST_INIT(&clk->children);
+}
+
+static void clock_finalizefn(Object *obj)
+{
+ Clock *clk = CLOCK(obj);
+ Clock *child, *next;
+
+ /* clear our list of children */
+ QLIST_FOREACH_SAFE(child, &clk->children, sibling, next) {
+ clock_disconnect(child);
+ }
+
+ /* remove us from source's children list */
+ clock_disconnect(clk);
+
+ g_free(clk->canonical_path);
+}
+
+static const TypeInfo clock_info = {
+ .name = TYPE_CLOCK,
+ .parent = TYPE_OBJECT,
+ .instance_size = sizeof(Clock),
+ .instance_init = clock_initfn,
+ .instance_finalize = clock_finalizefn,
+};
+
+static void clock_register_types(void)
+{
+ type_register_static(&clock_info);
+}
+
+type_init(clock_register_types)
diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c
index b76f7223af..39999c47c5 100644
--- a/hw/core/machine-hmp-cmds.c
+++ b/hw/core/machine-hmp-cmds.c
@@ -113,7 +113,7 @@ void hmp_info_memdev(Monitor *mon, const QDict *qdict)
while (m) {
v = string_output_visitor_new(false, &str);
- visit_type_uint16List(v, NULL, &m->value->host_nodes, NULL);
+ visit_type_uint16List(v, NULL, &m->value->host_nodes, &error_abort);
monitor_printf(mon, "memory backend: %s\n", m->value->id);
monitor_printf(mon, " size: %" PRId64 "\n", m->value->size);
monitor_printf(mon, " merge: %s\n",
diff --git a/hw/core/qdev-clock.c b/hw/core/qdev-clock.c
new file mode 100644
index 0000000000..a94cc44437
--- /dev/null
+++ b/hw/core/qdev-clock.c
@@ -0,0 +1,185 @@
+/*
+ * Device's clock input and output
+ *
+ * Copyright GreenSocs 2016-2020
+ *
+ * Authors:
+ * Frederic Konrad
+ * Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include "qemu/osdep.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-core.h"
+#include "qapi/error.h"
+
+/*
+ * qdev_init_clocklist:
+ * Add a new clock in a device
+ */
+static NamedClockList *qdev_init_clocklist(DeviceState *dev, const char *name,
+ bool output, Clock *clk)
+{
+ NamedClockList *ncl;
+
+ /*
+ * Clock must be added before realize() so that we can compute the
+ * clock's canonical path during device_realize().
+ */
+ assert(!dev->realized);
+
+ /*
+ * The ncl structure is freed by qdev_finalize_clocklist() which will
+ * be called during @dev's device_finalize().
+ */
+ ncl = g_new0(NamedClockList, 1);
+ ncl->name = g_strdup(name);
+ ncl->output = output;
+ ncl->alias = (clk != NULL);
+
+ /*
+ * Trying to create a clock whose name clashes with some other
+ * clock or property is a bug in the caller and we will abort().
+ */
+ if (clk == NULL) {
+ clk = CLOCK(object_new(TYPE_CLOCK));
+ object_property_add_child(OBJECT(dev), name, OBJECT(clk), &error_abort);
+ if (output) {
+ /*
+ * Remove object_new()'s initial reference.
+ * Note that for inputs, the reference created by object_new()
+ * will be deleted in qdev_finalize_clocklist().
+ */
+ object_unref(OBJECT(clk));
+ }
+ } else {
+ object_property_add_link(OBJECT(dev), name,
+ object_get_typename(OBJECT(clk)),
+ (Object **) &ncl->clock,
+ NULL, OBJ_PROP_LINK_STRONG, &error_abort);
+ }
+
+ ncl->clock = clk;
+
+ QLIST_INSERT_HEAD(&dev->clocks, ncl, node);
+ return ncl;
+}
+
+void qdev_finalize_clocklist(DeviceState *dev)
+{
+ /* called by @dev's device_finalize() */
+ NamedClockList *ncl, *ncl_next;
+
+ QLIST_FOREACH_SAFE(ncl, &dev->clocks, node, ncl_next) {
+ QLIST_REMOVE(ncl, node);
+ if (!ncl->output && !ncl->alias) {
+ /*
+ * We kept a reference on the input clock to ensure it lives up to
+ * this point so we can safely remove the callback.
+ * It avoids having a callback to a deleted object if ncl->clock
+ * is still referenced somewhere else (eg: by a clock output).
+ */
+ clock_clear_callback(ncl->clock);
+ object_unref(OBJECT(ncl->clock));
+ }
+ g_free(ncl->name);
+ g_free(ncl);
+ }
+}
+
+Clock *qdev_init_clock_out(DeviceState *dev, const char *name)
+{
+ NamedClockList *ncl;
+
+ assert(name);
+
+ ncl = qdev_init_clocklist(dev, name, true, NULL);
+
+ return ncl->clock;
+}
+
+Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
+ ClockCallback *callback, void *opaque)
+{
+ NamedClockList *ncl;
+
+ assert(name);
+
+ ncl = qdev_init_clocklist(dev, name, false, NULL);
+
+ if (callback) {
+ clock_set_callback(ncl->clock, callback, opaque);
+ }
+ return ncl->clock;
+}
+
+void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks)
+{
+ const struct ClockPortInitElem *elem;
+
+ for (elem = &clocks[0]; elem->name != NULL; elem++) {
+ Clock **clkp;
+ /* offset cannot be inside the DeviceState part */
+ assert(elem->offset > sizeof(DeviceState));
+ clkp = (Clock **)(((void *) dev) + elem->offset);
+ if (elem->is_output) {
+ *clkp = qdev_init_clock_out(dev, elem->name);
+ } else {
+ *clkp = qdev_init_clock_in(dev, elem->name, elem->callback, dev);
+ }
+ }
+}
+
+static NamedClockList *qdev_get_clocklist(DeviceState *dev, const char *name)
+{
+ NamedClockList *ncl;
+
+ QLIST_FOREACH(ncl, &dev->clocks, node) {
+ if (strcmp(name, ncl->name) == 0) {
+ return ncl;
+ }
+ }
+
+ return NULL;
+}
+
+Clock *qdev_get_clock_in(DeviceState *dev, const char *name)
+{
+ NamedClockList *ncl;
+
+ assert(name);
+
+ ncl = qdev_get_clocklist(dev, name);
+ assert(!ncl->output);
+
+ return ncl->clock;
+}
+
+Clock *qdev_get_clock_out(DeviceState *dev, const char *name)
+{
+ NamedClockList *ncl;
+
+ assert(name);
+
+ ncl = qdev_get_clocklist(dev, name);
+ assert(ncl->output);
+
+ return ncl->clock;
+}
+
+Clock *qdev_alias_clock(DeviceState *dev, const char *name,
+ DeviceState *alias_dev, const char *alias_name)
+{
+ NamedClockList *ncl;
+
+ assert(name && alias_name);
+
+ ncl = qdev_get_clocklist(dev, name);
+
+ qdev_init_clocklist(alias_dev, alias_name, ncl->output, ncl->clock);
+
+ return ncl->clock;
+}
diff --git a/hw/core/qdev.c b/hw/core/qdev.c
index 85f062def7..dd77a56067 100644
--- a/hw/core/qdev.c
+++ b/hw/core/qdev.c
@@ -37,6 +37,7 @@
#include "hw/qdev-properties.h"
#include "hw/boards.h"
#include "hw/sysbus.h"
+#include "hw/qdev-clock.h"
#include "migration/vmstate.h"
#include "trace.h"
@@ -855,6 +856,7 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
DeviceClass *dc = DEVICE_GET_CLASS(dev);
HotplugHandler *hotplug_ctrl;
BusState *bus;
+ NamedClockList *ncl;
Error *local_err = NULL;
bool unattached_parent = false;
static int unattached_count;
@@ -902,6 +904,13 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
*/
g_free(dev->canonical_path);
dev->canonical_path = object_get_canonical_path(OBJECT(dev));
+ QLIST_FOREACH(ncl, &dev->clocks, node) {
+ if (ncl->alias) {
+ continue;
+ } else {
+ clock_setup_canonical_path(ncl->clock);
+ }
+ }
if (qdev_get_vmsd(dev)) {
if (vmstate_register_with_alias_id(VMSTATE_IF(dev),
@@ -1025,6 +1034,7 @@ static void device_initfn(Object *obj)
dev->allow_unplug_during_migration = false;
QLIST_INIT(&dev->gpios);
+ QLIST_INIT(&dev->clocks);
}
static void device_post_init(Object *obj)
@@ -1054,6 +1064,8 @@ static void device_finalize(Object *obj)
*/
}
+ qdev_finalize_clocklist(dev);
+
/* Only send event if the device had been completely realized */
if (dev->pending_deleted_event) {
g_assert(dev->canonical_path);
diff --git a/hw/core/trace-events b/hw/core/trace-events
index aecd8e160e..1ac60ede6b 100644
--- a/hw/core/trace-events
+++ b/hw/core/trace-events
@@ -27,3 +27,10 @@ resettable_phase_exit_begin(void *obj, const char *objtype, unsigned count, int
resettable_phase_exit_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d"
resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d"
resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)"
+
+# clock.c
+clock_set_source(const char *clk, const char *src) "'%s', src='%s'"
+clock_disconnect(const char *clk) "'%s'"
+clock_set(const char *clk, uint64_t old, uint64_t new) "'%s', ns=%"PRIu64"->%"PRIu64
+clock_propagate(const char *clk) "'%s'"
+clock_update(const char *clk, const char *src, uint64_t val, int cb) "'%s', src='%s', ns=%"PRIu64", cb=%d"
diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c
index 1c45367f3c..4121a1b489 100644
--- a/hw/dma/xlnx-zdma.c
+++ b/hw/dma/xlnx-zdma.c
@@ -299,19 +299,30 @@ static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr)
s->regs[basereg + 1] = addr >> 32;
}
-static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
+static void zdma_load_descriptor_reg(XlnxZDMA *s, unsigned int reg,
+ XlnxZDMADescr *descr)
+{
+ descr->addr = zdma_get_regaddr64(s, reg);
+ descr->size = s->regs[reg + 2];
+ descr->attr = s->regs[reg + 3];
+}
+
+static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr,
+ XlnxZDMADescr *descr)
{
/* ZDMA descriptors must be aligned to their own size. */
if (addr % sizeof(XlnxZDMADescr)) {
qemu_log_mask(LOG_GUEST_ERROR,
"zdma: unaligned descriptor at %" PRIx64,
addr);
- memset(buf, 0x0, sizeof(XlnxZDMADescr));
+ memset(descr, 0x0, sizeof(XlnxZDMADescr));
s->error = true;
return false;
}
- address_space_read(s->dma_as, addr, s->attr, buf, sizeof(XlnxZDMADescr));
+ descr->addr = address_space_ldq_le(s->dma_as, addr, s->attr, NULL);
+ descr->size = address_space_ldl_le(s->dma_as, addr + 8, s->attr, NULL);
+ descr->attr = address_space_ldl_le(s->dma_as, addr + 12, s->attr, NULL);
return true;
}
@@ -321,8 +332,7 @@ static void zdma_load_src_descriptor(XlnxZDMA *s)
unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
if (ptype == PT_REG) {
- memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
- sizeof(s->dsc_src));
+ zdma_load_descriptor_reg(s, R_ZDMA_CH_SRC_DSCR_WORD0, &s->dsc_src);
return;
}
@@ -344,7 +354,7 @@ static void zdma_update_descr_addr(XlnxZDMA *s, bool type,
} else {
addr = zdma_get_regaddr64(s, basereg);
addr += sizeof(s->dsc_dst);
- address_space_read(s->dma_as, addr, s->attr, (void *) &next, 8);
+ next = address_space_ldq_le(s->dma_as, addr, s->attr, NULL);
}
zdma_put_regaddr64(s, basereg, next);
@@ -357,8 +367,7 @@ static void zdma_load_dst_descriptor(XlnxZDMA *s)
bool dst_type;
if (ptype == PT_REG) {
- memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
- sizeof(s->dsc_dst));
+ zdma_load_descriptor_reg(s, R_ZDMA_CH_DST_DSCR_WORD0, &s->dsc_dst);
return;
}
diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c
index 49304ca589..ca43bf87ca 100644
--- a/hw/intc/arm_gicv3_kvm.c
+++ b/hw/intc/arm_gicv3_kvm.c
@@ -658,13 +658,11 @@ static void kvm_arm_gicv3_get(GICv3State *s)
static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
{
- ARMCPU *cpu;
GICv3State *s;
GICv3CPUState *c;
c = (GICv3CPUState *)env->gicv3state;
s = c->gic;
- cpu = ARM_CPU(c->cpu);
c->icc_pmr_el1 = 0;
c->icc_bpr[GICV3_G0] = GIC_MIN_BPR;
@@ -681,7 +679,7 @@ static void arm_gicv3_icc_reset(CPUARMState *env, const ARMCPRegInfo *ri)
/* Initialize to actual HW supported configuration */
kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
- KVM_VGIC_ATTR(ICC_CTLR_EL1, cpu->mp_affinity),
+ KVM_VGIC_ATTR(ICC_CTLR_EL1, c->gicr_typer),
&c->icc_ctlr_el1[GICV3_NS], false, &error_abort);
c->icc_ctlr_el1[GICV3_S] = c->icc_ctlr_el1[GICV3_NS];
diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c
index b9a38272d9..f7472d1f3c 100644
--- a/hw/misc/zynq_slcr.c
+++ b/hw/misc/zynq_slcr.c
@@ -22,6 +22,7 @@
#include "qemu/log.h"
#include "qemu/module.h"
#include "hw/registerfields.h"
+#include "hw/qdev-clock.h"
#ifndef ZYNQ_SLCR_ERR_DEBUG
#define ZYNQ_SLCR_ERR_DEBUG 0
@@ -45,6 +46,12 @@ REG32(LOCKSTA, 0x00c)
REG32(ARM_PLL_CTRL, 0x100)
REG32(DDR_PLL_CTRL, 0x104)
REG32(IO_PLL_CTRL, 0x108)
+/* fields for [ARM|DDR|IO]_PLL_CTRL registers */
+ FIELD(xxx_PLL_CTRL, PLL_RESET, 0, 1)
+ FIELD(xxx_PLL_CTRL, PLL_PWRDWN, 1, 1)
+ FIELD(xxx_PLL_CTRL, PLL_BYPASS_QUAL, 3, 1)
+ FIELD(xxx_PLL_CTRL, PLL_BYPASS_FORCE, 4, 1)
+ FIELD(xxx_PLL_CTRL, PLL_FPDIV, 12, 7)
REG32(PLL_STATUS, 0x10c)
REG32(ARM_PLL_CFG, 0x110)
REG32(DDR_PLL_CFG, 0x114)
@@ -64,6 +71,10 @@ REG32(SMC_CLK_CTRL, 0x148)
REG32(LQSPI_CLK_CTRL, 0x14c)
REG32(SDIO_CLK_CTRL, 0x150)
REG32(UART_CLK_CTRL, 0x154)
+ FIELD(UART_CLK_CTRL, CLKACT0, 0, 1)
+ FIELD(UART_CLK_CTRL, CLKACT1, 1, 1)
+ FIELD(UART_CLK_CTRL, SRCSEL, 4, 2)
+ FIELD(UART_CLK_CTRL, DIVISOR, 8, 6)
REG32(SPI_CLK_CTRL, 0x158)
REG32(CAN_CLK_CTRL, 0x15c)
REG32(CAN_MIOCLK_CTRL, 0x160)
@@ -179,11 +190,127 @@ typedef struct ZynqSLCRState {
MemoryRegion iomem;
uint32_t regs[ZYNQ_SLCR_NUM_REGS];
+
+ Clock *ps_clk;
+ Clock *uart0_ref_clk;
+ Clock *uart1_ref_clk;
} ZynqSLCRState;
-static void zynq_slcr_reset(DeviceState *d)
+/*
+ * return the output frequency of ARM/DDR/IO pll
+ * using input frequency and PLL_CTRL register
+ */
+static uint64_t zynq_slcr_compute_pll(uint64_t input, uint32_t ctrl_reg)
+{
+ uint32_t mult = ((ctrl_reg & R_xxx_PLL_CTRL_PLL_FPDIV_MASK) >>
+ R_xxx_PLL_CTRL_PLL_FPDIV_SHIFT);
+
+ /* first, check if pll is bypassed */
+ if (ctrl_reg & R_xxx_PLL_CTRL_PLL_BYPASS_FORCE_MASK) {
+ return input;
+ }
+
+ /* is pll disabled ? */
+ if (ctrl_reg & (R_xxx_PLL_CTRL_PLL_RESET_MASK |
+ R_xxx_PLL_CTRL_PLL_PWRDWN_MASK)) {
+ return 0;
+ }
+
+ /* frequency multiplier -> period division */
+ return input / mult;
+}
+
+/*
+ * return the output period of a clock given:
+ * + the periods in an array corresponding to input mux selector
+ * + the register xxx_CLK_CTRL value
+ * + enable bit index in ctrl register
+ *
+ * This function makes the assumption that the ctrl_reg value is organized as
+ * follows:
+ * + bits[13:8] clock frequency divisor
+ * + bits[5:4] clock mux selector (index in array)
+ * + bits[index] clock enable
+ */
+static uint64_t zynq_slcr_compute_clock(const uint64_t periods[],
+ uint32_t ctrl_reg,
+ unsigned index)
+{
+ uint32_t srcsel = extract32(ctrl_reg, 4, 2); /* bits [5:4] */
+ uint32_t divisor = extract32(ctrl_reg, 8, 6); /* bits [13:8] */
+
+ /* first, check if clock is disabled */
+ if (((ctrl_reg >> index) & 1u) == 0) {
+ return 0;
+ }
+
+ /*
+ * according to the Zynq technical ref. manual UG585 v1.12.2 in
+ * Clocks chapter, section 25.10.1 page 705:
+ * "The 6-bit divider provides a divide range of 1 to 63"
+ * We follow here what is implemented in linux kernel and consider
+ * the 0 value as a bypass (no division).
+ */
+ /* frequency divisor -> period multiplication */
+ return periods[srcsel] * (divisor ? divisor : 1u);
+}
+
+/*
+ * macro helper around zynq_slcr_compute_clock to avoid repeating
+ * the register name.
+ */
+#define ZYNQ_COMPUTE_CLK(state, plls, reg, enable_field) \
+ zynq_slcr_compute_clock((plls), (state)->regs[reg], \
+ reg ## _ ## enable_field ## _SHIFT)
+
+/**
+ * Compute and set the ouputs clocks periods.
+ * But do not propagate them further. Connected clocks
+ * will not receive any updates (See zynq_slcr_compute_clocks())
+ */
+static void zynq_slcr_compute_clocks(ZynqSLCRState *s)
+{
+ uint64_t ps_clk = clock_get(s->ps_clk);
+
+ /* consider outputs clocks are disabled while in reset */
+ if (device_is_in_reset(DEVICE(s))) {
+ ps_clk = 0;
+ }
+
+ uint64_t io_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_IO_PLL_CTRL]);
+ uint64_t arm_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_ARM_PLL_CTRL]);
+ uint64_t ddr_pll = zynq_slcr_compute_pll(ps_clk, s->regs[R_DDR_PLL_CTRL]);
+
+ uint64_t uart_mux[4] = {io_pll, io_pll, arm_pll, ddr_pll};
+
+ /* compute uartX reference clocks */
+ clock_set(s->uart0_ref_clk,
+ ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT0));
+ clock_set(s->uart1_ref_clk,
+ ZYNQ_COMPUTE_CLK(s, uart_mux, R_UART_CLK_CTRL, CLKACT1));
+}
+
+/**
+ * Propagate the outputs clocks.
+ * zynq_slcr_compute_clocks() should have been called before
+ * to configure them.
+ */
+static void zynq_slcr_propagate_clocks(ZynqSLCRState *s)
{
- ZynqSLCRState *s = ZYNQ_SLCR(d);
+ clock_propagate(s->uart0_ref_clk);
+ clock_propagate(s->uart1_ref_clk);
+}
+
+static void zynq_slcr_ps_clk_callback(void *opaque)
+{
+ ZynqSLCRState *s = (ZynqSLCRState *) opaque;
+ zynq_slcr_compute_clocks(s);
+ zynq_slcr_propagate_clocks(s);
+}
+
+static void zynq_slcr_reset_init(Object *obj, ResetType type)
+{
+ ZynqSLCRState *s = ZYNQ_SLCR(obj);
int i;
DB_PRINT("RESET\n");
@@ -277,6 +404,23 @@ static void zynq_slcr_reset(DeviceState *d)
s->regs[R_DDRIOB + 12] = 0x00000021;
}
+static void zynq_slcr_reset_hold(Object *obj)
+{
+ ZynqSLCRState *s = ZYNQ_SLCR(obj);
+
+ /* will disable all output clocks */
+ zynq_slcr_compute_clocks(s);
+ zynq_slcr_propagate_clocks(s);
+}
+
+static void zynq_slcr_reset_exit(Object *obj)
+{
+ ZynqSLCRState *s = ZYNQ_SLCR(obj);
+
+ /* will compute output clocks according to ps_clk and registers */
+ zynq_slcr_compute_clocks(s);
+ zynq_slcr_propagate_clocks(s);
+}
static bool zynq_slcr_check_offset(hwaddr offset, bool rnw)
{
@@ -409,6 +553,13 @@ static void zynq_slcr_write(void *opaque, hwaddr offset,
qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
}
break;
+ case R_IO_PLL_CTRL:
+ case R_ARM_PLL_CTRL:
+ case R_DDR_PLL_CTRL:
+ case R_UART_CLK_CTRL:
+ zynq_slcr_compute_clocks(s);
+ zynq_slcr_propagate_clocks(s);
+ break;
}
}
@@ -418,6 +569,13 @@ static const MemoryRegionOps slcr_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
+static const ClockPortInitArray zynq_slcr_clocks = {
+ QDEV_CLOCK_IN(ZynqSLCRState, ps_clk, zynq_slcr_ps_clk_callback),
+ QDEV_CLOCK_OUT(ZynqSLCRState, uart0_ref_clk),
+ QDEV_CLOCK_OUT(ZynqSLCRState, uart1_ref_clk),
+ QDEV_CLOCK_END
+};
+
static void zynq_slcr_init(Object *obj)
{
ZynqSLCRState *s = ZYNQ_SLCR(obj);
@@ -425,14 +583,17 @@ static void zynq_slcr_init(Object *obj)
memory_region_init_io(&s->iomem, obj, &slcr_ops, s, "slcr",
ZYNQ_SLCR_MMIO_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem);
+
+ qdev_init_clocks(DEVICE(obj), zynq_slcr_clocks);
}
static const VMStateDescription vmstate_zynq_slcr = {
.name = "zynq_slcr",
- .version_id = 2,
+ .version_id = 3,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, ZynqSLCRState, ZYNQ_SLCR_NUM_REGS),
+ VMSTATE_CLOCK_V(ps_clk, ZynqSLCRState, 3),
VMSTATE_END_OF_LIST()
}
};
@@ -440,9 +601,12 @@ static const VMStateDescription vmstate_zynq_slcr = {
static void zynq_slcr_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->vmsd = &vmstate_zynq_slcr;
- dc->reset = zynq_slcr_reset;
+ rc->phases.enter = zynq_slcr_reset_init;
+ rc->phases.hold = zynq_slcr_reset_hold;
+ rc->phases.exit = zynq_slcr_reset_exit;
}
static const TypeInfo zynq_slcr_info = {
diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs
index af4d194866..f2b73983ee 100644
--- a/hw/net/Makefile.objs
+++ b/hw/net/Makefile.objs
@@ -55,3 +55,4 @@ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \
obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o
common-obj-$(CONFIG_CAN_BUS) += can/
+common-obj-$(CONFIG_MSF2) += msf2-emac.o
diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c
index 51ec5a072d..22a0b1b1f9 100644
--- a/hw/net/cadence_gem.c
+++ b/hw/net/cadence_gem.c
@@ -411,6 +411,11 @@ static inline void rx_desc_set_sof(uint32_t *desc)
desc[1] |= DESC_1_RX_SOF;
}
+static inline void rx_desc_clear_control(uint32_t *desc)
+{
+ desc[1] = 0;
+}
+
static inline void rx_desc_set_eof(uint32_t *desc)
{
desc[1] |= DESC_1_RX_EOF;
@@ -999,6 +1004,8 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size)
rxbuf_ptr += MIN(bytes_to_copy, rxbufsize);
bytes_to_copy -= MIN(bytes_to_copy, rxbufsize);
+ rx_desc_clear_control(s->rx_desc[q]);
+
/* Update the descriptor. */
if (first_desc) {
rx_desc_set_sof(s->rx_desc[q]);
@@ -1238,7 +1245,14 @@ static void gem_transmit(CadenceGEMState *s)
/* read next descriptor */
if (tx_desc_get_wrap(desc)) {
tx_desc_set_last(desc);
- packet_desc_addr = s->regs[GEM_TXQBASE];
+
+ if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) {
+ packet_desc_addr = s->regs[GEM_TBQPH];
+ packet_desc_addr <<= 32;
+ } else {
+ packet_desc_addr = 0;
+ }
+ packet_desc_addr |= s->regs[GEM_TXQBASE];
} else {
packet_desc_addr += 4 * gem_get_desc_len(s, false);
}
diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c
new file mode 100644
index 0000000000..32ba9e8412
--- /dev/null
+++ b/hw/net/msf2-emac.c
@@ -0,0 +1,589 @@
+/*
+ * QEMU model of the Smartfusion2 Ethernet MAC.
+ *
+ * Copyright (c) 2020 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * Refer to section Ethernet MAC in the document:
+ * UG0331: SmartFusion2 Microcontroller Subsystem User Guide
+ * Datasheet URL:
+ * https://www.microsemi.com/document-portal/cat_view/56661-internal-documents/
+ * 56758-soc?lang=en&limit=20&limitstart=220
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+#include "exec/address-spaces.h"
+#include "hw/registerfields.h"
+#include "hw/net/msf2-emac.h"
+#include "hw/net/mii.h"
+#include "hw/irq.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+
+REG32(CFG1, 0x0)
+ FIELD(CFG1, RESET, 31, 1)
+ FIELD(CFG1, RX_EN, 2, 1)
+ FIELD(CFG1, TX_EN, 0, 1)
+ FIELD(CFG1, LB_EN, 8, 1)
+REG32(CFG2, 0x4)
+REG32(IFG, 0x8)
+REG32(HALF_DUPLEX, 0xc)
+REG32(MAX_FRAME_LENGTH, 0x10)
+REG32(MII_CMD, 0x24)
+ FIELD(MII_CMD, READ, 0, 1)
+REG32(MII_ADDR, 0x28)
+ FIELD(MII_ADDR, REGADDR, 0, 5)
+ FIELD(MII_ADDR, PHYADDR, 8, 5)
+REG32(MII_CTL, 0x2c)
+REG32(MII_STS, 0x30)
+REG32(STA1, 0x40)
+REG32(STA2, 0x44)
+REG32(FIFO_CFG0, 0x48)
+REG32(FIFO_CFG4, 0x58)
+ FIELD(FIFO_CFG4, BCAST, 9, 1)
+ FIELD(FIFO_CFG4, MCAST, 8, 1)
+REG32(FIFO_CFG5, 0x5C)
+ FIELD(FIFO_CFG5, BCAST, 9, 1)
+ FIELD(FIFO_CFG5, MCAST, 8, 1)
+REG32(DMA_TX_CTL, 0x180)
+ FIELD(DMA_TX_CTL, EN, 0, 1)
+REG32(DMA_TX_DESC, 0x184)
+REG32(DMA_TX_STATUS, 0x188)
+ FIELD(DMA_TX_STATUS, PKTCNT, 16, 8)
+ FIELD(DMA_TX_STATUS, UNDERRUN, 1, 1)
+ FIELD(DMA_TX_STATUS, PKT_SENT, 0, 1)
+REG32(DMA_RX_CTL, 0x18c)
+ FIELD(DMA_RX_CTL, EN, 0, 1)
+REG32(DMA_RX_DESC, 0x190)
+REG32(DMA_RX_STATUS, 0x194)
+ FIELD(DMA_RX_STATUS, PKTCNT, 16, 8)
+ FIELD(DMA_RX_STATUS, OVERFLOW, 2, 1)
+ FIELD(DMA_RX_STATUS, PKT_RCVD, 0, 1)
+REG32(DMA_IRQ_MASK, 0x198)
+REG32(DMA_IRQ, 0x19c)
+
+#define EMPTY_MASK (1 << 31)
+#define PKT_SIZE 0x7FF
+#define PHYADDR 0x1
+#define MAX_PKT_SIZE 2048
+
+typedef struct {
+ uint32_t pktaddr;
+ uint32_t pktsize;
+ uint32_t next;
+} EmacDesc;
+
+static uint32_t emac_get_isr(MSF2EmacState *s)
+{
+ uint32_t ier = s->regs[R_DMA_IRQ_MASK];
+ uint32_t tx = s->regs[R_DMA_TX_STATUS] & 0xF;
+ uint32_t rx = s->regs[R_DMA_RX_STATUS] & 0xF;
+ uint32_t isr = (rx << 4) | tx;
+
+ s->regs[R_DMA_IRQ] = ier & isr;
+ return s->regs[R_DMA_IRQ];
+}
+
+static void emac_update_irq(MSF2EmacState *s)
+{
+ bool intr = emac_get_isr(s);
+
+ qemu_set_irq(s->irq, intr);
+}
+
+static void emac_load_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc)
+{
+ address_space_read(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, d, sizeof *d);
+ /* Convert from LE into host endianness. */
+ d->pktaddr = le32_to_cpu(d->pktaddr);
+ d->pktsize = le32_to_cpu(d->pktsize);
+ d->next = le32_to_cpu(d->next);
+}
+
+static void emac_store_desc(MSF2EmacState *s, EmacDesc *d, hwaddr desc)
+{
+ /* Convert from host endianness into LE. */
+ d->pktaddr = cpu_to_le32(d->pktaddr);
+ d->pktsize = cpu_to_le32(d->pktsize);
+ d->next = cpu_to_le32(d->next);
+
+ address_space_write(&s->dma_as, desc, MEMTXATTRS_UNSPECIFIED, d, sizeof *d);
+}
+
+static void msf2_dma_tx(MSF2EmacState *s)
+{
+ NetClientState *nc = qemu_get_queue(s->nic);
+ hwaddr desc = s->regs[R_DMA_TX_DESC];
+ uint8_t buf[MAX_PKT_SIZE];
+ EmacDesc d;
+ int size;
+ uint8_t pktcnt;
+ uint32_t status;
+
+ if (!(s->regs[R_CFG1] & R_CFG1_TX_EN_MASK)) {
+ return;
+ }
+
+ while (1) {
+ emac_load_desc(s, &d, desc);
+ if (d.pktsize & EMPTY_MASK) {
+ break;
+ }
+ size = d.pktsize & PKT_SIZE;
+ address_space_read(&s->dma_as, d.pktaddr, MEMTXATTRS_UNSPECIFIED,
+ buf, size);
+ /*
+ * This is very basic way to send packets. Ideally there should be
+ * a FIFO and packets should be sent out from FIFO only when
+ * R_CFG1 bit 0 is set.
+ */
+ if (s->regs[R_CFG1] & R_CFG1_LB_EN_MASK) {
+ nc->info->receive(nc, buf, size);
+ } else {
+ qemu_send_packet(nc, buf, size);
+ }
+ d.pktsize |= EMPTY_MASK;
+ emac_store_desc(s, &d, desc);
+ /* update sent packets count */
+ status = s->regs[R_DMA_TX_STATUS];
+ pktcnt = FIELD_EX32(status, DMA_TX_STATUS, PKTCNT);
+ pktcnt++;
+ s->regs[R_DMA_TX_STATUS] = FIELD_DP32(status, DMA_TX_STATUS,
+ PKTCNT, pktcnt);
+ s->regs[R_DMA_TX_STATUS] |= R_DMA_TX_STATUS_PKT_SENT_MASK;
+ desc = d.next;
+ }
+ s->regs[R_DMA_TX_STATUS] |= R_DMA_TX_STATUS_UNDERRUN_MASK;
+ s->regs[R_DMA_TX_CTL] &= ~R_DMA_TX_CTL_EN_MASK;
+}
+
+static void msf2_phy_update_link(MSF2EmacState *s)
+{
+ /* Autonegotiation status mirrors link status. */
+ if (qemu_get_queue(s->nic)->link_down) {
+ s->phy_regs[MII_BMSR] &= ~(MII_BMSR_AN_COMP |
+ MII_BMSR_LINK_ST);
+ } else {
+ s->phy_regs[MII_BMSR] |= (MII_BMSR_AN_COMP |
+ MII_BMSR_LINK_ST);
+ }
+}
+
+static void msf2_phy_reset(MSF2EmacState *s)
+{
+ memset(&s->phy_regs[0], 0, sizeof(s->phy_regs));
+ s->phy_regs[MII_BMCR] = 0x1140;
+ s->phy_regs[MII_BMSR] = 0x7968;
+ s->phy_regs[MII_PHYID1] = 0x0022;
+ s->phy_regs[MII_PHYID2] = 0x1550;
+ s->phy_regs[MII_ANAR] = 0x01E1;
+ s->phy_regs[MII_ANLPAR] = 0xCDE1;
+
+ msf2_phy_update_link(s);
+}
+
+static void write_to_phy(MSF2EmacState *s)
+{
+ uint8_t reg_addr = s->regs[R_MII_ADDR] & R_MII_ADDR_REGADDR_MASK;
+ uint8_t phy_addr = (s->regs[R_MII_ADDR] >> R_MII_ADDR_PHYADDR_SHIFT) &
+ R_MII_ADDR_REGADDR_MASK;
+ uint16_t data = s->regs[R_MII_CTL] & 0xFFFF;
+
+ if (phy_addr != PHYADDR) {
+ return;
+ }
+
+ switch (reg_addr) {
+ case MII_BMCR:
+ if (data & MII_BMCR_RESET) {
+ /* Phy reset */
+ msf2_phy_reset(s);
+ data &= ~MII_BMCR_RESET;
+ }
+ if (data & MII_BMCR_AUTOEN) {
+ /* Complete autonegotiation immediately */
+ data &= ~MII_BMCR_AUTOEN;
+ s->phy_regs[MII_BMSR] |= MII_BMSR_AN_COMP;
+ }
+ break;
+ }
+
+ s->phy_regs[reg_addr] = data;
+}
+
+static uint16_t read_from_phy(MSF2EmacState *s)
+{
+ uint8_t reg_addr = s->regs[R_MII_ADDR] & R_MII_ADDR_REGADDR_MASK;
+ uint8_t phy_addr = (s->regs[R_MII_ADDR] >> R_MII_ADDR_PHYADDR_SHIFT) &
+ R_MII_ADDR_REGADDR_MASK;
+
+ if (phy_addr == PHYADDR) {
+ return s->phy_regs[reg_addr];
+ } else {
+ return 0xFFFF;
+ }
+}
+
+static void msf2_emac_do_reset(MSF2EmacState *s)
+{
+ memset(&s->regs[0], 0, sizeof(s->regs));
+ s->regs[R_CFG1] = 0x80000000;
+ s->regs[R_CFG2] = 0x00007000;
+ s->regs[R_IFG] = 0x40605060;
+ s->regs[R_HALF_DUPLEX] = 0x00A1F037;
+ s->regs[R_MAX_FRAME_LENGTH] = 0x00000600;
+ s->regs[R_FIFO_CFG5] = 0X3FFFF;
+
+ msf2_phy_reset(s);
+}
+
+static uint64_t emac_read(void *opaque, hwaddr addr, unsigned int size)
+{
+ MSF2EmacState *s = opaque;
+ uint32_t r = 0;
+
+ addr >>= 2;
+
+ switch (addr) {
+ case R_DMA_IRQ:
+ r = emac_get_isr(s);
+ break;
+ default:
+ if (addr >= ARRAY_SIZE(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__,
+ addr * 4);
+ return r;
+ }
+ r = s->regs[addr];
+ break;
+ }
+ return r;
+}
+
+static void emac_write(void *opaque, hwaddr addr, uint64_t val64,
+ unsigned int size)
+{
+ MSF2EmacState *s = opaque;
+ uint32_t value = val64;
+ uint32_t enreqbits;
+ uint8_t pktcnt;
+
+ addr >>= 2;
+ switch (addr) {
+ case R_DMA_TX_CTL:
+ s->regs[addr] = value;
+ if (value & R_DMA_TX_CTL_EN_MASK) {
+ msf2_dma_tx(s);
+ }
+ break;
+ case R_DMA_RX_CTL:
+ s->regs[addr] = value;
+ if (value & R_DMA_RX_CTL_EN_MASK) {
+ s->rx_desc = s->regs[R_DMA_RX_DESC];
+ qemu_flush_queued_packets(qemu_get_queue(s->nic));
+ }
+ break;
+ case R_CFG1:
+ s->regs[addr] = value;
+ if (value & R_CFG1_RESET_MASK) {
+ msf2_emac_do_reset(s);
+ }
+ break;
+ case R_FIFO_CFG0:
+ /*
+ * For our implementation, turning on modules is instantaneous,
+ * so the states requested via the *ENREQ bits appear in the
+ * *ENRPLY bits immediately. Also the reset bits to reset PE-MCXMAC
+ * module are not emulated here since it deals with start of frames,
+ * inter-packet gap and control frames.
+ */
+ enreqbits = extract32(value, 8, 5);
+ s->regs[addr] = deposit32(value, 16, 5, enreqbits);
+ break;
+ case R_DMA_TX_DESC:
+ if (value & 0x3) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Tx Descriptor address should be"
+ " 32 bit aligned\n");
+ }
+ /* Ignore [1:0] bits */
+ s->regs[addr] = value & ~3;
+ break;
+ case R_DMA_RX_DESC:
+ if (value & 0x3) {
+ qemu_log_mask(LOG_GUEST_ERROR, "Rx Descriptor address should be"
+ " 32 bit aligned\n");
+ }
+ /* Ignore [1:0] bits */
+ s->regs[addr] = value & ~3;
+ break;
+ case R_DMA_TX_STATUS:
+ if (value & R_DMA_TX_STATUS_UNDERRUN_MASK) {
+ s->regs[addr] &= ~R_DMA_TX_STATUS_UNDERRUN_MASK;
+ }
+ if (value & R_DMA_TX_STATUS_PKT_SENT_MASK) {
+ pktcnt = FIELD_EX32(s->regs[addr], DMA_TX_STATUS, PKTCNT);
+ pktcnt--;
+ s->regs[addr] = FIELD_DP32(s->regs[addr], DMA_TX_STATUS,
+ PKTCNT, pktcnt);
+ if (pktcnt == 0) {
+ s->regs[addr] &= ~R_DMA_TX_STATUS_PKT_SENT_MASK;
+ }
+ }
+ break;
+ case R_DMA_RX_STATUS:
+ if (value & R_DMA_RX_STATUS_OVERFLOW_MASK) {
+ s->regs[addr] &= ~R_DMA_RX_STATUS_OVERFLOW_MASK;
+ }
+ if (value & R_DMA_RX_STATUS_PKT_RCVD_MASK) {
+ pktcnt = FIELD_EX32(s->regs[addr], DMA_RX_STATUS, PKTCNT);
+ pktcnt--;
+ s->regs[addr] = FIELD_DP32(s->regs[addr], DMA_RX_STATUS,
+ PKTCNT, pktcnt);
+ if (pktcnt == 0) {
+ s->regs[addr] &= ~R_DMA_RX_STATUS_PKT_RCVD_MASK;
+ }
+ }
+ break;
+ case R_DMA_IRQ:
+ break;
+ case R_MII_CMD:
+ if (value & R_MII_CMD_READ_MASK) {
+ s->regs[R_MII_STS] = read_from_phy(s);
+ }
+ break;
+ case R_MII_CTL:
+ s->regs[addr] = value;
+ write_to_phy(s);
+ break;
+ case R_STA1:
+ s->regs[addr] = value;
+ /*
+ * R_STA1 [31:24] : octet 1 of mac address
+ * R_STA1 [23:16] : octet 2 of mac address
+ * R_STA1 [15:8] : octet 3 of mac address
+ * R_STA1 [7:0] : octet 4 of mac address
+ */
+ stl_be_p(s->mac_addr, value);
+ break;
+ case R_STA2:
+ s->regs[addr] = value;
+ /*
+ * R_STA2 [31:24] : octet 5 of mac address
+ * R_STA2 [23:16] : octet 6 of mac address
+ */
+ stw_be_p(s->mac_addr + 4, value >> 16);
+ break;
+ default:
+ if (addr >= ARRAY_SIZE(s->regs)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__,
+ addr * 4);
+ return;
+ }
+ s->regs[addr] = value;
+ break;
+ }
+ emac_update_irq(s);
+}
+
+static const MemoryRegionOps emac_ops = {
+ .read = emac_read,
+ .write = emac_write,
+ .endianness = DEVICE_NATIVE_ENDIAN,
+ .impl = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static bool emac_can_rx(NetClientState *nc)
+{
+ MSF2EmacState *s = qemu_get_nic_opaque(nc);
+
+ return (s->regs[R_CFG1] & R_CFG1_RX_EN_MASK) &&
+ (s->regs[R_DMA_RX_CTL] & R_DMA_RX_CTL_EN_MASK);
+}
+
+static bool addr_filter_ok(MSF2EmacState *s, const uint8_t *buf)
+{
+ /* The broadcast MAC address: FF:FF:FF:FF:FF:FF */
+ const uint8_t broadcast_addr[] = { 0xFF, 0xFF, 0xFF, 0xFF,
+ 0xFF, 0xFF };
+ bool bcast_en = true;
+ bool mcast_en = true;
+
+ if (s->regs[R_FIFO_CFG5] & R_FIFO_CFG5_BCAST_MASK) {
+ bcast_en = true; /* Broadcast dont care for drop circuitry */
+ } else if (s->regs[R_FIFO_CFG4] & R_FIFO_CFG4_BCAST_MASK) {
+ bcast_en = false;
+ }
+
+ if (s->regs[R_FIFO_CFG5] & R_FIFO_CFG5_MCAST_MASK) {
+ mcast_en = true; /* Multicast dont care for drop circuitry */
+ } else if (s->regs[R_FIFO_CFG4] & R_FIFO_CFG4_MCAST_MASK) {
+ mcast_en = false;
+ }
+
+ if (!memcmp(buf, broadcast_addr, sizeof(broadcast_addr))) {
+ return bcast_en;
+ }
+
+ if (buf[0] & 1) {
+ return mcast_en;
+ }
+
+ return !memcmp(buf, s->mac_addr, sizeof(s->mac_addr));
+}
+
+static ssize_t emac_rx(NetClientState *nc, const uint8_t *buf, size_t size)
+{
+ MSF2EmacState *s = qemu_get_nic_opaque(nc);
+ EmacDesc d;
+ uint8_t pktcnt;
+ uint32_t status;
+
+ if (size > (s->regs[R_MAX_FRAME_LENGTH] & 0xFFFF)) {
+ return size;
+ }
+ if (!addr_filter_ok(s, buf)) {
+ return size;
+ }
+
+ emac_load_desc(s, &d, s->rx_desc);
+
+ if (d.pktsize & EMPTY_MASK) {
+ address_space_write(&s->dma_as, d.pktaddr, MEMTXATTRS_UNSPECIFIED,
+ buf, size & PKT_SIZE);
+ d.pktsize = size & PKT_SIZE;
+ emac_store_desc(s, &d, s->rx_desc);
+ /* update received packets count */
+ status = s->regs[R_DMA_RX_STATUS];
+ pktcnt = FIELD_EX32(status, DMA_RX_STATUS, PKTCNT);
+ pktcnt++;
+ s->regs[R_DMA_RX_STATUS] = FIELD_DP32(status, DMA_RX_STATUS,
+ PKTCNT, pktcnt);
+ s->regs[R_DMA_RX_STATUS] |= R_DMA_RX_STATUS_PKT_RCVD_MASK;
+ s->rx_desc = d.next;
+ } else {
+ s->regs[R_DMA_RX_CTL] &= ~R_DMA_RX_CTL_EN_MASK;
+ s->regs[R_DMA_RX_STATUS] |= R_DMA_RX_STATUS_OVERFLOW_MASK;
+ }
+ emac_update_irq(s);
+ return size;
+}
+
+static void msf2_emac_reset(DeviceState *dev)
+{
+ MSF2EmacState *s = MSS_EMAC(dev);
+
+ msf2_emac_do_reset(s);
+}
+
+static void emac_set_link(NetClientState *nc)
+{
+ MSF2EmacState *s = qemu_get_nic_opaque(nc);
+
+ msf2_phy_update_link(s);
+}
+
+static NetClientInfo net_msf2_emac_info = {
+ .type = NET_CLIENT_DRIVER_NIC,
+ .size = sizeof(NICState),
+ .can_receive = emac_can_rx,
+ .receive = emac_rx,
+ .link_status_changed = emac_set_link,
+};
+
+static void msf2_emac_realize(DeviceState *dev, Error **errp)
+{
+ MSF2EmacState *s = MSS_EMAC(dev);
+
+ if (!s->dma_mr) {
+ error_setg(errp, "MSS_EMAC 'ahb-bus' link not set");
+ return;
+ }
+
+ address_space_init(&s->dma_as, s->dma_mr, "emac-ahb");
+
+ qemu_macaddr_default_if_unset(&s->conf.macaddr);
+ s->nic = qemu_new_nic(&net_msf2_emac_info, &s->conf,
+ object_get_typename(OBJECT(dev)), dev->id, s);
+ qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a);
+}
+
+static void msf2_emac_init(Object *obj)
+{
+ MSF2EmacState *s = MSS_EMAC(obj);
+
+ sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+ memory_region_init_io(&s->mmio, obj, &emac_ops, s,
+ "msf2-emac", R_MAX * 4);
+ sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+}
+
+static Property msf2_emac_properties[] = {
+ DEFINE_PROP_LINK("ahb-bus", MSF2EmacState, dma_mr,
+ TYPE_MEMORY_REGION, MemoryRegion *),
+ DEFINE_NIC_PROPERTIES(MSF2EmacState, conf),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static const VMStateDescription vmstate_msf2_emac = {
+ .name = TYPE_MSS_EMAC,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT8_ARRAY(mac_addr, MSF2EmacState, ETH_ALEN),
+ VMSTATE_UINT32(rx_desc, MSF2EmacState),
+ VMSTATE_UINT16_ARRAY(phy_regs, MSF2EmacState, PHY_MAX_REGS),
+ VMSTATE_UINT32_ARRAY(regs, MSF2EmacState, R_MAX),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void msf2_emac_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = msf2_emac_realize;
+ dc->reset = msf2_emac_reset;
+ dc->vmsd = &vmstate_msf2_emac;
+ device_class_set_props(dc, msf2_emac_properties);
+}
+
+static const TypeInfo msf2_emac_info = {
+ .name = TYPE_MSS_EMAC,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(MSF2EmacState),
+ .instance_init = msf2_emac_init,
+ .class_init = msf2_emac_class_init,
+};
+
+static void msf2_emac_register_types(void)
+{
+ type_register_static(&msf2_emac_info);
+}
+
+type_init(msf2_emac_register_types)
diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c
index 65bb6886c7..3301869d4f 100644
--- a/hw/net/virtio-net.c
+++ b/hw/net/virtio-net.c
@@ -83,6 +83,8 @@
#define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc_ext data in csum_ fields */
#define VIRTIO_NET_F_RSC_EXT 61
+#endif
+
static inline __virtio16 *virtio_net_rsc_ext_num_packets(
struct virtio_net_hdr *hdr)
{
@@ -95,8 +97,6 @@ static inline __virtio16 *virtio_net_rsc_ext_num_dupacks(
return &hdr->csum_offset;
}
-#endif
-
static VirtIOFeature feature_sizes[] = {
{.flags = 1ULL << VIRTIO_NET_F_MAC,
.end = endof(struct virtio_net_config, mac)},
diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c
index b8e765277d..726300a171 100644
--- a/hw/riscv/boot.c
+++ b/hw/riscv/boot.c
@@ -36,7 +36,8 @@
void riscv_find_and_load_firmware(MachineState *machine,
const char *default_machine_firmware,
- hwaddr firmware_load_addr)
+ hwaddr firmware_load_addr,
+ symbol_fn_t sym_cb)
{
char *firmware_filename = NULL;
@@ -76,7 +77,7 @@ void riscv_find_and_load_firmware(MachineState *machine,
if (firmware_filename) {
/* If not "none" load the firmware */
- riscv_load_firmware(firmware_filename, firmware_load_addr);
+ riscv_load_firmware(firmware_filename, firmware_load_addr, sym_cb);
g_free(firmware_filename);
}
}
@@ -96,12 +97,14 @@ char *riscv_find_firmware(const char *firmware_filename)
}
target_ulong riscv_load_firmware(const char *firmware_filename,
- hwaddr firmware_load_addr)
+ hwaddr firmware_load_addr,
+ symbol_fn_t sym_cb)
{
uint64_t firmware_entry, firmware_start, firmware_end;
- if (load_elf(firmware_filename, NULL, NULL, NULL, &firmware_entry,
- &firmware_start, &firmware_end, NULL, 0, EM_RISCV, 1, 0) > 0) {
+ if (load_elf_ram_sym(firmware_filename, NULL, NULL, NULL,
+ &firmware_entry, &firmware_start, &firmware_end, NULL,
+ 0, EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) {
return firmware_entry;
}
diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c
index 646553a7c3..b53109521e 100644
--- a/hw/riscv/sifive_e.c
+++ b/hw/riscv/sifive_e.c
@@ -123,8 +123,6 @@ static void riscv_sifive_e_soc_init(Object *obj)
object_initialize_child(obj, "cpus", &s->cpus,
sizeof(s->cpus), TYPE_RISCV_HART_ARRAY,
&error_abort, NULL);
- object_property_set_str(OBJECT(&s->cpus), SIFIVE_E_CPU, "cpu-type",
- &error_abort);
object_property_set_int(OBJECT(&s->cpus), ms->smp.cpus, "num-harts",
&error_abort);
sysbus_init_child_obj(obj, "riscv.sifive.e.gpio0",
@@ -141,6 +139,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp)
SiFiveESoCState *s = RISCV_E_SOC(dev);
MemoryRegion *sys_mem = get_system_memory();
+ object_property_set_str(OBJECT(&s->cpus), ms->cpu_type, "cpu-type",
+ &error_abort);
object_property_set_bool(OBJECT(&s->cpus), true, "realized",
&error_abort);
@@ -219,6 +219,7 @@ static void riscv_sifive_e_machine_init(MachineClass *mc)
mc->desc = "RISC-V Board compatible with SiFive E SDK";
mc->init = riscv_sifive_e_init;
mc->max_cpus = 1;
+ mc->default_cpu_type = SIFIVE_E_CPU;
}
DEFINE_MACHINE("sifive_e", riscv_sifive_e_machine_init)
diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c
index 998666c91f..bed10fcfa8 100644
--- a/hw/riscv/sifive_u.c
+++ b/hw/riscv/sifive_u.c
@@ -34,6 +34,7 @@
#include "qemu/log.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
+#include "qapi/visitor.h"
#include "hw/boards.h"
#include "hw/loader.h"
#include "hw/sysbus.h"
@@ -159,7 +160,11 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
qemu_fdt_add_subnode(fdt, nodename);
/* cpu 0 is the management hart that does not have mmu */
if (cpu != 0) {
+#if defined(TARGET_RISCV32)
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv32");
+#else
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+#endif
isa = riscv_isa_string(&s->soc.u_cpus.harts[cpu - 1]);
} else {
isa = riscv_isa_string(&s->soc.e_cpus.harts[0]);
@@ -312,7 +317,7 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap,
g_free(nodename);
}
-static void riscv_sifive_u_init(MachineState *machine)
+static void sifive_u_machine_init(MachineState *machine)
{
const struct MemmapEntry *memmap = sifive_u_memmap;
SiFiveUState *s = RISCV_U_MACHINE(machine);
@@ -326,6 +331,8 @@ static void riscv_sifive_u_init(MachineState *machine)
object_initialize_child(OBJECT(machine), "soc", &s->soc,
sizeof(s->soc), TYPE_RISCV_U_SOC,
&error_abort, NULL);
+ object_property_set_uint(OBJECT(&s->soc), s->serial, "serial",
+ &error_abort);
object_property_set_bool(OBJECT(&s->soc), true, "realized",
&error_abort);
@@ -345,7 +352,7 @@ static void riscv_sifive_u_init(MachineState *machine)
create_fdt(s, memmap, machine->ram_size, machine->kernel_cmdline);
riscv_find_and_load_firmware(machine, BIOS_FILENAME,
- memmap[SIFIVE_U_DRAM].base);
+ memmap[SIFIVE_U_DRAM].base, NULL);
if (machine->kernel_filename) {
uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
@@ -403,6 +410,76 @@ static void riscv_sifive_u_init(MachineState *machine)
&address_space_memory);
}
+static bool sifive_u_machine_get_start_in_flash(Object *obj, Error **errp)
+{
+ SiFiveUState *s = RISCV_U_MACHINE(obj);
+
+ return s->start_in_flash;
+}
+
+static void sifive_u_machine_set_start_in_flash(Object *obj, bool value, Error **errp)
+{
+ SiFiveUState *s = RISCV_U_MACHINE(obj);
+
+ s->start_in_flash = value;
+}
+
+static void sifive_u_machine_get_serial(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ visit_type_uint32(v, name, (uint32_t *)opaque, errp);
+}
+
+static void sifive_u_machine_set_serial(Object *obj, Visitor *v, const char *name,
+ void *opaque, Error **errp)
+{
+ visit_type_uint32(v, name, (uint32_t *)opaque, errp);
+}
+
+static void sifive_u_machine_instance_init(Object *obj)
+{
+ SiFiveUState *s = RISCV_U_MACHINE(obj);
+
+ s->start_in_flash = false;
+ object_property_add_bool(obj, "start-in-flash", sifive_u_machine_get_start_in_flash,
+ sifive_u_machine_set_start_in_flash, NULL);
+ object_property_set_description(obj, "start-in-flash",
+ "Set on to tell QEMU's ROM to jump to "
+ "flash. Otherwise QEMU will jump to DRAM",
+ NULL);
+
+ s->serial = OTP_SERIAL;
+ object_property_add(obj, "serial", "uint32", sifive_u_machine_get_serial,
+ sifive_u_machine_set_serial, NULL, &s->serial, NULL);
+ object_property_set_description(obj, "serial", "Board serial number", NULL);
+}
+
+static void sifive_u_machine_class_init(ObjectClass *oc, void *data)
+{
+ MachineClass *mc = MACHINE_CLASS(oc);
+
+ mc->desc = "RISC-V Board compatible with SiFive U SDK";
+ mc->init = sifive_u_machine_init;
+ mc->max_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + SIFIVE_U_COMPUTE_CPU_COUNT;
+ mc->min_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + 1;
+ mc->default_cpus = mc->min_cpus;
+}
+
+static const TypeInfo sifive_u_machine_typeinfo = {
+ .name = MACHINE_TYPE_NAME("sifive_u"),
+ .parent = TYPE_MACHINE,
+ .class_init = sifive_u_machine_class_init,
+ .instance_init = sifive_u_machine_instance_init,
+ .instance_size = sizeof(SiFiveUState),
+};
+
+static void sifive_u_machine_init_register_types(void)
+{
+ type_register_static(&sifive_u_machine_typeinfo);
+}
+
+type_init(sifive_u_machine_init_register_types)
+
static void riscv_sifive_u_soc_init(Object *obj)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -438,38 +515,10 @@ static void riscv_sifive_u_soc_init(Object *obj)
TYPE_SIFIVE_U_PRCI);
sysbus_init_child_obj(obj, "otp", &s->otp, sizeof(s->otp),
TYPE_SIFIVE_U_OTP);
- qdev_prop_set_uint32(DEVICE(&s->otp), "serial", OTP_SERIAL);
sysbus_init_child_obj(obj, "gem", &s->gem, sizeof(s->gem),
TYPE_CADENCE_GEM);
}
-static bool sifive_u_get_start_in_flash(Object *obj, Error **errp)
-{
- SiFiveUState *s = RISCV_U_MACHINE(obj);
-
- return s->start_in_flash;
-}
-
-static void sifive_u_set_start_in_flash(Object *obj, bool value, Error **errp)
-{
- SiFiveUState *s = RISCV_U_MACHINE(obj);
-
- s->start_in_flash = value;
-}
-
-static void riscv_sifive_u_machine_instance_init(Object *obj)
-{
- SiFiveUState *s = RISCV_U_MACHINE(obj);
-
- s->start_in_flash = false;
- object_property_add_bool(obj, "start-in-flash", sifive_u_get_start_in_flash,
- sifive_u_set_start_in_flash, NULL);
- object_property_set_description(obj, "start-in-flash",
- "Set on to tell QEMU's ROM to jump to "
- "flash. Otherwise QEMU will jump to DRAM",
- NULL);
-}
-
static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -558,6 +607,7 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
object_property_set_bool(OBJECT(&s->prci), true, "realized", &err);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->prci), 0, memmap[SIFIVE_U_PRCI].base);
+ qdev_prop_set_uint32(DEVICE(&s->otp), "serial", s->serial);
object_property_set_bool(OBJECT(&s->otp), true, "realized", &err);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->otp), 0, memmap[SIFIVE_U_OTP].base);
@@ -584,10 +634,16 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp)
memmap[SIFIVE_U_GEM_MGMT].base, memmap[SIFIVE_U_GEM_MGMT].size);
}
+static Property riscv_sifive_u_soc_props[] = {
+ DEFINE_PROP_UINT32("serial", SiFiveUSoCState, serial, OTP_SERIAL),
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void riscv_sifive_u_soc_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
+ device_class_set_props(dc, riscv_sifive_u_soc_props);
dc->realize = riscv_sifive_u_soc_realize;
/* Reason: Uses serial_hds in realize function, thus can't be used twice */
dc->user_creatable = false;
@@ -607,29 +663,3 @@ static void riscv_sifive_u_soc_register_types(void)
}
type_init(riscv_sifive_u_soc_register_types)
-
-static void riscv_sifive_u_machine_class_init(ObjectClass *oc, void *data)
-{
- MachineClass *mc = MACHINE_CLASS(oc);
-
- mc->desc = "RISC-V Board compatible with SiFive U SDK";
- mc->init = riscv_sifive_u_init;
- mc->max_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + SIFIVE_U_COMPUTE_CPU_COUNT;
- mc->min_cpus = SIFIVE_U_MANAGEMENT_CPU_COUNT + 1;
- mc->default_cpus = mc->min_cpus;
-}
-
-static const TypeInfo riscv_sifive_u_machine_typeinfo = {
- .name = MACHINE_TYPE_NAME("sifive_u"),
- .parent = TYPE_MACHINE,
- .class_init = riscv_sifive_u_machine_class_init,
- .instance_init = riscv_sifive_u_machine_instance_init,
- .instance_size = sizeof(SiFiveUState),
-};
-
-static void riscv_sifive_u_machine_init_register_types(void)
-{
- type_register_static(&riscv_sifive_u_machine_typeinfo);
-}
-
-type_init(riscv_sifive_u_machine_init_register_types)
diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c
index 5053fe4590..d0c4843712 100644
--- a/hw/riscv/spike.c
+++ b/hw/riscv/spike.c
@@ -45,6 +45,12 @@
#include <libfdt.h>
+#if defined(TARGET_RISCV32)
+# define BIOS_FILENAME "opensbi-riscv32-spike-fw_jump.elf"
+#else
+# define BIOS_FILENAME "opensbi-riscv64-spike-fw_jump.elf"
+#endif
+
static const struct MemmapEntry {
hwaddr base;
hwaddr size;
@@ -102,7 +108,11 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap,
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
qemu_fdt_add_subnode(fdt, nodename);
+#if defined(TARGET_RISCV32)
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv32");
+#else
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+#endif
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
@@ -183,8 +193,24 @@ static void spike_board_init(MachineState *machine)
memory_region_add_subregion(system_memory, memmap[SPIKE_MROM].base,
mask_rom);
+ riscv_find_and_load_firmware(machine, BIOS_FILENAME,
+ memmap[SPIKE_DRAM].base,
+ htif_symbol_callback);
+
if (machine->kernel_filename) {
- riscv_load_kernel(machine->kernel_filename, htif_symbol_callback);
+ uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
+ htif_symbol_callback);
+
+ if (machine->initrd_filename) {
+ hwaddr start;
+ hwaddr end = riscv_load_initrd(machine->initrd_filename,
+ machine->ram_size, kernel_entry,
+ &start);
+ qemu_fdt_setprop_cell(s->fdt, "/chosen",
+ "linux,initrd-start", start);
+ qemu_fdt_setprop_cell(s->fdt, "/chosen", "linux,initrd-end",
+ end);
+ }
}
/* reset vector */
@@ -450,7 +476,7 @@ static void spike_machine_init(MachineClass *mc)
{
mc->desc = "RISC-V Spike Board";
mc->init = spike_board_init;
- mc->max_cpus = 1;
+ mc->max_cpus = 8;
mc->is_default = true;
mc->default_cpu_type = SPIKE_V1_10_0_CPU;
}
diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c
index 85ec9e22aa..daae3ebdbb 100644
--- a/hw/riscv/virt.c
+++ b/hw/riscv/virt.c
@@ -229,7 +229,11 @@ static void create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap,
char *intc = g_strdup_printf("/cpus/cpu@%d/interrupt-controller", cpu);
char *isa = riscv_isa_string(&s->soc.harts[cpu]);
qemu_fdt_add_subnode(fdt, nodename);
+#if defined(TARGET_RISCV32)
+ qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv32");
+#else
qemu_fdt_setprop_string(fdt, nodename, "mmu-type", "riscv,sv48");
+#endif
qemu_fdt_setprop_string(fdt, nodename, "riscv,isa", isa);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv");
qemu_fdt_setprop_string(fdt, nodename, "status", "okay");
@@ -507,7 +511,7 @@ static void riscv_virt_board_init(MachineState *machine)
mask_rom);
riscv_find_and_load_firmware(machine, BIOS_FILENAME,
- memmap[VIRT_DRAM].base);
+ memmap[VIRT_DRAM].base, NULL);
if (machine->kernel_filename) {
uint64_t kernel_entry = riscv_load_kernel(machine->kernel_filename,
diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs
index e02ed80b68..a46a1c7894 100644
--- a/hw/s390x/Makefile.objs
+++ b/hw/s390x/Makefile.objs
@@ -31,6 +31,7 @@ obj-y += tod-qemu.o
obj-$(CONFIG_KVM) += tod-kvm.o
obj-$(CONFIG_KVM) += s390-skeys-kvm.o
obj-$(CONFIG_KVM) += s390-stattrib-kvm.o
+obj-$(CONFIG_KVM) += pv.o
obj-y += s390-ccw.o
obj-y += ap-device.o
obj-y += ap-bridge.o
diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c
index 8c3e019571..ce21494c08 100644
--- a/hw/s390x/ipl.c
+++ b/hw/s390x/ipl.c
@@ -1,10 +1,11 @@
/*
* bootloader support
*
- * Copyright IBM, Corp. 2012
+ * Copyright IBM, Corp. 2012, 2020
*
* Authors:
* Christian Borntraeger <borntraeger@de.ibm.com>
+ * Janosch Frank <frankja@linux.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at your
* option) any later version. See the COPYING file in the top-level directory.
@@ -27,6 +28,7 @@
#include "hw/s390x/vfio-ccw.h"
#include "hw/s390x/css.h"
#include "hw/s390x/ebcdic.h"
+#include "hw/s390x/pv.h"
#include "ipl.h"
#include "qemu/error-report.h"
#include "qemu/config-file.h"
@@ -566,12 +568,31 @@ void s390_ipl_update_diag308(IplParameterBlock *iplb)
{
S390IPLState *ipl = get_ipl_device();
- ipl->iplb = *iplb;
- ipl->iplb_valid = true;
+ /*
+ * The IPLB set and retrieved by subcodes 8/9 is completely
+ * separate from the one managed via subcodes 5/6.
+ */
+ if (iplb->pbt == S390_IPL_TYPE_PV) {
+ ipl->iplb_pv = *iplb;
+ ipl->iplb_valid_pv = true;
+ } else {
+ ipl->iplb = *iplb;
+ ipl->iplb_valid = true;
+ }
ipl->netboot = is_virtio_net_device(iplb);
update_machine_ipl_properties(iplb);
}
+IplParameterBlock *s390_ipl_get_iplb_pv(void)
+{
+ S390IPLState *ipl = get_ipl_device();
+
+ if (!ipl->iplb_valid_pv) {
+ return NULL;
+ }
+ return &ipl->iplb_pv;
+}
+
IplParameterBlock *s390_ipl_get_iplb(void)
{
S390IPLState *ipl = get_ipl_device();
@@ -660,6 +681,38 @@ static void s390_ipl_prepare_qipl(S390CPU *cpu)
cpu_physical_memory_unmap(addr, len, 1, len);
}
+int s390_ipl_prepare_pv_header(void)
+{
+ IplParameterBlock *ipib = s390_ipl_get_iplb_pv();
+ IPLBlockPV *ipib_pv = &ipib->pv;
+ void *hdr = g_malloc(ipib_pv->pv_header_len);
+ int rc;
+
+ cpu_physical_memory_read(ipib_pv->pv_header_addr, hdr,
+ ipib_pv->pv_header_len);
+ rc = s390_pv_set_sec_parms((uintptr_t)hdr,
+ ipib_pv->pv_header_len);
+ g_free(hdr);
+ return rc;
+}
+
+int s390_ipl_pv_unpack(void)
+{
+ IplParameterBlock *ipib = s390_ipl_get_iplb_pv();
+ IPLBlockPV *ipib_pv = &ipib->pv;
+ int i, rc = 0;
+
+ for (i = 0; i < ipib_pv->num_comp; i++) {
+ rc = s390_pv_unpack(ipib_pv->components[i].addr,
+ TARGET_PAGE_ALIGN(ipib_pv->components[i].size),
+ ipib_pv->components[i].tweak_pref);
+ if (rc) {
+ break;
+ }
+ }
+ return rc;
+}
+
void s390_ipl_prepare_cpu(S390CPU *cpu)
{
S390IPLState *ipl = get_ipl_device();
diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h
index 3e44abe1c6..53cc9eb5ac 100644
--- a/hw/s390x/ipl.h
+++ b/hw/s390x/ipl.h
@@ -1,8 +1,9 @@
/*
* s390 IPL device
*
- * Copyright 2015 IBM Corp.
+ * Copyright 2015, 2020 IBM Corp.
* Author(s): Zhang Fan <bjfanzh@cn.ibm.com>
+ * Janosch Frank <frankja@linux.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -13,8 +14,27 @@
#define HW_S390_IPL_H
#include "cpu.h"
+#include "exec/address-spaces.h"
#include "hw/qdev-core.h"
+struct IPLBlockPVComp {
+ uint64_t tweak_pref;
+ uint64_t addr;
+ uint64_t size;
+} QEMU_PACKED;
+typedef struct IPLBlockPVComp IPLBlockPVComp;
+
+struct IPLBlockPV {
+ uint8_t reserved18[87]; /* 0x18 */
+ uint8_t version; /* 0x6f */
+ uint32_t reserved70; /* 0x70 */
+ uint32_t num_comp; /* 0x74 */
+ uint64_t pv_header_addr; /* 0x78 */
+ uint64_t pv_header_len; /* 0x80 */
+ struct IPLBlockPVComp components[];
+} QEMU_PACKED;
+typedef struct IPLBlockPV IPLBlockPV;
+
struct IplBlockCcw {
uint8_t reserved0[85];
uint8_t ssid;
@@ -71,6 +91,7 @@ union IplParameterBlock {
union {
IplBlockCcw ccw;
IplBlockFcp fcp;
+ IPLBlockPV pv;
IplBlockQemuScsi scsi;
};
} QEMU_PACKED;
@@ -85,8 +106,11 @@ typedef union IplParameterBlock IplParameterBlock;
int s390_ipl_set_loadparm(uint8_t *loadparm);
void s390_ipl_update_diag308(IplParameterBlock *iplb);
+int s390_ipl_prepare_pv_header(void);
+int s390_ipl_pv_unpack(void);
void s390_ipl_prepare_cpu(S390CPU *cpu);
IplParameterBlock *s390_ipl_get_iplb(void);
+IplParameterBlock *s390_ipl_get_iplb_pv(void);
enum s390_reset {
/* default is a reset not triggered by a CPU e.g. issued by QMP */
@@ -94,6 +118,7 @@ enum s390_reset {
S390_RESET_REIPL,
S390_RESET_MODIFIED_CLEAR,
S390_RESET_LOAD_NORMAL,
+ S390_RESET_PV,
};
void s390_ipl_reset_request(CPUState *cs, enum s390_reset reset_type);
void s390_ipl_get_reset_request(CPUState **cs, enum s390_reset *reset_type);
@@ -133,6 +158,7 @@ struct S390IPLState {
/*< private >*/
DeviceState parent_obj;
IplParameterBlock iplb;
+ IplParameterBlock iplb_pv;
QemuIplParameters qipl;
uint64_t start_addr;
uint64_t compat_start_addr;
@@ -140,6 +166,7 @@ struct S390IPLState {
uint64_t compat_bios_start_addr;
bool enforce_bios;
bool iplb_valid;
+ bool iplb_valid_pv;
bool netboot;
/* reset related properties don't have to be migrated or reset */
enum s390_reset reset_type;
@@ -159,11 +186,29 @@ struct S390IPLState {
typedef struct S390IPLState S390IPLState;
QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong");
+#define DIAG_308_RC_OK 0x0001
+#define DIAG_308_RC_NO_CONF 0x0102
+#define DIAG_308_RC_INVALID 0x0402
+#define DIAG_308_RC_NO_PV_CONF 0x0902
+#define DIAG_308_RC_INVAL_FOR_PV 0x0a02
+
+#define DIAG308_RESET_MOD_CLR 0
+#define DIAG308_RESET_LOAD_NORM 1
+#define DIAG308_LOAD_CLEAR 3
+#define DIAG308_LOAD_NORMAL_DUMP 4
+#define DIAG308_SET 5
+#define DIAG308_STORE 6
+#define DIAG308_PV_SET 8
+#define DIAG308_PV_STORE 9
+#define DIAG308_PV_START 10
+
#define S390_IPL_TYPE_FCP 0x00
#define S390_IPL_TYPE_CCW 0x02
+#define S390_IPL_TYPE_PV 0x05
#define S390_IPL_TYPE_QEMU_SCSI 0xff
#define S390_IPLB_HEADER_LEN 8
+#define S390_IPLB_MIN_PV_LEN 148
#define S390_IPLB_MIN_CCW_LEN 200
#define S390_IPLB_MIN_FCP_LEN 384
#define S390_IPLB_MIN_QEMU_SCSI_LEN 200
@@ -173,6 +218,62 @@ static inline bool iplb_valid_len(IplParameterBlock *iplb)
return be32_to_cpu(iplb->len) <= sizeof(IplParameterBlock);
}
+static inline bool ipl_valid_pv_components(IplParameterBlock *iplb)
+{
+ IPLBlockPV *ipib_pv = &iplb->pv;
+ int i;
+
+ if (ipib_pv->num_comp == 0) {
+ return false;
+ }
+
+ for (i = 0; i < ipib_pv->num_comp; i++) {
+ /* Addr must be 4k aligned */
+ if (ipib_pv->components[i].addr & ~TARGET_PAGE_MASK) {
+ return false;
+ }
+
+ /* Tweak prefix is monotonically increasing with each component */
+ if (i < ipib_pv->num_comp - 1 &&
+ ipib_pv->components[i].tweak_pref >=
+ ipib_pv->components[i + 1].tweak_pref) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool ipl_valid_pv_header(IplParameterBlock *iplb)
+{
+ IPLBlockPV *ipib_pv = &iplb->pv;
+
+ if (ipib_pv->pv_header_len > 2 * TARGET_PAGE_SIZE) {
+ return false;
+ }
+
+ if (!address_space_access_valid(&address_space_memory,
+ ipib_pv->pv_header_addr,
+ ipib_pv->pv_header_len,
+ false,
+ MEMTXATTRS_UNSPECIFIED)) {
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool iplb_valid_pv(IplParameterBlock *iplb)
+{
+ if (iplb->pbt != S390_IPL_TYPE_PV ||
+ be32_to_cpu(iplb->len) < S390_IPLB_MIN_PV_LEN) {
+ return false;
+ }
+ if (!ipl_valid_pv_header(iplb)) {
+ return false;
+ }
+ return ipl_valid_pv_components(iplb);
+}
+
static inline bool iplb_valid(IplParameterBlock *iplb)
{
switch (iplb->pbt) {
diff --git a/hw/s390x/pv.c b/hw/s390x/pv.c
new file mode 100644
index 0000000000..f11868e865
--- /dev/null
+++ b/hw/s390x/pv.c
@@ -0,0 +1,113 @@
+/*
+ * Protected Virtualization functions
+ *
+ * Copyright IBM Corp. 2020
+ * Author(s):
+ * Janosch Frank <frankja@linux.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+#include "qemu/osdep.h"
+
+#include <linux/kvm.h>
+
+#include "cpu.h"
+#include "qemu/error-report.h"
+#include "sysemu/kvm.h"
+#include "hw/s390x/ipl.h"
+#include "hw/s390x/pv.h"
+
+static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data)
+{
+ struct kvm_pv_cmd pv_cmd = {
+ .cmd = cmd,
+ .data = (uint64_t)data,
+ };
+ int rc;
+
+ do {
+ rc = kvm_vm_ioctl(kvm_state, KVM_S390_PV_COMMAND, &pv_cmd);
+ } while (rc == -EINTR);
+
+ if (rc) {
+ error_report("KVM PV command %d (%s) failed: header rc %x rrc %x "
+ "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc,
+ rc);
+ }
+ return rc;
+}
+
+/*
+ * This macro lets us pass the command as a string to the function so
+ * we can print it on an error.
+ */
+#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data);
+#define s390_pv_cmd_exit(cmd, data) \
+{ \
+ int rc; \
+ \
+ rc = __s390_pv_cmd(cmd, #cmd, data);\
+ if (rc) { \
+ exit(1); \
+ } \
+}
+
+int s390_pv_vm_enable(void)
+{
+ return s390_pv_cmd(KVM_PV_ENABLE, NULL);
+}
+
+void s390_pv_vm_disable(void)
+{
+ s390_pv_cmd_exit(KVM_PV_DISABLE, NULL);
+}
+
+int s390_pv_set_sec_parms(uint64_t origin, uint64_t length)
+{
+ struct kvm_s390_pv_sec_parm args = {
+ .origin = origin,
+ .length = length,
+ };
+
+ return s390_pv_cmd(KVM_PV_SET_SEC_PARMS, &args);
+}
+
+/*
+ * Called for each component in the SE type IPL parameter block 0.
+ */
+int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak)
+{
+ struct kvm_s390_pv_unp args = {
+ .addr = addr,
+ .size = size,
+ .tweak = tweak,
+ };
+
+ return s390_pv_cmd(KVM_PV_UNPACK, &args);
+}
+
+void s390_pv_perf_clear_reset(void)
+{
+ s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL);
+}
+
+int s390_pv_verify(void)
+{
+ return s390_pv_cmd(KVM_PV_VERIFY, NULL);
+}
+
+void s390_pv_unshare(void)
+{
+ s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL);
+}
+
+void s390_pv_inject_reset_error(CPUState *cs)
+{
+ int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4;
+ CPUS390XState *env = &S390_CPU(cs)->env;
+
+ /* Report that we are unable to enter protected mode */
+ env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
+}
diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c
index 0fa00a9fff..45292fb5a8 100644
--- a/hw/s390x/s390-virtio-ccw.c
+++ b/hw/s390x/s390-virtio-ccw.c
@@ -1,9 +1,10 @@
/*
* virtio ccw machine
*
- * Copyright 2012 IBM Corp.
+ * Copyright 2012, 2020 IBM Corp.
* Copyright (c) 2009 Alexander Graf <agraf@suse.de>
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Janosch Frank <frankja@linux.ibm.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -42,6 +43,11 @@
#include "hw/qdev-properties.h"
#include "hw/s390x/tod.h"
#include "sysemu/sysemu.h"
+#include "sysemu/balloon.h"
+#include "hw/s390x/pv.h"
+#include "migration/blocker.h"
+
+static Error *pv_mig_blocker;
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
{
@@ -317,10 +323,93 @@ static inline void s390_do_cpu_ipl(CPUState *cs, run_on_cpu_data arg)
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
}
+static void s390_machine_unprotect(S390CcwMachineState *ms)
+{
+ s390_pv_vm_disable();
+ ms->pv = false;
+ migrate_del_blocker(pv_mig_blocker);
+ error_free_or_abort(&pv_mig_blocker);
+ qemu_balloon_inhibit(false);
+}
+
+static int s390_machine_protect(S390CcwMachineState *ms)
+{
+ Error *local_err = NULL;
+ int rc;
+
+ /*
+ * Ballooning on protected VMs needs support in the guest for
+ * sharing and unsharing balloon pages. Block ballooning for
+ * now, until we have a solution to make at least Linux guests
+ * either support it or fail gracefully.
+ */
+ qemu_balloon_inhibit(true);
+ error_setg(&pv_mig_blocker,
+ "protected VMs are currently not migrateable.");
+ rc = migrate_add_blocker(pv_mig_blocker, &local_err);
+ if (rc) {
+ qemu_balloon_inhibit(false);
+ error_report_err(local_err);
+ error_free_or_abort(&pv_mig_blocker);
+ return rc;
+ }
+
+ /* Create SE VM */
+ rc = s390_pv_vm_enable();
+ if (rc) {
+ qemu_balloon_inhibit(false);
+ migrate_del_blocker(pv_mig_blocker);
+ error_free_or_abort(&pv_mig_blocker);
+ return rc;
+ }
+
+ ms->pv = true;
+
+ /* Set SE header and unpack */
+ rc = s390_ipl_prepare_pv_header();
+ if (rc) {
+ goto out_err;
+ }
+
+ /* Decrypt image */
+ rc = s390_ipl_pv_unpack();
+ if (rc) {
+ goto out_err;
+ }
+
+ /* Verify integrity */
+ rc = s390_pv_verify();
+ if (rc) {
+ goto out_err;
+ }
+ return rc;
+
+out_err:
+ s390_machine_unprotect(ms);
+ return rc;
+}
+
+static void s390_pv_prepare_reset(S390CcwMachineState *ms)
+{
+ CPUState *cs;
+
+ if (!s390_is_pv()) {
+ return;
+ }
+ /* Unsharing requires all cpus to be stopped */
+ CPU_FOREACH(cs) {
+ s390_cpu_set_state(S390_CPU_STATE_STOPPED, S390_CPU(cs));
+ }
+ s390_pv_unshare();
+ s390_pv_perf_clear_reset();
+}
+
static void s390_machine_reset(MachineState *machine)
{
+ S390CcwMachineState *ms = S390_CCW_MACHINE(machine);
enum s390_reset reset_type;
CPUState *cs, *t;
+ S390CPU *cpu;
/* get the reset parameters, reset them once done */
s390_ipl_get_reset_request(&cs, &reset_type);
@@ -328,9 +417,15 @@ static void s390_machine_reset(MachineState *machine)
/* all CPUs are paused and synchronized at this point */
s390_cmma_reset();
+ cpu = S390_CPU(cs);
+
switch (reset_type) {
case S390_RESET_EXTERNAL:
case S390_RESET_REIPL:
+ if (s390_is_pv()) {
+ s390_machine_unprotect(ms);
+ }
+
qemu_devices_reset();
s390_crypto_reset();
@@ -338,24 +433,58 @@ static void s390_machine_reset(MachineState *machine)
run_on_cpu(cs, s390_do_cpu_ipl, RUN_ON_CPU_NULL);
break;
case S390_RESET_MODIFIED_CLEAR:
+ /*
+ * Susbsystem reset needs to be done before we unshare memory
+ * and lose access to VIRTIO structures in guest memory.
+ */
+ subsystem_reset();
+ s390_crypto_reset();
+ s390_pv_prepare_reset(ms);
CPU_FOREACH(t) {
run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
}
- subsystem_reset();
- s390_crypto_reset();
run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL);
break;
case S390_RESET_LOAD_NORMAL:
+ /*
+ * Susbsystem reset needs to be done before we unshare memory
+ * and lose access to VIRTIO structures in guest memory.
+ */
+ subsystem_reset();
+ s390_pv_prepare_reset(ms);
CPU_FOREACH(t) {
if (t == cs) {
continue;
}
run_on_cpu(t, s390_do_cpu_reset, RUN_ON_CPU_NULL);
}
- subsystem_reset();
run_on_cpu(cs, s390_do_cpu_initial_reset, RUN_ON_CPU_NULL);
run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL);
break;
+ case S390_RESET_PV: /* Subcode 10 */
+ subsystem_reset();
+ s390_crypto_reset();
+
+ CPU_FOREACH(t) {
+ if (t == cs) {
+ continue;
+ }
+ run_on_cpu(t, s390_do_cpu_full_reset, RUN_ON_CPU_NULL);
+ }
+ run_on_cpu(cs, s390_do_cpu_reset, RUN_ON_CPU_NULL);
+
+ if (s390_machine_protect(ms)) {
+ s390_pv_inject_reset_error(cs);
+ /*
+ * Continue after the diag308 so the guest knows something
+ * went wrong.
+ */
+ s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
+ return;
+ }
+
+ run_on_cpu(cs, s390_do_cpu_load_normal, RUN_ON_CPU_NULL);
+ break;
default:
g_assert_not_reached();
}
diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c
index f0c35aa57a..ede056b3ef 100644
--- a/hw/s390x/sclp.c
+++ b/hw/s390x/sclp.c
@@ -33,6 +33,22 @@ static inline SCLPDevice *get_sclp_device(void)
return sclp;
}
+static inline bool sclp_command_code_valid(uint32_t code)
+{
+ switch (code & SCLP_CMD_CODE_MASK) {
+ case SCLP_CMDW_READ_SCP_INFO:
+ case SCLP_CMDW_READ_SCP_INFO_FORCED:
+ case SCLP_CMDW_READ_CPU_INFO:
+ case SCLP_CMDW_CONFIGURE_IOA:
+ case SCLP_CMDW_DECONFIGURE_IOA:
+ case SCLP_CMD_READ_EVENT_DATA:
+ case SCLP_CMD_WRITE_EVENT_DATA:
+ case SCLP_CMD_WRITE_EVENT_MASK:
+ return true;
+ }
+ return false;
+}
+
static void prepare_cpu_entries(SCLPDevice *sclp, CPUEntry *entry, int *count)
{
MachineState *ms = MACHINE(qdev_get_machine());
@@ -193,6 +209,34 @@ static void sclp_execute(SCLPDevice *sclp, SCCB *sccb, uint32_t code)
}
}
+/*
+ * We only need the address to have something valid for the
+ * service_interrupt call.
+ */
+#define SCLP_PV_DUMMY_ADDR 0x4000
+int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb,
+ uint32_t code)
+{
+ SCLPDevice *sclp = get_sclp_device();
+ SCLPDeviceClass *sclp_c = SCLP_GET_CLASS(sclp);
+ SCCB work_sccb;
+ hwaddr sccb_len = sizeof(SCCB);
+
+ s390_cpu_pv_mem_read(env_archcpu(env), 0, &work_sccb, sccb_len);
+
+ if (!sclp_command_code_valid(code)) {
+ work_sccb.h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
+ goto out_write;
+ }
+
+ sclp_c->execute(sclp, &work_sccb, code);
+out_write:
+ s390_cpu_pv_mem_write(env_archcpu(env), 0, &work_sccb,
+ be16_to_cpu(work_sccb.h.length));
+ sclp_c->service_interrupt(sclp, SCLP_PV_DUMMY_ADDR);
+ return 0;
+}
+
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
{
SCLPDevice *sclp = get_sclp_device();
@@ -225,17 +269,7 @@ int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code)
return -PGM_SPECIFICATION;
}
- switch (code & SCLP_CMD_CODE_MASK) {
- case SCLP_CMDW_READ_SCP_INFO:
- case SCLP_CMDW_READ_SCP_INFO_FORCED:
- case SCLP_CMDW_READ_CPU_INFO:
- case SCLP_CMDW_CONFIGURE_IOA:
- case SCLP_CMDW_DECONFIGURE_IOA:
- case SCLP_CMD_READ_EVENT_DATA:
- case SCLP_CMD_WRITE_EVENT_DATA:
- case SCLP_CMD_WRITE_EVENT_MASK:
- break;
- default:
+ if (!sclp_command_code_valid(code)) {
work_sccb.h.response_code = cpu_to_be16(SCLP_RC_INVALID_SCLP_COMMAND);
goto out_write;
}
diff --git a/include/hw/arm/msf2-soc.h b/include/hw/arm/msf2-soc.h
index 3cfe5c76ee..c9cb214aa6 100644
--- a/include/hw/arm/msf2-soc.h
+++ b/include/hw/arm/msf2-soc.h
@@ -29,6 +29,7 @@
#include "hw/timer/mss-timer.h"
#include "hw/misc/msf2-sysreg.h"
#include "hw/ssi/mss-spi.h"
+#include "hw/net/msf2-emac.h"
#define TYPE_MSF2_SOC "msf2-soc"
#define MSF2_SOC(obj) OBJECT_CHECK(MSF2State, (obj), TYPE_MSF2_SOC)
@@ -62,6 +63,7 @@ typedef struct MSF2State {
MSF2SysregState sysreg;
MSSTimerState timer;
MSSSpiState spi[MSF2_NUM_SPIS];
+ MSF2EmacState emac;
} MSF2State;
#endif
diff --git a/include/hw/char/cadence_uart.h b/include/hw/char/cadence_uart.h
index 47cec956c4..2a179a572f 100644
--- a/include/hw/char/cadence_uart.h
+++ b/include/hw/char/cadence_uart.h
@@ -49,6 +49,7 @@ typedef struct {
CharBackend chr;
qemu_irq irq;
QEMUTimer *fifo_trigger_handle;
+ Clock *refclk;
} CadenceUARTState;
static inline DeviceState *cadence_uart_create(hwaddr addr,
diff --git a/include/hw/clock.h b/include/hw/clock.h
new file mode 100644
index 0000000000..f822a94220
--- /dev/null
+++ b/include/hw/clock.h
@@ -0,0 +1,225 @@
+/*
+ * Hardware Clocks
+ *
+ * Copyright GreenSocs 2016-2020
+ *
+ * Authors:
+ * Frederic Konrad
+ * Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QEMU_HW_CLOCK_H
+#define QEMU_HW_CLOCK_H
+
+#include "qom/object.h"
+#include "qemu/queue.h"
+
+#define TYPE_CLOCK "clock"
+#define CLOCK(obj) OBJECT_CHECK(Clock, (obj), TYPE_CLOCK)
+
+typedef void ClockCallback(void *opaque);
+
+/*
+ * clock store a value representing the clock's period in 2^-32ns unit.
+ * It can represent:
+ * + periods from 2^-32ns up to 4seconds
+ * + frequency from ~0.25Hz 2e10Ghz
+ * Resolution of frequency representation decreases with frequency:
+ * + at 100MHz, resolution is ~2mHz
+ * + at 1Ghz, resolution is ~0.2Hz
+ * + at 10Ghz, resolution is ~20Hz
+ */
+#define CLOCK_PERIOD_1SEC (1000000000llu << 32)
+
+/*
+ * macro helpers to convert to hertz / nanosecond
+ */
+#define CLOCK_PERIOD_FROM_NS(ns) ((ns) * (CLOCK_PERIOD_1SEC / 1000000000llu))
+#define CLOCK_PERIOD_TO_NS(per) ((per) / (CLOCK_PERIOD_1SEC / 1000000000llu))
+#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u)
+#define CLOCK_PERIOD_TO_HZ(per) (((per) != 0) ? CLOCK_PERIOD_1SEC / (per) : 0u)
+
+/**
+ * Clock:
+ * @parent_obj: parent class
+ * @period: unsigned integer representing the period of the clock
+ * @canonical_path: clock path string cache (used for trace purpose)
+ * @callback: called when clock changes
+ * @callback_opaque: argument for @callback
+ * @source: source (or parent in clock tree) of the clock
+ * @children: list of clocks connected to this one (it is their source)
+ * @sibling: structure used to form a clock list
+ */
+
+typedef struct Clock Clock;
+
+struct Clock {
+ /*< private >*/
+ Object parent_obj;
+
+ /* all fields are private and should not be modified directly */
+
+ /* fields */
+ uint64_t period;
+ char *canonical_path;
+ ClockCallback *callback;
+ void *callback_opaque;
+
+ /* Clocks are organized in a clock tree */
+ Clock *source;
+ QLIST_HEAD(, Clock) children;
+ QLIST_ENTRY(Clock) sibling;
+};
+
+/*
+ * vmstate description entry to be added in device vmsd.
+ */
+extern const VMStateDescription vmstate_clock;
+#define VMSTATE_CLOCK(field, state) \
+ VMSTATE_CLOCK_V(field, state, 0)
+#define VMSTATE_CLOCK_V(field, state, version) \
+ VMSTATE_STRUCT_POINTER_V(field, state, version, vmstate_clock, Clock)
+
+/**
+ * clock_setup_canonical_path:
+ * @clk: clock
+ *
+ * compute the canonical path of the clock (used by log messages)
+ */
+void clock_setup_canonical_path(Clock *clk);
+
+/**
+ * clock_set_callback:
+ * @clk: the clock to register the callback into
+ * @cb: the callback function
+ * @opaque: the argument to the callback
+ *
+ * Register a callback called on every clock update.
+ */
+void clock_set_callback(Clock *clk, ClockCallback *cb, void *opaque);
+
+/**
+ * clock_clear_callback:
+ * @clk: the clock to delete the callback from
+ *
+ * Unregister the callback registered with clock_set_callback.
+ */
+void clock_clear_callback(Clock *clk);
+
+/**
+ * clock_set_source:
+ * @clk: the clock.
+ * @src: the source clock
+ *
+ * Setup @src as the clock source of @clk. The current @src period
+ * value is also copied to @clk and its subtree but no callback is
+ * called.
+ * Further @src update will be propagated to @clk and its subtree.
+ */
+void clock_set_source(Clock *clk, Clock *src);
+
+/**
+ * clock_set:
+ * @clk: the clock to initialize.
+ * @value: the clock's value, 0 means unclocked
+ *
+ * Set the local cached period value of @clk to @value.
+ */
+void clock_set(Clock *clk, uint64_t value);
+
+static inline void clock_set_hz(Clock *clk, unsigned hz)
+{
+ clock_set(clk, CLOCK_PERIOD_FROM_HZ(hz));
+}
+
+static inline void clock_set_ns(Clock *clk, unsigned ns)
+{
+ clock_set(clk, CLOCK_PERIOD_FROM_NS(ns));
+}
+
+/**
+ * clock_propagate:
+ * @clk: the clock
+ *
+ * Propagate the clock period that has been previously configured using
+ * @clock_set(). This will update recursively all connected clocks.
+ * It is an error to call this function on a clock which has a source.
+ * Note: this function must not be called during device inititialization
+ * or migration.
+ */
+void clock_propagate(Clock *clk);
+
+/**
+ * clock_update:
+ * @clk: the clock to update.
+ * @value: the new clock's value, 0 means unclocked
+ *
+ * Update the @clk to the new @value. All connected clocks will be informed
+ * of this update. This is equivalent to call @clock_set() then
+ * @clock_propagate().
+ */
+static inline void clock_update(Clock *clk, uint64_t value)
+{
+ clock_set(clk, value);
+ clock_propagate(clk);
+}
+
+static inline void clock_update_hz(Clock *clk, unsigned hz)
+{
+ clock_update(clk, CLOCK_PERIOD_FROM_HZ(hz));
+}
+
+static inline void clock_update_ns(Clock *clk, unsigned ns)
+{
+ clock_update(clk, CLOCK_PERIOD_FROM_NS(ns));
+}
+
+/**
+ * clock_get:
+ * @clk: the clk to fetch the clock
+ *
+ * @return: the current period.
+ */
+static inline uint64_t clock_get(const Clock *clk)
+{
+ return clk->period;
+}
+
+static inline unsigned clock_get_hz(Clock *clk)
+{
+ return CLOCK_PERIOD_TO_HZ(clock_get(clk));
+}
+
+static inline unsigned clock_get_ns(Clock *clk)
+{
+ return CLOCK_PERIOD_TO_NS(clock_get(clk));
+}
+
+/**
+ * clock_is_enabled:
+ * @clk: a clock
+ *
+ * @return: true if the clock is running.
+ */
+static inline bool clock_is_enabled(const Clock *clk)
+{
+ return clock_get(clk) != 0;
+}
+
+static inline void clock_init(Clock *clk, uint64_t value)
+{
+ clock_set(clk, value);
+}
+static inline void clock_init_hz(Clock *clk, uint64_t value)
+{
+ clock_set_hz(clk, value);
+}
+static inline void clock_init_ns(Clock *clk, uint64_t value)
+{
+ clock_set_ns(clk, value);
+}
+
+#endif /* QEMU_HW_CLOCK_H */
diff --git a/include/hw/gpio/nrf51_gpio.h b/include/hw/gpio/nrf51_gpio.h
index 337ee534bb..1d62bbc928 100644
--- a/include/hw/gpio/nrf51_gpio.h
+++ b/include/hw/gpio/nrf51_gpio.h
@@ -42,7 +42,7 @@
#define NRF51_GPIO_REG_DIRSET 0x518
#define NRF51_GPIO_REG_DIRCLR 0x51C
#define NRF51_GPIO_REG_CNF_START 0x700
-#define NRF51_GPIO_REG_CNF_END 0x77F
+#define NRF51_GPIO_REG_CNF_END 0x77C
#define NRF51_GPIO_PULLDOWN 1
#define NRF51_GPIO_PULLUP 3
diff --git a/include/hw/net/msf2-emac.h b/include/hw/net/msf2-emac.h
new file mode 100644
index 0000000000..37966d3a81
--- /dev/null
+++ b/include/hw/net/msf2-emac.h
@@ -0,0 +1,53 @@
+/*
+ * QEMU model of the Smartfusion2 Ethernet MAC.
+ *
+ * Copyright (c) 2020 Subbaraya Sundeep <sundeep.lkml@gmail.com>.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "hw/sysbus.h"
+#include "exec/memory.h"
+#include "net/net.h"
+#include "net/eth.h"
+
+#define TYPE_MSS_EMAC "msf2-emac"
+#define MSS_EMAC(obj) \
+ OBJECT_CHECK(MSF2EmacState, (obj), TYPE_MSS_EMAC)
+
+#define R_MAX (0x1a0 / 4)
+#define PHY_MAX_REGS 32
+
+typedef struct MSF2EmacState {
+ SysBusDevice parent;
+
+ MemoryRegion mmio;
+ MemoryRegion *dma_mr;
+ AddressSpace dma_as;
+
+ qemu_irq irq;
+ NICState *nic;
+ NICConf conf;
+
+ uint8_t mac_addr[ETH_ALEN];
+ uint32_t rx_desc;
+ uint16_t phy_regs[PHY_MAX_REGS];
+
+ uint32_t regs[R_MAX];
+} MSF2EmacState;
diff --git a/include/hw/qdev-clock.h b/include/hw/qdev-clock.h
new file mode 100644
index 0000000000..a340f65ff9
--- /dev/null
+++ b/include/hw/qdev-clock.h
@@ -0,0 +1,159 @@
+/*
+ * Device's clock input and output
+ *
+ * Copyright GreenSocs 2016-2020
+ *
+ * Authors:
+ * Frederic Konrad
+ * Damien Hedde
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#ifndef QDEV_CLOCK_H
+#define QDEV_CLOCK_H
+
+#include "hw/clock.h"
+
+/**
+ * qdev_init_clock_in:
+ * @dev: the device to add an input clock to
+ * @name: the name of the clock (can't be NULL).
+ * @callback: optional callback to be called on update or NULL.
+ * @opaque: argument for the callback
+ * @returns: a pointer to the newly added clock
+ *
+ * Add an input clock to device @dev as a clock named @name.
+ * This adds a child<> property.
+ * The callback will be called with @opaque as opaque parameter.
+ */
+Clock *qdev_init_clock_in(DeviceState *dev, const char *name,
+ ClockCallback *callback, void *opaque);
+
+/**
+ * qdev_init_clock_out:
+ * @dev: the device to add an output clock to
+ * @name: the name of the clock (can't be NULL).
+ * @returns: a pointer to the newly added clock
+ *
+ * Add an output clock to device @dev as a clock named @name.
+ * This adds a child<> property.
+ */
+Clock *qdev_init_clock_out(DeviceState *dev, const char *name);
+
+/**
+ * qdev_get_clock_in:
+ * @dev: the device which has the clock
+ * @name: the name of the clock (can't be NULL).
+ * @returns: a pointer to the clock
+ *
+ * Get the input clock @name from @dev or NULL if does not exist.
+ */
+Clock *qdev_get_clock_in(DeviceState *dev, const char *name);
+
+/**
+ * qdev_get_clock_out:
+ * @dev: the device which has the clock
+ * @name: the name of the clock (can't be NULL).
+ * @returns: a pointer to the clock
+ *
+ * Get the output clock @name from @dev or NULL if does not exist.
+ */
+Clock *qdev_get_clock_out(DeviceState *dev, const char *name);
+
+/**
+ * qdev_connect_clock_in:
+ * @dev: a device
+ * @name: the name of an input clock in @dev
+ * @source: the source clock (an output clock of another device for example)
+ *
+ * Set the source clock of input clock @name of device @dev to @source.
+ * @source period update will be propagated to @name clock.
+ */
+static inline void qdev_connect_clock_in(DeviceState *dev, const char *name,
+ Clock *source)
+{
+ clock_set_source(qdev_get_clock_in(dev, name), source);
+}
+
+/**
+ * qdev_alias_clock:
+ * @dev: the device which has the clock
+ * @name: the name of the clock in @dev (can't be NULL)
+ * @alias_dev: the device to add the clock
+ * @alias_name: the name of the clock in @container
+ * @returns: a pointer to the clock
+ *
+ * Add a clock @alias_name in @alias_dev which is an alias of the clock @name
+ * in @dev. The direction _in_ or _out_ will the same as the original.
+ * An alias clock must not be modified or used by @alias_dev and should
+ * typically be only only for device composition purpose.
+ */
+Clock *qdev_alias_clock(DeviceState *dev, const char *name,
+ DeviceState *alias_dev, const char *alias_name);
+
+/**
+ * qdev_finalize_clocklist:
+ * @dev: the device being finalized
+ *
+ * Clear the clocklist from @dev. Only used internally in qdev.
+ */
+void qdev_finalize_clocklist(DeviceState *dev);
+
+/**
+ * ClockPortInitElem:
+ * @name: name of the clock (can't be NULL)
+ * @output: indicates whether the clock is input or output
+ * @callback: for inputs, optional callback to be called on clock's update
+ * with device as opaque
+ * @offset: optional offset to store the ClockIn or ClockOut pointer in device
+ * state structure (0 means unused)
+ */
+struct ClockPortInitElem {
+ const char *name;
+ bool is_output;
+ ClockCallback *callback;
+ size_t offset;
+};
+
+#define clock_offset_value(devstate, field) \
+ (offsetof(devstate, field) + \
+ type_check(Clock *, typeof_field(devstate, field)))
+
+#define QDEV_CLOCK(out_not_in, devstate, field, cb) { \
+ .name = (stringify(field)), \
+ .is_output = out_not_in, \
+ .callback = cb, \
+ .offset = clock_offset_value(devstate, field), \
+}
+
+/**
+ * QDEV_CLOCK_(IN|OUT):
+ * @devstate: structure type. @dev argument of qdev_init_clocks below must be
+ * a pointer to that same type.
+ * @field: a field in @_devstate (must be Clock*)
+ * @callback: (for input only) callback (or NULL) to be called with the device
+ * state as argument
+ *
+ * The name of the clock will be derived from @field
+ */
+#define QDEV_CLOCK_IN(devstate, field, callback) \
+ QDEV_CLOCK(false, devstate, field, callback)
+
+#define QDEV_CLOCK_OUT(devstate, field) \
+ QDEV_CLOCK(true, devstate, field, NULL)
+
+#define QDEV_CLOCK_END { .name = NULL }
+
+typedef struct ClockPortInitElem ClockPortInitArray[];
+
+/**
+ * qdev_init_clocks:
+ * @dev: the device to add clocks to
+ * @clocks: a QDEV_CLOCK_END-terminated array which contains the
+ * clocks information.
+ */
+void qdev_init_clocks(DeviceState *dev, const ClockPortInitArray clocks);
+
+#endif /* QDEV_CLOCK_H */
diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h
index 1405b8a990..d87d989e72 100644
--- a/include/hw/qdev-core.h
+++ b/include/hw/qdev-core.h
@@ -149,6 +149,17 @@ struct NamedGPIOList {
QLIST_ENTRY(NamedGPIOList) node;
};
+typedef struct Clock Clock;
+typedef struct NamedClockList NamedClockList;
+
+struct NamedClockList {
+ char *name;
+ Clock *clock;
+ bool output;
+ bool alias;
+ QLIST_ENTRY(NamedClockList) node;
+};
+
/**
* DeviceState:
* @realized: Indicates whether the device has been fully constructed.
@@ -171,6 +182,7 @@ struct DeviceState {
bool allow_unplug_during_migration;
BusState *parent_bus;
QLIST_HEAD(, NamedGPIOList) gpios;
+ QLIST_HEAD(, NamedClockList) clocks;
QLIST_HEAD(, BusState) child_bus;
int num_child_bus;
int instance_id_alias;
diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h
index df80051fbc..474a940ad5 100644
--- a/include/hw/riscv/boot.h
+++ b/include/hw/riscv/boot.h
@@ -24,10 +24,12 @@
void riscv_find_and_load_firmware(MachineState *machine,
const char *default_machine_firmware,
- hwaddr firmware_load_addr);
+ hwaddr firmware_load_addr,
+ symbol_fn_t sym_cb);
char *riscv_find_firmware(const char *firmware_filename);
target_ulong riscv_load_firmware(const char *firmware_filename,
- hwaddr firmware_load_addr);
+ hwaddr firmware_load_addr,
+ symbol_fn_t sym_cb);
target_ulong riscv_load_kernel(const char *kernel_filename,
symbol_fn_t sym_cb);
hwaddr riscv_load_initrd(const char *filename, uint64_t mem_size,
diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h
index 82667b5746..16c297ec5f 100644
--- a/include/hw/riscv/sifive_u.h
+++ b/include/hw/riscv/sifive_u.h
@@ -42,6 +42,8 @@ typedef struct SiFiveUSoCState {
SiFiveUPRCIState prci;
SiFiveUOTPState otp;
CadenceGEMState gem;
+
+ uint32_t serial;
} SiFiveUSoCState;
#define TYPE_RISCV_U_MACHINE MACHINE_TYPE_NAME("sifive_u")
@@ -59,6 +61,7 @@ typedef struct SiFiveUState {
int fdt_size;
bool start_in_flash;
+ uint32_t serial;
} SiFiveUState;
enum {
diff --git a/include/hw/s390x/pv.h b/include/hw/s390x/pv.h
new file mode 100644
index 0000000000..522ca6a04e
--- /dev/null
+++ b/include/hw/s390x/pv.h
@@ -0,0 +1,58 @@
+/*
+ * Protected Virtualization header
+ *
+ * Copyright IBM Corp. 2020
+ * Author(s):
+ * Janosch Frank <frankja@linux.ibm.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or (at
+ * your option) any later version. See the COPYING file in the top-level
+ * directory.
+ */
+#ifndef HW_S390_PV_H
+#define HW_S390_PV_H
+
+#ifdef CONFIG_KVM
+#include "cpu.h"
+#include "hw/s390x/s390-virtio-ccw.h"
+
+static inline bool s390_is_pv(void)
+{
+ static S390CcwMachineState *ccw;
+ Object *obj;
+
+ if (ccw) {
+ return ccw->pv;
+ }
+
+ /* we have to bail out for the "none" machine */
+ obj = object_dynamic_cast(qdev_get_machine(),
+ TYPE_S390_CCW_MACHINE);
+ if (!obj) {
+ return false;
+ }
+ ccw = S390_CCW_MACHINE(obj);
+ return ccw->pv;
+}
+
+int s390_pv_vm_enable(void);
+void s390_pv_vm_disable(void);
+int s390_pv_set_sec_parms(uint64_t origin, uint64_t length);
+int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak);
+void s390_pv_perf_clear_reset(void);
+int s390_pv_verify(void);
+void s390_pv_unshare(void);
+void s390_pv_inject_reset_error(CPUState *cs);
+#else /* CONFIG_KVM */
+static inline bool s390_is_pv(void) { return false; }
+static inline int s390_pv_vm_enable(void) { return 0; }
+static inline void s390_pv_vm_disable(void) {}
+static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length) { return 0; }
+static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; }
+static inline void s390_pv_perf_clear_reset(void) {}
+static inline int s390_pv_verify(void) { return 0; }
+static inline void s390_pv_unshare(void) {}
+static inline void s390_pv_inject_reset_error(CPUState *cs) {};
+#endif /* CONFIG_KVM */
+
+#endif /* HW_S390_PV_H */
diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h
index 8aa27199c9..cd1dccc6e3 100644
--- a/include/hw/s390x/s390-virtio-ccw.h
+++ b/include/hw/s390x/s390-virtio-ccw.h
@@ -28,6 +28,7 @@ typedef struct S390CcwMachineState {
/*< public >*/
bool aes_key_wrap;
bool dea_key_wrap;
+ bool pv;
uint8_t loadparm[8];
} S390CcwMachineState;
diff --git a/include/hw/s390x/sclp.h b/include/hw/s390x/sclp.h
index cd7b24359f..822eff4396 100644
--- a/include/hw/s390x/sclp.h
+++ b/include/hw/s390x/sclp.h
@@ -217,5 +217,7 @@ void s390_sclp_init(void);
void sclp_service_interrupt(uint32_t sccb);
void raise_irq_cpu_hotplug(void);
int sclp_service_call(CPUS390XState *env, uint64_t sccb, uint32_t code);
+int sclp_service_call_protected(CPUS390XState *env, uint64_t sccb,
+ uint32_t code);
#endif
diff --git a/include/qapi/qmp/qdict.h b/include/qapi/qmp/qdict.h
index 7f3ec10a10..da942347a7 100644
--- a/include/qapi/qmp/qdict.h
+++ b/include/qapi/qmp/qdict.h
@@ -40,9 +40,6 @@ void qdict_del(QDict *qdict, const char *key);
int qdict_haskey(const QDict *qdict, const char *key);
QObject *qdict_get(const QDict *qdict, const char *key);
bool qdict_is_equal(const QObject *x, const QObject *y);
-void qdict_iter(const QDict *qdict,
- void (*iter)(const char *key, QObject *obj, void *opaque),
- void *opaque);
const QDictEntry *qdict_first(const QDict *qdict);
const QDictEntry *qdict_next(const QDict *qdict, const QDictEntry *entry);
void qdict_destroy_obj(QObject *obj);
diff --git a/include/qapi/qmp/qlist.h b/include/qapi/qmp/qlist.h
index 8d2c32ca28..595b7943e1 100644
--- a/include/qapi/qmp/qlist.h
+++ b/include/qapi/qmp/qlist.h
@@ -34,10 +34,10 @@ void qlist_append_int(QList *qlist, int64_t value);
void qlist_append_null(QList *qlist);
void qlist_append_str(QList *qlist, const char *value);
-#define QLIST_FOREACH_ENTRY(qlist, var) \
- for ((var) = ((qlist)->head.tqh_first); \
- (var); \
- (var) = ((var)->next.tqe_next))
+#define QLIST_FOREACH_ENTRY(qlist, var) \
+ for ((var) = QTAILQ_FIRST(&(qlist)->head); \
+ (var); \
+ (var) = QTAILQ_NEXT((var), next))
static inline QObject *qlist_entry_obj(const QListEntry *entry)
{
@@ -47,8 +47,6 @@ static inline QObject *qlist_entry_obj(const QListEntry *entry)
QList *qlist_new(void);
QList *qlist_copy(QList *src);
void qlist_append_obj(QList *qlist, QObject *obj);
-void qlist_iter(const QList *qlist,
- void (*iter)(QObject *obj, void *opaque), void *opaque);
QObject *qlist_pop(QList *qlist);
QObject *qlist_peek(QList *qlist);
int qlist_empty(const QList *qlist);
diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index 8ccb3b6c20..98dc533d39 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -43,6 +43,10 @@ typedef enum VisitorType {
struct Visitor
{
+ /*
+ * Only input visitors may fail!
+ */
+
/* Must be set to visit structs */
void (*start_struct)(Visitor *v, const char *name, void **obj,
size_t size, Error **errp);
@@ -67,13 +71,12 @@ struct Visitor
/* Must be set */
void (*end_list)(Visitor *v, void **list);
- /* Must be set by input and dealloc visitors to visit alternates;
- * optional for output visitors. */
+ /* Must be set by input and clone visitors to visit alternates */
void (*start_alternate)(Visitor *v, const char *name,
GenericAlternate **obj, size_t size,
Error **errp);
- /* Optional, needed for dealloc visitor */
+ /* Optional */
void (*end_alternate)(Visitor *v, void **obj);
/* Must be set */
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index c5b23851a1..5573906966 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -25,19 +25,21 @@
* for doing work at each node of a QAPI graph; it can also be used
* for a virtual walk, where there is no actual QAPI C struct.
*
- * There are four kinds of visitor classes: input visitors (QObject,
- * string, and QemuOpts) parse an external representation and build
- * the corresponding QAPI graph, output visitors (QObject and string) take
- * a completed QAPI graph and generate an external representation, the
- * dealloc visitor can take a QAPI graph (possibly partially
- * constructed) and recursively free its resources, and the clone
- * visitor performs a deep clone of one QAPI object to another. While
- * the dealloc and QObject input/output visitors are general, the string,
- * QemuOpts, and clone visitors have some implementation limitations;
- * see the documentation for each visitor for more details on what it
- * supports. Also, see visitor-impl.h for the callback contracts
- * implemented by each visitor, and docs/devel/qapi-code-gen.txt for more
- * about the QAPI code generator.
+ * There are four kinds of visitors: input visitors (QObject, string,
+ * and QemuOpts) parse an external representation and build the
+ * corresponding QAPI object, output visitors (QObject and string)
+ * take a QAPI object and generate an external representation, the
+ * dealloc visitor takes a QAPI object (possibly partially
+ * constructed) and recursively frees it, and the clone visitor
+ * performs a deep clone of a QAPI object.
+ *
+ * While the dealloc and QObject input/output visitors are general,
+ * the string, QemuOpts, and clone visitors have some implementation
+ * limitations; see the documentation for each visitor for more
+ * details on what it supports. Also, see visitor-impl.h for the
+ * callback contracts implemented by each visitor, and
+ * docs/devel/qapi-code-gen.txt for more about the QAPI code
+ * generator.
*
* All of the visitors are created via:
*
@@ -45,11 +47,15 @@
*
* A visitor should be used for exactly one top-level visit_type_FOO()
* or virtual walk; if that is successful, the caller can optionally
- * call visit_complete() (for now, useful only for output visits, but
- * safe to call on all visits). Then, regardless of success or
- * failure, the user should call visit_free() to clean up resources.
- * It is okay to free the visitor without completing the visit, if
- * some other error is detected in the meantime.
+ * call visit_complete() (useful only for output visits, but safe to
+ * call on all visits). Then, regardless of success or failure, the
+ * user should call visit_free() to clean up resources. It is okay to
+ * free the visitor without completing the visit, if some other error
+ * is detected in the meantime.
+ *
+ * The clone and dealloc visitor should not be used directly outside
+ * of QAPI code. Use the qapi_free_FOO() and QAPI_CLONE() instead,
+ * described below.
*
* All QAPI types have a corresponding function with a signature
* roughly compatible with this:
@@ -58,7 +64,7 @@
*
* where T is FOO for scalar types, and FOO * otherwise. The scalar
* visitors are declared here; the remaining visitors are generated in
- * qapi-visit.h.
+ * qapi-visit-MODULE.h.
*
* The @name parameter of visit_type_FOO() describes the relation
* between this QAPI value and its parent container. When visiting
@@ -68,55 +74,56 @@
* alternate, @name should equal the name used for visiting the
* alternate.
*
- * The visit_type_FOO() functions expect a non-null @obj argument;
- * they allocate *@obj during input visits, leave it unchanged on
- * output visits, and recursively free any resources during a dealloc
- * visit. Each function also takes the customary @errp argument (see
+ * The visit_type_FOO() functions take a non-null @obj argument; they
+ * allocate *@obj during input visits, leave it unchanged during
+ * output and clone visits, and free it (recursively) during a dealloc
+ * visit.
+ *
+ * Each function also takes the customary @errp argument (see
* qapi/error.h for details), for reporting any errors (such as if a
* member @name is not present, or is present but not the specified
- * type).
+ * type). Only input visitors can fail.
*
* If an error is detected during visit_type_FOO() with an input
- * visitor, then *@obj will be NULL for pointer types, and left
- * unchanged for scalar types. Using an output or clone visitor with
- * an incomplete object has undefined behavior (other than a special
- * case for visit_type_str() treating NULL like ""), while the dealloc
- * visitor safely handles incomplete objects. Since input visitors
- * never produce an incomplete object, such an object is possible only
- * by manual construction.
+ * visitor, then *@obj will be set to NULL for pointer types, and left
+ * unchanged for scalar types.
+ *
+ * Using an output or clone visitor with an incomplete object has
+ * undefined behavior (other than a special case for visit_type_str()
+ * treating NULL like ""), while the dealloc visitor safely handles
+ * incomplete objects. Since input visitors never produce an
+ * incomplete object, such an object is possible only by manual
+ * construction.
*
* For the QAPI object types (structs, unions, and alternates), there
- * is an additional generated function in qapi-visit.h compatible
- * with:
+ * is an additional generated function in qapi-visit-MODULE.h
+ * compatible with:
*
* void visit_type_FOO_members(Visitor *v, FOO *obj, Error **errp);
*
* for visiting the members of a type without also allocating the QAPI
* struct.
*
- * Additionally, in qapi-types.h, all QAPI pointer types (structs,
- * unions, alternates, and lists) have a generated function compatible
+ * Additionally, QAPI pointer types (structs, unions, alternates, and
+ * lists) have a generated function in qapi-types-MODULE.h compatible
* with:
*
* void qapi_free_FOO(FOO *obj);
*
- * where behaves like free() in that @obj may be NULL. Such objects
- * may also be used with the following macro, provided alongside the
- * clone visitor:
+ * Does nothing when @obj is NULL.
+ *
+ * Such objects may also be used with macro
*
* Type *QAPI_CLONE(Type, src);
*
- * in order to perform a deep clone of @src. Because of the generated
- * qapi_free functions and the QAPI_CLONE() macro, the clone and
- * dealloc visitor should not be used directly outside of QAPI code.
+ * in order to perform a deep clone of @src.
*
- * QAPI types can also inherit from a base class; when this happens, a
- * function is generated for easily going from the derived type to the
- * base type:
+ * For QAPI types can that inherit from a base type, a function is
+ * generated for going from the derived type to the base type:
*
* BASE *qapi_CHILD_base(CHILD *obj);
*
- * For a real QAPI struct, typical input usage involves:
+ * Typical input visitor usage involves:
*
* <example>
* Foo *f;
@@ -153,36 +160,22 @@
* qapi_free_FooList(l);
* </example>
*
- * Similarly, typical output usage is:
+ * Typical output visitor usage:
*
* <example>
* Foo *f = ...obtain populated object...
- * Error *err = NULL;
* Visitor *v;
* Type *result;
*
* v = FOO_visitor_new(..., &result);
- * visit_type_Foo(v, NULL, &f, &err);
- * if (err) {
- * ...handle error...
- * } else {
- * visit_complete(v, &result);
- * ...use result...
- * }
+ * visit_type_Foo(v, NULL, &f, &error_abort);
+ * visit_complete(v, &result);
* visit_free(v);
+ * ...use result...
* </example>
*
- * When visiting a real QAPI struct, this file provides several
- * helpers that rely on in-tree information to control the walk:
- * visit_optional() for the 'has_member' field associated with
- * optional 'member' in the C struct; and visit_next_list() for
- * advancing through a FooList linked list. Similarly, the
- * visit_is_input() helper makes it possible to write code that is
- * visitor-agnostic everywhere except for cleanup. Only the generated
- * visit_type functions need to use these helpers.
- *
* It is also possible to use the visitors to do a virtual walk, where
- * no actual QAPI struct is present. In this situation, decisions
+ * no actual QAPI object is present. In this situation, decisions
* about what needs to be walked are made by the calling code, and
* structured visits are split between pairs of start and end methods
* (where the end method must be called if the start function
@@ -215,6 +208,9 @@
* goto outlist;
* }
* outlist:
+ * if (!err) {
+ * visit_check_list(v, &err);
+ * }
* visit_end_list(v, NULL);
* if (!err) {
* visit_check_struct(v, &err);
@@ -222,9 +218,14 @@
* outobj:
* visit_end_struct(v, NULL);
* out:
- * error_propagate(errp, err);
* visit_free(v);
* </example>
+ *
+ * This file provides helpers for use by the generated
+ * visit_type_FOO(): visit_optional() for the 'has_member' field
+ * associated with optional 'member' in the C struct,
+ * visit_next_list() for advancing through a FooList linked list, and
+ * visit_is_input() for cleaning up on failure.
*/
/*** Useful types ***/
@@ -282,9 +283,8 @@ void visit_free(Visitor *v);
* into *@obj. @obj may also be NULL for a virtual walk, in which
* case @size is ignored.
*
- * @errp obeys typical error usage, and reports failures such as a
- * member @name is not present, or present but not an object. On
- * error, input visitors set *@obj to NULL.
+ * On failure, set *@obj to NULL and store an error through @errp.
+ * Can happen only when @v is an input visitor.
*
* After visit_start_struct() succeeds, the caller may visit its
* members one after the other, passing the member's name and address
@@ -301,8 +301,8 @@ void visit_start_struct(Visitor *v, const char *name, void **obj,
/*
* Prepare for completing an object visit.
*
- * @errp obeys typical error usage, and reports failures such as
- * unparsed keys remaining in the input stream.
+ * On failure, store an error through @errp. Can happen only when @v
+ * is an input visitor.
*
* Should be called prior to visit_end_struct() if all other
* intermediate visit steps were successful, to allow the visitor one
@@ -338,14 +338,13 @@ void visit_end_struct(Visitor *v, void **obj);
* allow @list to be NULL for a virtual walk, in which case @size is
* ignored.
*
- * @errp obeys typical error usage, and reports failures such as a
- * member @name is not present, or present but not a list. On error,
- * input visitors set *@list to NULL.
+ * On failure, set *@list to NULL and store an error through @errp.
+ * Can happen only when @v is an input visitor.
*
* After visit_start_list() succeeds, the caller may visit its members
- * one after the other. A real visit (where @obj is non-NULL) uses
+ * one after the other. A real visit (where @list is non-NULL) uses
* visit_next_list() for traversing the linked list, while a virtual
- * visit (where @obj is NULL) uses other means. For each list
+ * visit (where @list is NULL) uses other means. For each list
* element, call the appropriate visit_type_FOO() with name set to
* NULL and obj set to the address of the value member of the list
* element. Finally, visit_end_list() needs to be called with the
@@ -374,8 +373,8 @@ GenericList *visit_next_list(Visitor *v, GenericList *tail, size_t size);
/*
* Prepare for completing a list visit.
*
- * @errp obeys typical error usage, and reports failures such as
- * unvisited list tail remaining in the input stream.
+ * On failure, store an error through @errp. Can happen only when @v
+ * is an input visitor.
*
* Should be called prior to visit_end_list() if all other
* intermediate visit steps were successful, to allow the visitor one
@@ -407,8 +406,11 @@ void visit_end_list(Visitor *v, void **list);
*
* @obj must not be NULL. Input and clone visitors use @size to
* determine how much memory to allocate into *@obj, then determine
- * the qtype of the next thing to be visited, stored in (*@obj)->type.
- * Other visitors will leave @obj unchanged.
+ * the qtype of the next thing to be visited, and store it in
+ * (*@obj)->type. Other visitors leave @obj unchanged.
+ *
+ * On failure, set *@obj to NULL and store an error through @errp.
+ * Can happen only when @v is an input visitor.
*
* If successful, this must be paired with visit_end_alternate() with
* the same @obj to clean up, even if visiting the contents of the
@@ -461,12 +463,15 @@ bool visit_optional(Visitor *v, const char *name, bool *present);
*
* Currently, all input visitors parse text input, and all output
* visitors produce text output. The mapping between enumeration
- * values and strings is done by the visitor core, using @strings; it
- * should be the ENUM_lookup array from visit-types.h.
+ * values and strings is done by the visitor core, using @lookup.
+ *
+ * On failure, store an error through @errp. Can happen only when @v
+ * is an input visitor.
*
* May call visit_type_str() under the hood, and the enum visit may
* fail even if the corresponding string visit succeeded; this implies
- * that visit_type_str() must have no unwelcome side effects.
+ * that an input visitor's visit_type_str() must have no unwelcome
+ * side effects.
*/
void visit_type_enum(Visitor *v, const char *name, int *obj,
const QEnumLookup *lookup, Error **errp);
@@ -476,6 +481,11 @@ void visit_type_enum(Visitor *v, const char *name, int *obj,
*/
bool visit_is_input(Visitor *v);
+/*
+ * Check if visitor is a dealloc visitor.
+ */
+bool visit_is_dealloc(Visitor *v);
+
/*** Visiting built-in types ***/
/*
@@ -486,6 +496,9 @@ bool visit_is_input(Visitor *v);
*
* @obj must be non-NULL. Input visitors set *@obj to the value;
* other visitors will leave *@obj unchanged.
+ *
+ * On failure, store an error through @errp. Can happen only when @v
+ * is an input visitor.
*/
void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp);
@@ -562,6 +575,9 @@ void visit_type_size(Visitor *v, const char *name, uint64_t *obj,
*
* @obj must be non-NULL. Input visitors set *@obj to the value;
* other visitors will leave *@obj unchanged.
+ *
+ * On failure, store an error through @errp. Can happen only when @v
+ * is an input visitor.
*/
void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
@@ -579,6 +595,9 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp);
* It is safe to cast away const when preparing a (const char *) value
* into @obj for use by an output visitor.
*
+ * On failure, set *@obj to NULL and store an error through @errp.
+ * Can happen only when @v is an input visitor.
+ *
* FIXME: Callers that try to output NULL *obj should not be allowed.
*/
void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
@@ -592,6 +611,9 @@ void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp);
* @obj must be non-NULL. Input visitors set *@obj to the value;
* other visitors will leave *@obj unchanged. Visitors should
* document if infinity or NaN are not permitted.
+ *
+ * On failure, store an error through @errp. Can happen only when @v
+ * is an input visitor.
*/
void visit_type_number(Visitor *v, const char *name, double *obj,
Error **errp);
@@ -606,6 +628,9 @@ void visit_type_number(Visitor *v, const char *name, double *obj,
* other visitors will leave *@obj unchanged. *@obj must be non-NULL
* for output visitors.
*
+ * On failure, set *@obj to NULL and store an error through @errp.
+ * Can happen only when @v is an input visitor.
+ *
* Note that some kinds of input can't express arbitrary QObject.
* E.g. the visitor returned by qobject_input_visitor_new_keyval()
* can't create numbers or booleans, only strings.
@@ -620,6 +645,9 @@ void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp);
*
* @obj must be non-NULL. Input visitors set *@obj to the value;
* other visitors ignore *@obj.
+ *
+ * On failure, set *@obj to NULL and store an error through @errp.
+ * Can happen only when @v is an input visitor.
*/
void visit_type_null(Visitor *v, const char *name, QNull **obj,
Error **errp);
diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h
index 8adf3b018b..1200890c86 100644
--- a/include/standard-headers/linux/ethtool.h
+++ b/include/standard-headers/linux/ethtool.h
@@ -596,6 +596,9 @@ struct ethtool_pauseparam {
* @ETH_SS_LINK_MODES: link mode names
* @ETH_SS_MSG_CLASSES: debug message class names
* @ETH_SS_WOL_MODES: wake-on-lan modes
+ * @ETH_SS_SOF_TIMESTAMPING: SOF_TIMESTAMPING_* flags
+ * @ETH_SS_TS_TX_TYPES: timestamping Tx types
+ * @ETH_SS_TS_RX_FILTERS: timestamping Rx filters
*/
enum ethtool_stringset {
ETH_SS_TEST = 0,
@@ -610,6 +613,9 @@ enum ethtool_stringset {
ETH_SS_LINK_MODES,
ETH_SS_MSG_CLASSES,
ETH_SS_WOL_MODES,
+ ETH_SS_SOF_TIMESTAMPING,
+ ETH_SS_TS_TX_TYPES,
+ ETH_SS_TS_RX_FILTERS,
/* add new constants above here */
ETH_SS_COUNT
@@ -1330,6 +1336,7 @@ enum ethtool_fec_config_bits {
ETHTOOL_FEC_OFF_BIT,
ETHTOOL_FEC_RS_BIT,
ETHTOOL_FEC_BASER_BIT,
+ ETHTOOL_FEC_LLRS_BIT,
};
#define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT)
@@ -1337,6 +1344,7 @@ enum ethtool_fec_config_bits {
#define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT)
#define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT)
#define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT)
+#define ETHTOOL_FEC_LLRS (1 << ETHTOOL_FEC_LLRS_BIT)
/* CMDs currently supported */
#define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings.
@@ -1521,7 +1529,7 @@ enum ethtool_link_mode_bit_indices {
ETHTOOL_LINK_MODE_400000baseLR8_ER8_FR8_Full_BIT = 71,
ETHTOOL_LINK_MODE_400000baseDR8_Full_BIT = 72,
ETHTOOL_LINK_MODE_400000baseCR8_Full_BIT = 73,
-
+ ETHTOOL_LINK_MODE_FEC_LLRS_BIT = 74,
/* must be last entry */
__ETHTOOL_LINK_MODE_MASK_NBITS
};
diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h
index b484c25289..ebf72c1031 100644
--- a/include/standard-headers/linux/input-event-codes.h
+++ b/include/standard-headers/linux/input-event-codes.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
/*
* Input event codes
*
@@ -652,6 +652,9 @@
/* Electronic privacy screen control */
#define KEY_PRIVACY_SCREEN_TOGGLE 0x279
+/* Select an area of screen to be copied */
+#define KEY_SELECTIVE_SCREENSHOT 0x27a
+
/*
* Some keyboards have keys which do not have a defined meaning, these keys
* are intended to be programmed / bound to macros by the user. For most
diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h
index 5437690483..f9701410d3 100644
--- a/include/standard-headers/linux/pci_regs.h
+++ b/include/standard-headers/linux/pci_regs.h
@@ -605,6 +605,7 @@
#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */
#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
+#define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */
#define PCI_EXP_SLTSTA 26 /* Slot Status */
#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */
@@ -680,6 +681,7 @@
#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */
#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
+#define PCI_EXP_SLTCAP2_IBPD 0x00000001 /* In-band PD Disable Supported */
#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */
diff --git a/include/standard-headers/linux/vhost_types.h b/include/standard-headers/linux/vhost_types.h
index 5351fe172d..a678d8fbaa 100644
--- a/include/standard-headers/linux/vhost_types.h
+++ b/include/standard-headers/linux/vhost_types.h
@@ -119,6 +119,14 @@ struct vhost_scsi_target {
unsigned short reserved;
};
+/* VHOST_VDPA specific definitions */
+
+struct vhost_vdpa_config {
+ uint32_t off;
+ uint32_t len;
+ uint8_t buf[0];
+};
+
/* Feature bits */
/* Log all write descriptors. Can be changed while device is active. */
#define VHOST_F_LOG_ALL 26
diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h
index 9375ca2a70..f343bfefd8 100644
--- a/include/standard-headers/linux/virtio_balloon.h
+++ b/include/standard-headers/linux/virtio_balloon.h
@@ -36,6 +36,7 @@
#define VIRTIO_BALLOON_F_DEFLATE_ON_OOM 2 /* Deflate balloon on OOM */
#define VIRTIO_BALLOON_F_FREE_PAGE_HINT 3 /* VQ to report free pages */
#define VIRTIO_BALLOON_F_PAGE_POISON 4 /* Guest is using page poisoning */
+#define VIRTIO_BALLOON_F_REPORTING 5 /* Page reporting virtqueue */
/* Size of a PFN in the balloon interface. */
#define VIRTIO_BALLOON_PFN_SHIFT 12
@@ -47,8 +48,15 @@ struct virtio_balloon_config {
uint32_t num_pages;
/* Number of pages we've actually got in balloon. */
uint32_t actual;
- /* Free page report command id, readonly by guest */
- uint32_t free_page_report_cmd_id;
+ /*
+ * Free page hint command id, readonly by guest.
+ * Was previously named free_page_report_cmd_id so we
+ * need to carry that name for legacy support.
+ */
+ union {
+ uint32_t free_page_hint_cmd_id;
+ uint32_t free_page_report_cmd_id; /* deprecated */
+ };
/* Stores PAGE_POISON if page poisoning is in use */
uint32_t poison_val;
};
diff --git a/include/standard-headers/linux/virtio_ids.h b/include/standard-headers/linux/virtio_ids.h
index 585e07b273..ecc27a1740 100644
--- a/include/standard-headers/linux/virtio_ids.h
+++ b/include/standard-headers/linux/virtio_ids.h
@@ -46,5 +46,6 @@
#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
#define VIRTIO_ID_FS 26 /* virtio filesystem */
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
+#define VIRTIO_ID_MAC80211_HWSIM 29 /* virtio mac80211-hwsim */
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/standard-headers/linux/virtio_net.h b/include/standard-headers/linux/virtio_net.h
index 260c3681d7..a90f79e1b1 100644
--- a/include/standard-headers/linux/virtio_net.h
+++ b/include/standard-headers/linux/virtio_net.h
@@ -57,6 +57,9 @@
* Steering */
#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */
+#define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */
+#define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */
+#define VIRTIO_NET_F_RSC_EXT 61 /* extended coalescing info */
#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device
* with the same MAC.
*/
@@ -69,6 +72,17 @@
#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */
#define VIRTIO_NET_S_ANNOUNCE 2 /* Announcement is needed */
+/* supported/enabled hash types */
+#define VIRTIO_NET_RSS_HASH_TYPE_IPv4 (1 << 0)
+#define VIRTIO_NET_RSS_HASH_TYPE_TCPv4 (1 << 1)
+#define VIRTIO_NET_RSS_HASH_TYPE_UDPv4 (1 << 2)
+#define VIRTIO_NET_RSS_HASH_TYPE_IPv6 (1 << 3)
+#define VIRTIO_NET_RSS_HASH_TYPE_TCPv6 (1 << 4)
+#define VIRTIO_NET_RSS_HASH_TYPE_UDPv6 (1 << 5)
+#define VIRTIO_NET_RSS_HASH_TYPE_IP_EX (1 << 6)
+#define VIRTIO_NET_RSS_HASH_TYPE_TCP_EX (1 << 7)
+#define VIRTIO_NET_RSS_HASH_TYPE_UDP_EX (1 << 8)
+
struct virtio_net_config {
/* The config defining mac address (if VIRTIO_NET_F_MAC) */
uint8_t mac[ETH_ALEN];
@@ -92,6 +106,12 @@ struct virtio_net_config {
* Any other value stands for unknown.
*/
uint8_t duplex;
+ /* maximum size of RSS key */
+ uint8_t rss_max_key_size;
+ /* maximum number of indirection table entries */
+ uint16_t rss_max_indirection_table_length;
+ /* bitmask of supported VIRTIO_NET_RSS_HASH_ types */
+ uint32_t supported_hash_types;
} QEMU_PACKED;
/*
@@ -104,6 +124,7 @@ struct virtio_net_config {
struct virtio_net_hdr_v1 {
#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */
#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */
+#define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc info in csum_ fields */
uint8_t flags;
#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */
#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */
@@ -113,11 +134,46 @@ struct virtio_net_hdr_v1 {
uint8_t gso_type;
__virtio16 hdr_len; /* Ethernet + IP + tcp/udp hdrs */
__virtio16 gso_size; /* Bytes to append to hdr_len per frame */
- __virtio16 csum_start; /* Position to start checksumming from */
- __virtio16 csum_offset; /* Offset after that to place checksum */
+ union {
+ struct {
+ __virtio16 csum_start;
+ __virtio16 csum_offset;
+ };
+ /* Checksum calculation */
+ struct {
+ /* Position to start checksumming from */
+ __virtio16 start;
+ /* Offset after that to place checksum */
+ __virtio16 offset;
+ } csum;
+ /* Receive Segment Coalescing */
+ struct {
+ /* Number of coalesced segments */
+ uint16_t segments;
+ /* Number of duplicated acks */
+ uint16_t dup_acks;
+ } rsc;
+ };
__virtio16 num_buffers; /* Number of merged rx buffers */
};
+struct virtio_net_hdr_v1_hash {
+ struct virtio_net_hdr_v1 hdr;
+ uint32_t hash_value;
+#define VIRTIO_NET_HASH_REPORT_NONE 0
+#define VIRTIO_NET_HASH_REPORT_IPv4 1
+#define VIRTIO_NET_HASH_REPORT_TCPv4 2
+#define VIRTIO_NET_HASH_REPORT_UDPv4 3
+#define VIRTIO_NET_HASH_REPORT_IPv6 4
+#define VIRTIO_NET_HASH_REPORT_TCPv6 5
+#define VIRTIO_NET_HASH_REPORT_UDPv6 6
+#define VIRTIO_NET_HASH_REPORT_IPv6_EX 7
+#define VIRTIO_NET_HASH_REPORT_TCPv6_EX 8
+#define VIRTIO_NET_HASH_REPORT_UDPv6_EX 9
+ uint16_t hash_report;
+ uint16_t padding;
+};
+
#ifndef VIRTIO_NET_NO_LEGACY
/* This header comes first in the scatter-gather list.
* For legacy virtio, if VIRTIO_F_ANY_LAYOUT is not negotiated, it must
@@ -228,7 +284,9 @@ struct virtio_net_ctrl_mac {
/*
* Control Receive Flow Steering
- *
+ */
+#define VIRTIO_NET_CTRL_MQ 4
+/*
* The command VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET
* enables Receive Flow Steering, specifying the number of the transmit and
* receive queues that will be used. After the command is consumed and acked by
@@ -241,12 +299,48 @@ struct virtio_net_ctrl_mq {
__virtio16 virtqueue_pairs;
};
-#define VIRTIO_NET_CTRL_MQ 4
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET 0
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN 1
#define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX 0x8000
/*
+ * The command VIRTIO_NET_CTRL_MQ_RSS_CONFIG has the same effect as
+ * VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET does and additionally configures
+ * the receive steering to use a hash calculated for incoming packet
+ * to decide on receive virtqueue to place the packet. The command
+ * also provides parameters to calculate a hash and receive virtqueue.
+ */
+struct virtio_net_rss_config {
+ uint32_t hash_types;
+ uint16_t indirection_table_mask;
+ uint16_t unclassified_queue;
+ uint16_t indirection_table[1/* + indirection_table_mask */];
+ uint16_t max_tx_vq;
+ uint8_t hash_key_length;
+ uint8_t hash_key_data[/* hash_key_length */];
+};
+
+ #define VIRTIO_NET_CTRL_MQ_RSS_CONFIG 1
+
+/*
+ * The command VIRTIO_NET_CTRL_MQ_HASH_CONFIG requests the device
+ * to include in the virtio header of the packet the value of the
+ * calculated hash and the report type of hash. It also provides
+ * parameters for hash calculation. The command requires feature
+ * VIRTIO_NET_F_HASH_REPORT to be negotiated to extend the
+ * layout of virtio header as defined in virtio_net_hdr_v1_hash.
+ */
+struct virtio_net_hash_config {
+ uint32_t hash_types;
+ /* for compatibility with virtio_net_rss_config */
+ uint16_t reserved[4];
+ uint8_t hash_key_length;
+ uint8_t hash_key_data[/* hash_key_length */];
+};
+
+ #define VIRTIO_NET_CTRL_MQ_HASH_CONFIG 2
+
+/*
* Control network offloads
*
* Reconfigures the network offloads that Guest can handle.
diff --git a/include/sysemu/device_tree.h b/include/sysemu/device_tree.h
index c16fd69bc0..982c89345f 100644
--- a/include/sysemu/device_tree.h
+++ b/include/sysemu/device_tree.h
@@ -39,8 +39,11 @@ void *load_device_tree_from_sysfs(void);
* NULL. If there is no error but no matching node was found, the
* returned array contains a single element equal to NULL. If an error
* was encountered when parsing the blob, the function returns NULL
+ *
+ * @name may be NULL to wildcard names and only match compatibility
+ * strings.
*/
-char **qemu_fdt_node_path(void *fdt, const char *name, char *compat,
+char **qemu_fdt_node_path(void *fdt, const char *name, const char *compat,
Error **errp);
/**
diff --git a/linux-headers/COPYING b/linux-headers/COPYING
index da4cb28feb..a635a38ef9 100644
--- a/linux-headers/COPYING
+++ b/linux-headers/COPYING
@@ -16,3 +16,5 @@ In addition, other licenses may also apply. Please see:
Documentation/process/license-rules.rst
for more details.
+
+All contributions to the Linux Kernel are subject to this COPYING file.
diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h
index 503d3f42da..3f3f780c8c 100644
--- a/linux-headers/asm-x86/kvm.h
+++ b/linux-headers/asm-x86/kvm.h
@@ -390,6 +390,7 @@ struct kvm_sync_regs {
#define KVM_STATE_NESTED_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_RUN_PENDING 0x00000002
#define KVM_STATE_NESTED_EVMCS 0x00000004
+#define KVM_STATE_NESTED_MTF_PENDING 0x00000008
#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001
#define KVM_STATE_NESTED_SMM_VMXON 0x00000002
diff --git a/linux-headers/asm-x86/unistd_32.h b/linux-headers/asm-x86/unistd_32.h
index f6e06fcfbd..1e6c1a5867 100644
--- a/linux-headers/asm-x86/unistd_32.h
+++ b/linux-headers/asm-x86/unistd_32.h
@@ -429,4 +429,5 @@
#define __NR_openat2 437
#define __NR_pidfd_getfd 438
+
#endif /* _ASM_X86_UNISTD_32_H */
diff --git a/linux-headers/asm-x86/unistd_64.h b/linux-headers/asm-x86/unistd_64.h
index 924f826d2d..6daf0aecb2 100644
--- a/linux-headers/asm-x86/unistd_64.h
+++ b/linux-headers/asm-x86/unistd_64.h
@@ -351,4 +351,5 @@
#define __NR_openat2 437
#define __NR_pidfd_getfd 438
+
#endif /* _ASM_X86_UNISTD_64_H */
diff --git a/linux-headers/asm-x86/unistd_x32.h b/linux-headers/asm-x86/unistd_x32.h
index 010307757b..e3f17ef370 100644
--- a/linux-headers/asm-x86/unistd_x32.h
+++ b/linux-headers/asm-x86/unistd_x32.h
@@ -340,4 +340,5 @@
#define __NR_preadv2 (__X32_SYSCALL_BIT + 546)
#define __NR_pwritev2 (__X32_SYSCALL_BIT + 547)
+
#endif /* _ASM_X86_UNISTD_X32_H */
diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h
index 265099100e..9804495a46 100644
--- a/linux-headers/linux/kvm.h
+++ b/linux-headers/linux/kvm.h
@@ -474,12 +474,17 @@ struct kvm_s390_mem_op {
__u32 size; /* amount of bytes */
__u32 op; /* type of operation */
__u64 buf; /* buffer in userspace */
- __u8 ar; /* the access register number */
- __u8 reserved[31]; /* should be set to 0 */
+ union {
+ __u8 ar; /* the access register number */
+ __u32 sida_offset; /* offset into the sida */
+ __u8 reserved[32]; /* should be set to 0 */
+ };
};
/* types for kvm_s390_mem_op->op */
#define KVM_S390_MEMOP_LOGICAL_READ 0
#define KVM_S390_MEMOP_LOGICAL_WRITE 1
+#define KVM_S390_MEMOP_SIDA_READ 2
+#define KVM_S390_MEMOP_SIDA_WRITE 3
/* flags for kvm_s390_mem_op->flags */
#define KVM_S390_MEMOP_F_CHECK_ONLY (1ULL << 0)
#define KVM_S390_MEMOP_F_INJECT_EXCEPTION (1ULL << 1)
@@ -1010,6 +1015,8 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_ARM_NISV_TO_USER 177
#define KVM_CAP_ARM_INJECT_EXT_DABT 178
#define KVM_CAP_S390_VCPU_RESETS 179
+#define KVM_CAP_S390_PROTECTED 180
+#define KVM_CAP_PPC_SECURE_GUEST 181
#ifdef KVM_CAP_IRQ_ROUTING
@@ -1478,6 +1485,39 @@ struct kvm_enc_region {
#define KVM_S390_NORMAL_RESET _IO(KVMIO, 0xc3)
#define KVM_S390_CLEAR_RESET _IO(KVMIO, 0xc4)
+struct kvm_s390_pv_sec_parm {
+ __u64 origin;
+ __u64 length;
+};
+
+struct kvm_s390_pv_unp {
+ __u64 addr;
+ __u64 size;
+ __u64 tweak;
+};
+
+enum pv_cmd_id {
+ KVM_PV_ENABLE,
+ KVM_PV_DISABLE,
+ KVM_PV_SET_SEC_PARMS,
+ KVM_PV_UNPACK,
+ KVM_PV_VERIFY,
+ KVM_PV_PREP_RESET,
+ KVM_PV_UNSHARE_ALL,
+};
+
+struct kvm_pv_cmd {
+ __u32 cmd; /* Command to be executed */
+ __u16 rc; /* Ultravisor return code */
+ __u16 rrc; /* Ultravisor return reason code */
+ __u64 data; /* Data or address */
+ __u32 flags; /* flags for future extensions. Must be 0 for now */
+ __u32 reserved[3];
+};
+
+/* Available with KVM_CAP_S390_PROTECTED */
+#define KVM_S390_PV_COMMAND _IOWR(KVMIO, 0xc5, struct kvm_pv_cmd)
+
/* Secure Encrypted Virtualization command */
enum sev_cmd_id {
/* Guest initialization commands */
@@ -1628,4 +1668,7 @@ struct kvm_hyperv_eventfd {
#define KVM_HYPERV_CONN_ID_MASK 0x00ffffff
#define KVM_HYPERV_EVENTFD_DEASSIGN (1 << 0)
+#define KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE (1 << 0)
+#define KVM_DIRTY_LOG_INITIALLY_SET (1 << 1)
+
#endif /* __LINUX_KVM_H */
diff --git a/linux-headers/linux/mman.h b/linux-headers/linux/mman.h
index 1f6e2cd89c..51ea363759 100644
--- a/linux-headers/linux/mman.h
+++ b/linux-headers/linux/mman.h
@@ -5,8 +5,9 @@
#include <asm/mman.h>
#include <asm-generic/hugetlb_encode.h>
-#define MREMAP_MAYMOVE 1
-#define MREMAP_FIXED 2
+#define MREMAP_MAYMOVE 1
+#define MREMAP_FIXED 2
+#define MREMAP_DONTUNMAP 4
#define OVERCOMMIT_GUESS 0
#define OVERCOMMIT_ALWAYS 1
diff --git a/linux-headers/linux/userfaultfd.h b/linux-headers/linux/userfaultfd.h
index ce78878d12..8d3996eb82 100644
--- a/linux-headers/linux/userfaultfd.h
+++ b/linux-headers/linux/userfaultfd.h
@@ -19,7 +19,8 @@
* means the userland is reading).
*/
#define UFFD_API ((__u64)0xAA)
-#define UFFD_API_FEATURES (UFFD_FEATURE_EVENT_FORK | \
+#define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \
+ UFFD_FEATURE_EVENT_FORK | \
UFFD_FEATURE_EVENT_REMAP | \
UFFD_FEATURE_EVENT_REMOVE | \
UFFD_FEATURE_EVENT_UNMAP | \
@@ -34,7 +35,8 @@
#define UFFD_API_RANGE_IOCTLS \
((__u64)1 << _UFFDIO_WAKE | \
(__u64)1 << _UFFDIO_COPY | \
- (__u64)1 << _UFFDIO_ZEROPAGE)
+ (__u64)1 << _UFFDIO_ZEROPAGE | \
+ (__u64)1 << _UFFDIO_WRITEPROTECT)
#define UFFD_API_RANGE_IOCTLS_BASIC \
((__u64)1 << _UFFDIO_WAKE | \
(__u64)1 << _UFFDIO_COPY)
@@ -52,6 +54,7 @@
#define _UFFDIO_WAKE (0x02)
#define _UFFDIO_COPY (0x03)
#define _UFFDIO_ZEROPAGE (0x04)
+#define _UFFDIO_WRITEPROTECT (0x06)
#define _UFFDIO_API (0x3F)
/* userfaultfd ioctl ids */
@@ -68,6 +71,8 @@
struct uffdio_copy)
#define UFFDIO_ZEROPAGE _IOWR(UFFDIO, _UFFDIO_ZEROPAGE, \
struct uffdio_zeropage)
+#define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, \
+ struct uffdio_writeprotect)
/* read() structure */
struct uffd_msg {
@@ -203,13 +208,14 @@ struct uffdio_copy {
__u64 dst;
__u64 src;
__u64 len;
+#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0)
/*
- * There will be a wrprotection flag later that allows to map
- * pages wrprotected on the fly. And such a flag will be
- * available if the wrprotection ioctl are implemented for the
- * range according to the uffdio_register.ioctls.
+ * UFFDIO_COPY_MODE_WP will map the page write protected on
+ * the fly. UFFDIO_COPY_MODE_WP is available only if the
+ * write protected ioctl is implemented for the range
+ * according to the uffdio_register.ioctls.
*/
-#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0)
+#define UFFDIO_COPY_MODE_WP ((__u64)1<<1)
__u64 mode;
/*
@@ -231,4 +237,24 @@ struct uffdio_zeropage {
__s64 zeropage;
};
+struct uffdio_writeprotect {
+ struct uffdio_range range;
+/*
+ * UFFDIO_WRITEPROTECT_MODE_WP: set the flag to write protect a range,
+ * unset the flag to undo protection of a range which was previously
+ * write protected.
+ *
+ * UFFDIO_WRITEPROTECT_MODE_DONTWAKE: set the flag to avoid waking up
+ * any wait thread after the operation succeeds.
+ *
+ * NOTE: Write protecting a region (WP=1) is unrelated to page faults,
+ * therefore DONTWAKE flag is meaningless with WP=1. Removing write
+ * protection (WP=0) in response to a page fault wakes the faulting
+ * task unless DONTWAKE is set.
+ */
+#define UFFDIO_WRITEPROTECT_MODE_WP ((__u64)1<<0)
+#define UFFDIO_WRITEPROTECT_MODE_DONTWAKE ((__u64)1<<1)
+ __u64 mode;
+};
+
#endif /* _LINUX_USERFAULTFD_H */
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h
index fb10370d29..a41c452865 100644
--- a/linux-headers/linux/vfio.h
+++ b/linux-headers/linux/vfio.h
@@ -707,6 +707,43 @@ struct vfio_device_ioeventfd {
#define VFIO_DEVICE_IOEVENTFD _IO(VFIO_TYPE, VFIO_BASE + 16)
+/**
+ * VFIO_DEVICE_FEATURE - _IORW(VFIO_TYPE, VFIO_BASE + 17,
+ * struct vfio_device_feature)
+ *
+ * Get, set, or probe feature data of the device. The feature is selected
+ * using the FEATURE_MASK portion of the flags field. Support for a feature
+ * can be probed by setting both the FEATURE_MASK and PROBE bits. A probe
+ * may optionally include the GET and/or SET bits to determine read vs write
+ * access of the feature respectively. Probing a feature will return success
+ * if the feature is supported and all of the optionally indicated GET/SET
+ * methods are supported. The format of the data portion of the structure is
+ * specific to the given feature. The data portion is not required for
+ * probing. GET and SET are mutually exclusive, except for use with PROBE.
+ *
+ * Return 0 on success, -errno on failure.
+ */
+struct vfio_device_feature {
+ __u32 argsz;
+ __u32 flags;
+#define VFIO_DEVICE_FEATURE_MASK (0xffff) /* 16-bit feature index */
+#define VFIO_DEVICE_FEATURE_GET (1 << 16) /* Get feature into data[] */
+#define VFIO_DEVICE_FEATURE_SET (1 << 17) /* Set feature from data[] */
+#define VFIO_DEVICE_FEATURE_PROBE (1 << 18) /* Probe feature support */
+ __u8 data[];
+};
+
+#define VFIO_DEVICE_FEATURE _IO(VFIO_TYPE, VFIO_BASE + 17)
+
+/*
+ * Provide support for setting a PCI VF Token, which is used as a shared
+ * secret between PF and VF drivers. This feature may only be set on a
+ * PCI SR-IOV PF when SR-IOV is enabled on the PF and there are no existing
+ * open VFs. Data provided when setting this feature is a 16-byte array
+ * (__u8 b[16]), representing a UUID.
+ */
+#define VFIO_DEVICE_FEATURE_PCI_VF_TOKEN (0)
+
/* -------- API for Type1 VFIO IOMMU -------- */
/**
diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h
index 40d028eed6..9fe72e4b13 100644
--- a/linux-headers/linux/vhost.h
+++ b/linux-headers/linux/vhost.h
@@ -116,4 +116,28 @@
#define VHOST_VSOCK_SET_GUEST_CID _IOW(VHOST_VIRTIO, 0x60, __u64)
#define VHOST_VSOCK_SET_RUNNING _IOW(VHOST_VIRTIO, 0x61, int)
+/* VHOST_VDPA specific defines */
+
+/* Get the device id. The device ids follow the same definition of
+ * the device id defined in virtio-spec.
+ */
+#define VHOST_VDPA_GET_DEVICE_ID _IOR(VHOST_VIRTIO, 0x70, __u32)
+/* Get and set the status. The status bits follow the same definition
+ * of the device status defined in virtio-spec.
+ */
+#define VHOST_VDPA_GET_STATUS _IOR(VHOST_VIRTIO, 0x71, __u8)
+#define VHOST_VDPA_SET_STATUS _IOW(VHOST_VIRTIO, 0x72, __u8)
+/* Get and set the device config. The device config follows the same
+ * definition of the device config defined in virtio-spec.
+ */
+#define VHOST_VDPA_GET_CONFIG _IOR(VHOST_VIRTIO, 0x73, \
+ struct vhost_vdpa_config)
+#define VHOST_VDPA_SET_CONFIG _IOW(VHOST_VIRTIO, 0x74, \
+ struct vhost_vdpa_config)
+/* Enable/disable the ring. */
+#define VHOST_VDPA_SET_VRING_ENABLE _IOW(VHOST_VIRTIO, 0x75, \
+ struct vhost_vring_state)
+/* Get the max ring size. */
+#define VHOST_VDPA_GET_VRING_NUM _IOR(VHOST_VIRTIO, 0x76, __u16)
+
#endif
diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c
index 83ecc6f799..67a95dbc7b 100644
--- a/linux-user/riscv/signal.c
+++ b/linux-user/riscv/signal.c
@@ -40,8 +40,9 @@ struct target_ucontext {
unsigned long uc_flags;
struct target_ucontext *uc_link;
target_stack_t uc_stack;
- struct target_sigcontext uc_mcontext;
target_sigset_t uc_sigmask;
+ uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)];
+ struct target_sigcontext uc_mcontext QEMU_ALIGNED(16);
};
struct target_rt_sigframe {
diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c
index 9b94e67879..7f6e982dc8 100644
--- a/monitor/hmp-cmds.c
+++ b/monitor/hmp-cmds.c
@@ -334,7 +334,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict)
Visitor *v;
char *str;
v = string_output_visitor_new(false, &str);
- visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, NULL);
+ visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime,
+ &error_abort);
visit_complete(v, &str);
monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str);
g_free(str);
diff --git a/monitor/qmp.c b/monitor/qmp.c
index f89e7daf27..d433ceae5b 100644
--- a/monitor/qmp.c
+++ b/monitor/qmp.c
@@ -322,9 +322,12 @@ static QDict *qmp_greeting(MonitorQMP *mon)
{
QList *cap_list = qlist_new();
QObject *ver = NULL;
+ QDict *args;
QMPCapability cap;
- qmp_marshal_query_version(NULL, &ver, NULL);
+ args = qdict_new();
+ qmp_marshal_query_version(args, &ver, NULL);
+ qobject_unref(args);
for (cap = 0; cap < QMP_CAPABILITY__MAX; cap++) {
if (mon->capab_offered[cap]) {
diff --git a/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
index bab13f597a..3e0da54080 100644
--- a/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
+++ b/pc-bios/opensbi-riscv32-sifive_u-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv32-virt-fw_jump.bin b/pc-bios/opensbi-riscv32-virt-fw_jump.bin
index c9654e7047..bc56ed61fd 100644
--- a/pc-bios/opensbi-riscv32-virt-fw_jump.bin
+++ b/pc-bios/opensbi-riscv32-virt-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
index 77f4dc8f83..1acee86dc7 100644
--- a/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
+++ b/pc-bios/opensbi-riscv64-sifive_u-fw_jump.bin
Binary files differ
diff --git a/pc-bios/opensbi-riscv64-virt-fw_jump.bin b/pc-bios/opensbi-riscv64-virt-fw_jump.bin
index 31e74d12ea..c62f2b449b 100644
--- a/pc-bios/opensbi-riscv64-virt-fw_jump.bin
+++ b/pc-bios/opensbi-riscv64-virt-fw_jump.bin
Binary files differ
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index d192724b13..2239fc6417 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -34,12 +34,6 @@ static void qapi_dealloc_end_struct(Visitor *v, void **obj)
}
}
-static void qapi_dealloc_start_alternate(Visitor *v, const char *name,
- GenericAlternate **obj, size_t size,
- Error **errp)
-{
-}
-
static void qapi_dealloc_end_alternate(Visitor *v, void **obj)
{
if (obj) {
@@ -123,7 +117,6 @@ Visitor *qapi_dealloc_visitor_new(void)
v->visitor.type = VISITOR_DEALLOC;
v->visitor.start_struct = qapi_dealloc_start_struct;
v->visitor.end_struct = qapi_dealloc_end_struct;
- v->visitor.start_alternate = qapi_dealloc_start_alternate;
v->visitor.end_alternate = qapi_dealloc_end_alternate;
v->visitor.start_list = qapi_dealloc_start_list;
v->visitor.next_list = qapi_dealloc_next_list;
diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 5365561b07..74aa9c04bd 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -142,6 +142,11 @@ bool visit_is_input(Visitor *v)
return v->type == VISITOR_INPUT;
}
+bool visit_is_dealloc(Visitor *v)
+{
+ return v->type == VISITOR_DEALLOC;
+}
+
void visit_type_int(Visitor *v, const char *name, int64_t *obj, Error **errp)
{
assert(obj);
@@ -155,10 +160,13 @@ static void visit_type_uintN(Visitor *v, uint64_t *obj, const char *name,
Error *err = NULL;
uint64_t value = *obj;
+ assert(v->type == VISITOR_INPUT || value <= max);
+
v->type_uint64(v, name, &value, &err);
if (err) {
error_propagate(errp, err);
} else if (value > max) {
+ assert(v->type == VISITOR_INPUT);
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
name ? name : "null", type);
} else {
@@ -214,10 +222,13 @@ static void visit_type_intN(Visitor *v, int64_t *obj, const char *name,
Error *err = NULL;
int64_t value = *obj;
+ assert(v->type == VISITOR_INPUT || (value >= min && value <= max));
+
v->type_int64(v, name, &value, &err);
if (err) {
error_propagate(errp, err);
} else if (value < min || value > max) {
+ assert(v->type == VISITOR_INPUT);
error_setg(errp, QERR_INVALID_PARAMETER_VALUE,
name ? name : "null", type);
} else {
@@ -336,15 +347,6 @@ static void output_type_enum(Visitor *v, const char *name, int *obj,
int value = *obj;
char *enum_str;
- /*
- * TODO why is this an error, not an assertion? If assertion:
- * delete, and rely on qapi_enum_lookup()
- */
- if (value < 0 || value >= lookup->size) {
- error_setg(errp, QERR_INVALID_PARAMETER, name ? name : "null");
- return;
- }
-
enum_str = (char *)qapi_enum_lookup(lookup, value);
visit_type_str(v, name, &enum_str, errp);
}
diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c
index 32236cbcb1..5ce3ec2e5f 100644
--- a/qapi/qobject-input-visitor.c
+++ b/qapi/qobject-input-visitor.c
@@ -203,31 +203,32 @@ static const char *qobject_input_get_keyval(QObjectInputVisitor *qiv,
return qstring_get_str(qstr);
}
-static void qdict_add_key(const char *key, QObject *obj, void *opaque)
-{
- GHashTable *h = opaque;
- g_hash_table_insert(h, (gpointer) key, NULL);
-}
-
static const QListEntry *qobject_input_push(QObjectInputVisitor *qiv,
const char *name,
QObject *obj, void *qapi)
{
GHashTable *h;
StackObject *tos = g_new0(StackObject, 1);
+ QDict *qdict = qobject_to(QDict, obj);
+ QList *qlist = qobject_to(QList, obj);
+ const QDictEntry *entry;
assert(obj);
tos->name = name;
tos->obj = obj;
tos->qapi = qapi;
- if (qobject_type(obj) == QTYPE_QDICT) {
+ if (qdict) {
h = g_hash_table_new(g_str_hash, g_str_equal);
- qdict_iter(qobject_to(QDict, obj), qdict_add_key, h);
+ for (entry = qdict_first(qdict);
+ entry;
+ entry = qdict_next(qdict, entry)) {
+ g_hash_table_insert(h, (void *)qdict_entry_key(entry), NULL);
+ }
tos->h = h;
} else {
- assert(qobject_type(obj) == QTYPE_QLIST);
- tos->entry = qlist_first(qobject_to(QList, obj));
+ assert(qlist);
+ tos->entry = qlist_first(qlist);
tos->index = -1;
}
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 9833b33549..56cee1483f 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -38,6 +38,7 @@
#include "migration/misc.h"
#include "migration/migration.h"
#include "qemu/cutils.h"
+#include "hw/clock.h"
/*
* Aliases were a bad idea from the start. Let's keep them
@@ -737,6 +738,7 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
ObjectClass *class;
BusState *child;
NamedGPIOList *ngl;
+ NamedClockList *ncl;
qdev_printf("dev: %s, id \"%s\"\n", object_get_typename(OBJECT(dev)),
dev->id ? dev->id : "");
@@ -751,6 +753,13 @@ static void qdev_print(Monitor *mon, DeviceState *dev, int indent)
ngl->num_out);
}
}
+ QLIST_FOREACH(ncl, &dev->clocks, node) {
+ qdev_printf("clock-%s%s \"%s\" freq_hz=%e\n",
+ ncl->output ? "out" : "in",
+ ncl->alias ? " (alias)" : "",
+ ncl->name,
+ CLOCK_PERIOD_TO_HZ(1.0 * clock_get(ncl->clock)));
+ }
class = object_get_class(OBJECT(dev));
do {
qdev_print_props(mon, dev, DEVICE_CLASS(class)->props_, indent);
diff --git a/qobject/qdict.c b/qobject/qdict.c
index 3d8c2f7bbc..526de54ceb 100644
--- a/qobject/qdict.c
+++ b/qobject/qdict.c
@@ -298,25 +298,6 @@ const char *qdict_get_try_str(const QDict *qdict, const char *key)
return qstr ? qstring_get_str(qstr) : NULL;
}
-/**
- * qdict_iter(): Iterate over all the dictionary's stored values.
- *
- * This function allows the user to provide an iterator, which will be
- * called for each stored value in the dictionary.
- */
-void qdict_iter(const QDict *qdict,
- void (*iter)(const char *key, QObject *obj, void *opaque),
- void *opaque)
-{
- int i;
- QDictEntry *entry;
-
- for (i = 0; i < QDICT_BUCKET_MAX; i++) {
- QLIST_FOREACH(entry, &qdict->table[i], next)
- iter(entry->key, entry->value, opaque);
- }
-}
-
static QDictEntry *qdict_next_entry(const QDict *qdict, int first_bucket)
{
int i;
diff --git a/qobject/qjson.c b/qobject/qjson.c
index db36101f3b..f1f2c69704 100644
--- a/qobject/qjson.c
+++ b/qobject/qjson.c
@@ -149,58 +149,18 @@ QDict *qdict_from_jsonf_nofail(const char *string, ...)
return qdict;
}
-typedef struct ToJsonIterState
-{
- int indent;
- int pretty;
- int count;
- QString *str;
-} ToJsonIterState;
-
static void to_json(const QObject *obj, QString *str, int pretty, int indent);
-static void to_json_dict_iter(const char *key, QObject *obj, void *opaque)
-{
- ToJsonIterState *s = opaque;
- QString *qkey;
- int j;
-
- if (s->count) {
- qstring_append(s->str, s->pretty ? "," : ", ");
- }
-
- if (s->pretty) {
- qstring_append(s->str, "\n");
- for (j = 0 ; j < s->indent ; j++)
- qstring_append(s->str, " ");
- }
-
- qkey = qstring_from_str(key);
- to_json(QOBJECT(qkey), s->str, s->pretty, s->indent);
- qobject_unref(qkey);
-
- qstring_append(s->str, ": ");
- to_json(obj, s->str, s->pretty, s->indent);
- s->count++;
-}
-
-static void to_json_list_iter(QObject *obj, void *opaque)
+static void json_pretty_newline(QString *str, bool pretty, int indent)
{
- ToJsonIterState *s = opaque;
- int j;
+ int i;
- if (s->count) {
- qstring_append(s->str, s->pretty ? "," : ", ");
- }
-
- if (s->pretty) {
- qstring_append(s->str, "\n");
- for (j = 0 ; j < s->indent ; j++)
- qstring_append(s->str, " ");
+ if (pretty) {
+ qstring_append(str, "\n");
+ for (i = 0; i < indent; i++) {
+ qstring_append(str, " ");
+ }
}
-
- to_json(obj, s->str, s->pretty, s->indent);
- s->count++;
}
static void to_json(const QObject *obj, QString *str, int pretty, int indent)
@@ -273,40 +233,49 @@ static void to_json(const QObject *obj, QString *str, int pretty, int indent)
break;
}
case QTYPE_QDICT: {
- ToJsonIterState s;
QDict *val = qobject_to(QDict, obj);
+ const char *comma = pretty ? "," : ", ";
+ const char *sep = "";
+ const QDictEntry *entry;
+ QString *qkey;
- s.count = 0;
- s.str = str;
- s.indent = indent + 1;
- s.pretty = pretty;
qstring_append(str, "{");
- qdict_iter(val, to_json_dict_iter, &s);
- if (pretty) {
- int j;
- qstring_append(str, "\n");
- for (j = 0 ; j < indent ; j++)
- qstring_append(str, " ");
+
+ for (entry = qdict_first(val);
+ entry;
+ entry = qdict_next(val, entry)) {
+ qstring_append(str, sep);
+ json_pretty_newline(str, pretty, indent + 1);
+
+ qkey = qstring_from_str(qdict_entry_key(entry));
+ to_json(QOBJECT(qkey), str, pretty, indent + 1);
+ qobject_unref(qkey);
+
+ qstring_append(str, ": ");
+ to_json(qdict_entry_value(entry), str, pretty, indent + 1);
+ sep = comma;
}
+
+ json_pretty_newline(str, pretty, indent);
qstring_append(str, "}");
break;
}
case QTYPE_QLIST: {
- ToJsonIterState s;
QList *val = qobject_to(QList, obj);
+ const char *comma = pretty ? "," : ", ";
+ const char *sep = "";
+ QListEntry *entry;
- s.count = 0;
- s.str = str;
- s.indent = indent + 1;
- s.pretty = pretty;
qstring_append(str, "[");
- qlist_iter(val, (void *)to_json_list_iter, &s);
- if (pretty) {
- int j;
- qstring_append(str, "\n");
- for (j = 0 ; j < indent ; j++)
- qstring_append(str, " ");
+
+ QLIST_FOREACH_ENTRY(val, entry) {
+ qstring_append(str, sep);
+ json_pretty_newline(str, pretty, indent + 1);
+ to_json(qlist_entry_obj(entry), str, pretty, indent + 1);
+ sep = comma;
}
+
+ json_pretty_newline(str, pretty, indent);
qstring_append(str, "]");
break;
}
diff --git a/qobject/qlist.c b/qobject/qlist.c
index b3274af88b..1be95367d1 100644
--- a/qobject/qlist.c
+++ b/qobject/qlist.c
@@ -34,20 +34,17 @@ QList *qlist_new(void)
return qlist;
}
-static void qlist_copy_elem(QObject *obj, void *opaque)
-{
- QList *dst = opaque;
-
- qobject_ref(obj);
- qlist_append_obj(dst, obj);
-}
-
QList *qlist_copy(QList *src)
{
QList *dst = qlist_new();
+ QListEntry *entry;
+ QObject *elt;
- qlist_iter(src, qlist_copy_elem, dst);
-
+ QLIST_FOREACH_ENTRY(src, entry) {
+ elt = qlist_entry_obj(entry);
+ qobject_ref(elt);
+ qlist_append_obj(dst, elt);
+ }
return dst;
}
@@ -86,21 +83,6 @@ void qlist_append_null(QList *qlist)
qlist_append(qlist, qnull());
}
-/**
- * qlist_iter(): Iterate over all the list's stored values.
- *
- * This function allows the user to provide an iterator, which will be
- * called for each stored value in the list.
- */
-void qlist_iter(const QList *qlist,
- void (*iter)(QObject *obj, void *opaque), void *opaque)
-{
- QListEntry *entry;
-
- QTAILQ_FOREACH(entry, &qlist->head, next)
- iter(entry->value, opaque);
-}
-
QObject *qlist_pop(QList *qlist)
{
QListEntry *entry;
@@ -137,16 +119,14 @@ int qlist_empty(const QList *qlist)
return QTAILQ_EMPTY(&qlist->head);
}
-static void qlist_size_iter(QObject *obj, void *opaque)
-{
- size_t *count = opaque;
- (*count)++;
-}
-
size_t qlist_size(const QList *qlist)
{
size_t count = 0;
- qlist_iter(qlist, qlist_size_iter, &count);
+ QListEntry *entry;
+
+ QLIST_FOREACH_ENTRY(qlist, entry) {
+ count++;
+ }
return count;
}
diff --git a/qom/object.c b/qom/object.c
index 1812f79224..be700e831f 100644
--- a/qom/object.c
+++ b/qom/object.c
@@ -1550,11 +1550,9 @@ int object_property_get_enum(Object *obj, const char *name,
}
visit_complete(v, &str);
visit_free(v);
- v = string_input_visitor_new(str);
- visit_type_enum(v, name, &ret, enumprop->lookup, errp);
+ ret = qapi_enum_parse(enumprop->lookup, str, -1, errp);
g_free(str);
- visit_free(v);
return ret;
}
diff --git a/roms/opensbi b/roms/opensbi
-Subproject ac5e821d50be631f26274765a59bc1b444ffd86
+Subproject 9f1b72ce66d659e91013b358939e832fb27223f
diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py
index bc30876c88..6809b0fb6e 100644
--- a/scripts/qapi/commands.py
+++ b/scripts/qapi/commands.py
@@ -104,6 +104,7 @@ def gen_marshal(name, arg_type, boxed, ret_type):
%(proto)s
{
Error *err = NULL;
+ Visitor *v;
''',
proto=build_marshal_proto(name))
@@ -114,44 +115,38 @@ def gen_marshal(name, arg_type, boxed, ret_type):
c_type=ret_type.c_type())
if have_args:
- visit_members = ('visit_type_%s_members(v, &arg, &err);'
- % arg_type.c_name())
ret += mcgen('''
- Visitor *v;
%(c_name)s arg = {0};
-
''',
c_name=arg_type.c_name())
- else:
- visit_members = ''
- ret += mcgen('''
- Visitor *v = NULL;
-
- if (args) {
-''')
- push_indent()
ret += mcgen('''
+
v = qobject_input_visitor_new(QOBJECT(args));
visit_start_struct(v, NULL, NULL, 0, &err);
if (err) {
goto out;
}
- %(visit_members)s
+''')
+
+ if have_args:
+ ret += mcgen('''
+ visit_type_%(c_arg_type)s_members(v, &arg, &err);
if (!err) {
visit_check_struct(v, &err);
}
+''',
+ c_arg_type=arg_type.c_name())
+ else:
+ ret += mcgen('''
+ visit_check_struct(v, &err);
+''')
+
+ ret += mcgen('''
visit_end_struct(v, NULL);
if (err) {
goto out;
}
-''',
- visit_members=visit_members)
-
- if not have_args:
- pop_indent()
- ret += mcgen('''
- }
''')
ret += gen_call(name, arg_type, boxed, ret_type)
@@ -163,29 +158,20 @@ out:
visit_free(v);
''')
+ ret += mcgen('''
+ v = qapi_dealloc_visitor_new();
+ visit_start_struct(v, NULL, NULL, 0, NULL);
+''')
+
if have_args:
- visit_members = ('visit_type_%s_members(v, &arg, NULL);'
- % arg_type.c_name())
- else:
- visit_members = ''
ret += mcgen('''
- if (args) {
-''')
- push_indent()
+ visit_type_%(c_arg_type)s_members(v, &arg, NULL);
+''',
+ c_arg_type=arg_type.c_name())
ret += mcgen('''
- v = qapi_dealloc_visitor_new();
- visit_start_struct(v, NULL, NULL, 0, NULL);
- %(visit_members)s
visit_end_struct(v, NULL);
visit_free(v);
-''',
- visit_members=visit_members)
-
- if not have_args:
- pop_indent()
- ret += mcgen('''
- }
''')
ret += mcgen('''
diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py
index 23d9194aa4..d5d7a1031f 100644
--- a/scripts/qapi/visit.py
+++ b/scripts/qapi/visit.py
@@ -189,6 +189,8 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
goto out;
}
if (!*obj) {
+ /* incomplete */
+ assert(visit_is_dealloc(v));
goto out_obj;
}
switch ((*obj)->type) {
@@ -230,8 +232,12 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
case QTYPE_NONE:
abort();
default:
+ assert(visit_is_input(v));
error_setg(&err, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
"%(name)s");
+ /* Avoid passing invalid *obj to qapi_free_%(c_name)s() */
+ g_free(*obj);
+ *obj = NULL;
}
out_obj:
visit_end_alternate(v, (void **)obj);
@@ -260,6 +266,8 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error
goto out;
}
if (!*obj) {
+ /* incomplete */
+ assert(visit_is_dealloc(v));
goto out_obj;
}
visit_type_%(c_name)s_members(v, *obj, &err);
diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h
index d95568bf05..56395b87f6 100644
--- a/target/arm/cpu-qom.h
+++ b/target/arm/cpu-qom.h
@@ -35,7 +35,14 @@ struct arm_boot_info;
#define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU
-typedef struct ARMCPUInfo ARMCPUInfo;
+typedef struct ARMCPUInfo {
+ const char *name;
+ void (*initfn)(Object *obj);
+ void (*class_init)(ObjectClass *oc, void *data);
+} ARMCPUInfo;
+
+void arm_cpu_register(const ARMCPUInfo *info);
+void aarch64_cpu_register(const ARMCPUInfo *info);
/**
* ARMCPUClass:
diff --git a/target/arm/cpu.c b/target/arm/cpu.c
index a79f233b17..141d947775 100644
--- a/target/arm/cpu.c
+++ b/target/arm/cpu.c
@@ -582,7 +582,8 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
CPUARMState *env = &cpu->env;
bool ret = false;
- /* ARMv7-M interrupt masking works differently than -A or -R.
+ /*
+ * ARMv7-M interrupt masking works differently than -A or -R.
* There is no FIQ/IRQ distinction. Instead of I and F bits
* masking FIQ and IRQ interrupts, an exception is taken only
* if it is higher priority than the current execution priority
@@ -1912,7 +1913,8 @@ static void arm1026_initfn(Object *obj)
static void arm1136_r2_initfn(Object *obj)
{
ARMCPU *cpu = ARM_CPU(obj);
- /* What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an
+ /*
+ * What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an
* older core than plain "arm1136". In particular this does not
* have the v6K features.
* These ID register values are correct for 1136 but may be wrong
@@ -2693,18 +2695,13 @@ static void arm_max_initfn(Object *obj)
#endif /* !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) */
-struct ARMCPUInfo {
- const char *name;
- void (*initfn)(Object *obj);
- void (*class_init)(ObjectClass *oc, void *data);
-};
-
static const ARMCPUInfo arm_cpus[] = {
#if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64)
{ .name = "arm926", .initfn = arm926_initfn },
{ .name = "arm946", .initfn = arm946_initfn },
{ .name = "arm1026", .initfn = arm1026_initfn },
- /* What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
+ /*
+ * What QEMU calls "arm1136-r2" is actually the 1136 r0p2, i.e. an
* older core than plain "arm1136". In particular this does not
* have the v6K features.
*/
@@ -2864,7 +2861,7 @@ static void cpu_register_class_init(ObjectClass *oc, void *data)
acc->info = data;
}
-static void cpu_register(const ARMCPUInfo *info)
+void arm_cpu_register(const ARMCPUInfo *info)
{
TypeInfo type_info = {
.parent = TYPE_ARM_CPU,
@@ -2905,7 +2902,7 @@ static void arm_cpu_register_types(void)
type_register_static(&idau_interface_type_info);
while (info->name) {
- cpu_register(info);
+ arm_cpu_register(info);
info++;
}
diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c
index 95d0c8c101..74afc28d53 100644
--- a/target/arm/cpu64.c
+++ b/target/arm/cpu64.c
@@ -737,12 +737,6 @@ static void aarch64_max_initfn(Object *obj)
cpu_max_set_sve_max_vq, NULL, NULL, &error_fatal);
}
-struct ARMCPUInfo {
- const char *name;
- void (*initfn)(Object *obj);
- void (*class_init)(ObjectClass *oc, void *data);
-};
-
static const ARMCPUInfo aarch64_cpus[] = {
{ .name = "cortex-a57", .initfn = aarch64_a57_initfn },
{ .name = "cortex-a53", .initfn = aarch64_a53_initfn },
@@ -825,7 +819,7 @@ static void cpu_register_class_init(ObjectClass *oc, void *data)
acc->info = data;
}
-static void aarch64_cpu_register(const ARMCPUInfo *info)
+void aarch64_cpu_register(const ARMCPUInfo *info)
{
TypeInfo type_info = {
.parent = TYPE_AARCH64_CPU,
diff --git a/target/arm/helper.c b/target/arm/helper.c
index 7e9ea5d20f..dfefb9b3d9 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -3442,6 +3442,7 @@ static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
+#ifdef CONFIG_TCG
static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
MMUAccessType access_type, ARMMMUIdx mmu_idx)
{
@@ -3602,9 +3603,11 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value,
}
return par64;
}
+#endif /* CONFIG_TCG */
static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
{
+#ifdef CONFIG_TCG
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
uint64_t par64;
ARMMMUIdx mmu_idx;
@@ -3664,17 +3667,26 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
par64 = do_ats_write(env, value, access_type, mmu_idx);
A32_BANKED_CURRENT_REG_SET(env, par, par64);
+#else
+ /* Handled by hardware accelerator. */
+ g_assert_not_reached();
+#endif /* CONFIG_TCG */
}
static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
+#ifdef CONFIG_TCG
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
uint64_t par64;
par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2);
A32_BANKED_CURRENT_REG_SET(env, par, par64);
+#else
+ /* Handled by hardware accelerator. */
+ g_assert_not_reached();
+#endif /* CONFIG_TCG */
}
static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
@@ -3689,6 +3701,7 @@ static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri,
static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
+#ifdef CONFIG_TCG
MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD;
ARMMMUIdx mmu_idx;
int secure = arm_is_secure_below_el3(env);
@@ -3728,6 +3741,10 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri,
}
env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx);
+#else
+ /* Handled by hardware accelerator. */
+ g_assert_not_reached();
+#endif /* CONFIG_TCG */
}
#endif
diff --git a/target/arm/helper.h b/target/arm/helper.h
index f37b8670a5..5817626b20 100644
--- a/target/arm/helper.h
+++ b/target/arm/helper.h
@@ -275,19 +275,6 @@ DEF_HELPER_2(neon_hsub_u16, i32, i32, i32)
DEF_HELPER_2(neon_hsub_s32, s32, s32, s32)
DEF_HELPER_2(neon_hsub_u32, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_u8, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_s8, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_u16, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_s16, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_u32, i32, i32, i32)
-DEF_HELPER_2(neon_cgt_s32, i32, i32, i32)
-DEF_HELPER_2(neon_cge_u8, i32, i32, i32)
-DEF_HELPER_2(neon_cge_s8, i32, i32, i32)
-DEF_HELPER_2(neon_cge_u16, i32, i32, i32)
-DEF_HELPER_2(neon_cge_s16, i32, i32, i32)
-DEF_HELPER_2(neon_cge_u32, i32, i32, i32)
-DEF_HELPER_2(neon_cge_s32, i32, i32, i32)
-
DEF_HELPER_2(neon_pmin_u8, i32, i32, i32)
DEF_HELPER_2(neon_pmin_s8, i32, i32, i32)
DEF_HELPER_2(neon_pmin_u16, i32, i32, i32)
@@ -347,9 +334,6 @@ DEF_HELPER_2(neon_mul_u16, i32, i32, i32)
DEF_HELPER_2(neon_tst_u8, i32, i32, i32)
DEF_HELPER_2(neon_tst_u16, i32, i32, i32)
DEF_HELPER_2(neon_tst_u32, i32, i32, i32)
-DEF_HELPER_2(neon_ceq_u8, i32, i32, i32)
-DEF_HELPER_2(neon_ceq_u16, i32, i32, i32)
-DEF_HELPER_2(neon_ceq_u32, i32, i32, i32)
DEF_HELPER_1(neon_clz_u8, i32, i32)
DEF_HELPER_1(neon_clz_u16, i32, i32)
@@ -686,6 +670,17 @@ DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr)
DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr)
DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr)
+DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_clt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_clt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_cle0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_cle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_cgt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_cgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_cge0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+DEF_HELPER_FLAGS_3(gvec_cge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32)
+
DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32)
diff --git a/target/arm/neon_helper.c b/target/arm/neon_helper.c
index c7a8438b42..448be93fa1 100644
--- a/target/arm/neon_helper.c
+++ b/target/arm/neon_helper.c
@@ -562,24 +562,6 @@ uint32_t HELPER(neon_hsub_u32)(uint32_t src1, uint32_t src2)
return dest;
}
-#define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? ~0 : 0
-NEON_VOP(cgt_s8, neon_s8, 4)
-NEON_VOP(cgt_u8, neon_u8, 4)
-NEON_VOP(cgt_s16, neon_s16, 2)
-NEON_VOP(cgt_u16, neon_u16, 2)
-NEON_VOP(cgt_s32, neon_s32, 1)
-NEON_VOP(cgt_u32, neon_u32, 1)
-#undef NEON_FN
-
-#define NEON_FN(dest, src1, src2) dest = (src1 >= src2) ? ~0 : 0
-NEON_VOP(cge_s8, neon_s8, 4)
-NEON_VOP(cge_u8, neon_u8, 4)
-NEON_VOP(cge_s16, neon_s16, 2)
-NEON_VOP(cge_u16, neon_u16, 2)
-NEON_VOP(cge_s32, neon_s32, 1)
-NEON_VOP(cge_u32, neon_u32, 1)
-#undef NEON_FN
-
#define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2
NEON_POP(pmin_s8, neon_s8, 4)
NEON_POP(pmin_u8, neon_u8, 4)
@@ -1135,12 +1117,6 @@ NEON_VOP(tst_u16, neon_u16, 2)
NEON_VOP(tst_u32, neon_u32, 1)
#undef NEON_FN
-#define NEON_FN(dest, src1, src2) dest = (src1 == src2) ? -1 : 0
-NEON_VOP(ceq_u8, neon_u8, 4)
-NEON_VOP(ceq_u16, neon_u16, 2)
-NEON_VOP(ceq_u32, neon_u32, 1)
-#undef NEON_FN
-
/* Count Leading Sign/Zero Bits. */
static inline int do_clz8(uint8_t x)
{
diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c
index 7580e46367..efb1c4adc4 100644
--- a/target/arm/translate-a64.c
+++ b/target/arm/translate-a64.c
@@ -594,6 +594,14 @@ static void gen_gvec_fn4(DisasContext *s, bool is_q, int rd, int rn, int rm,
is_q ? 16 : 8, vec_full_reg_size(s));
}
+/* Expand a 2-operand AdvSIMD vector operation using an op descriptor. */
+static void gen_gvec_op2(DisasContext *s, bool is_q, int rd,
+ int rn, const GVecGen2 *gvec_op)
+{
+ tcg_gen_gvec_2(vec_full_reg_offset(s, rd), vec_full_reg_offset(s, rn),
+ is_q ? 16 : 8, vec_full_reg_size(s), gvec_op);
+}
+
/* Expand a 2-operand + immediate AdvSIMD vector operation using
* an op descriptor.
*/
@@ -12366,6 +12374,15 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
return;
}
break;
+ case 0x8: /* CMGT, CMGE */
+ gen_gvec_op2(s, is_q, rd, rn, u ? &cge0_op[size] : &cgt0_op[size]);
+ return;
+ case 0x9: /* CMEQ, CMLE */
+ gen_gvec_op2(s, is_q, rd, rn, u ? &cle0_op[size] : &ceq0_op[size]);
+ return;
+ case 0xa: /* CMLT */
+ gen_gvec_op2(s, is_q, rd, rn, &clt0_op[size]);
+ return;
case 0xb:
if (u) { /* ABS, NEG */
gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_neg, size);
@@ -12403,29 +12420,12 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
for (pass = 0; pass < (is_q ? 4 : 2); pass++) {
TCGv_i32 tcg_op = tcg_temp_new_i32();
TCGv_i32 tcg_res = tcg_temp_new_i32();
- TCGCond cond;
read_vec_element_i32(s, tcg_op, rn, pass, MO_32);
if (size == 2) {
/* Special cases for 32 bit elements */
switch (opcode) {
- case 0xa: /* CMLT */
- /* 32 bit integer comparison against zero, result is
- * test ? (2^32 - 1) : 0. We implement via setcond(test)
- * and inverting.
- */
- cond = TCG_COND_LT;
- do_cmop:
- tcg_gen_setcondi_i32(cond, tcg_res, tcg_op, 0);
- tcg_gen_neg_i32(tcg_res, tcg_res);
- break;
- case 0x8: /* CMGT, CMGE */
- cond = u ? TCG_COND_GE : TCG_COND_GT;
- goto do_cmop;
- case 0x9: /* CMEQ, CMLE */
- cond = u ? TCG_COND_LE : TCG_COND_EQ;
- goto do_cmop;
case 0x4: /* CLS */
if (u) {
tcg_gen_clzi_i32(tcg_res, tcg_op, 32);
@@ -12522,36 +12522,6 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn)
genfn(tcg_res, cpu_env, tcg_op);
break;
}
- case 0x8: /* CMGT, CMGE */
- case 0x9: /* CMEQ, CMLE */
- case 0xa: /* CMLT */
- {
- static NeonGenTwoOpFn * const fns[3][2] = {
- { gen_helper_neon_cgt_s8, gen_helper_neon_cgt_s16 },
- { gen_helper_neon_cge_s8, gen_helper_neon_cge_s16 },
- { gen_helper_neon_ceq_u8, gen_helper_neon_ceq_u16 },
- };
- NeonGenTwoOpFn *genfn;
- int comp;
- bool reverse;
- TCGv_i32 tcg_zero = tcg_const_i32(0);
-
- /* comp = index into [CMGT, CMGE, CMEQ, CMLE, CMLT] */
- comp = (opcode - 0x8) * 2 + u;
- /* ...but LE, LT are implemented as reverse GE, GT */
- reverse = (comp > 2);
- if (reverse) {
- comp = 4 - comp;
- }
- genfn = fns[comp][size];
- if (reverse) {
- genfn(tcg_res, tcg_zero, tcg_op);
- } else {
- genfn(tcg_res, tcg_op, tcg_zero);
- }
- tcg_temp_free_i32(tcg_zero);
- break;
- }
case 0x4: /* CLS, CLZ */
if (u) {
if (size == 0) {
diff --git a/target/arm/translate.c b/target/arm/translate.c
index 9f9f4e19e0..d4ad2028f1 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -3917,6 +3917,205 @@ static int do_v81_helper(DisasContext *s, gen_helper_gvec_3_ptr *fn,
return 1;
}
+static void gen_ceq0_i32(TCGv_i32 d, TCGv_i32 a)
+{
+ tcg_gen_setcondi_i32(TCG_COND_EQ, d, a, 0);
+ tcg_gen_neg_i32(d, d);
+}
+
+static void gen_ceq0_i64(TCGv_i64 d, TCGv_i64 a)
+{
+ tcg_gen_setcondi_i64(TCG_COND_EQ, d, a, 0);
+ tcg_gen_neg_i64(d, d);
+}
+
+static void gen_ceq0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
+{
+ TCGv_vec zero = tcg_const_zeros_vec_matching(d);
+ tcg_gen_cmp_vec(TCG_COND_EQ, vece, d, a, zero);
+ tcg_temp_free_vec(zero);
+}
+
+static const TCGOpcode vecop_list_cmp[] = {
+ INDEX_op_cmp_vec, 0
+};
+
+const GVecGen2 ceq0_op[4] = {
+ { .fno = gen_helper_gvec_ceq0_b,
+ .fniv = gen_ceq0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_8 },
+ { .fno = gen_helper_gvec_ceq0_h,
+ .fniv = gen_ceq0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_16 },
+ { .fni4 = gen_ceq0_i32,
+ .fniv = gen_ceq0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_32 },
+ { .fni8 = gen_ceq0_i64,
+ .fniv = gen_ceq0_vec,
+ .opt_opc = vecop_list_cmp,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .vece = MO_64 },
+};
+
+static void gen_cle0_i32(TCGv_i32 d, TCGv_i32 a)
+{
+ tcg_gen_setcondi_i32(TCG_COND_LE, d, a, 0);
+ tcg_gen_neg_i32(d, d);
+}
+
+static void gen_cle0_i64(TCGv_i64 d, TCGv_i64 a)
+{
+ tcg_gen_setcondi_i64(TCG_COND_LE, d, a, 0);
+ tcg_gen_neg_i64(d, d);
+}
+
+static void gen_cle0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
+{
+ TCGv_vec zero = tcg_const_zeros_vec_matching(d);
+ tcg_gen_cmp_vec(TCG_COND_LE, vece, d, a, zero);
+ tcg_temp_free_vec(zero);
+}
+
+const GVecGen2 cle0_op[4] = {
+ { .fno = gen_helper_gvec_cle0_b,
+ .fniv = gen_cle0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_8 },
+ { .fno = gen_helper_gvec_cle0_h,
+ .fniv = gen_cle0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_16 },
+ { .fni4 = gen_cle0_i32,
+ .fniv = gen_cle0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_32 },
+ { .fni8 = gen_cle0_i64,
+ .fniv = gen_cle0_vec,
+ .opt_opc = vecop_list_cmp,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .vece = MO_64 },
+};
+
+static void gen_cge0_i32(TCGv_i32 d, TCGv_i32 a)
+{
+ tcg_gen_setcondi_i32(TCG_COND_GE, d, a, 0);
+ tcg_gen_neg_i32(d, d);
+}
+
+static void gen_cge0_i64(TCGv_i64 d, TCGv_i64 a)
+{
+ tcg_gen_setcondi_i64(TCG_COND_GE, d, a, 0);
+ tcg_gen_neg_i64(d, d);
+}
+
+static void gen_cge0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
+{
+ TCGv_vec zero = tcg_const_zeros_vec_matching(d);
+ tcg_gen_cmp_vec(TCG_COND_GE, vece, d, a, zero);
+ tcg_temp_free_vec(zero);
+}
+
+const GVecGen2 cge0_op[4] = {
+ { .fno = gen_helper_gvec_cge0_b,
+ .fniv = gen_cge0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_8 },
+ { .fno = gen_helper_gvec_cge0_h,
+ .fniv = gen_cge0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_16 },
+ { .fni4 = gen_cge0_i32,
+ .fniv = gen_cge0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_32 },
+ { .fni8 = gen_cge0_i64,
+ .fniv = gen_cge0_vec,
+ .opt_opc = vecop_list_cmp,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .vece = MO_64 },
+};
+
+static void gen_clt0_i32(TCGv_i32 d, TCGv_i32 a)
+{
+ tcg_gen_setcondi_i32(TCG_COND_LT, d, a, 0);
+ tcg_gen_neg_i32(d, d);
+}
+
+static void gen_clt0_i64(TCGv_i64 d, TCGv_i64 a)
+{
+ tcg_gen_setcondi_i64(TCG_COND_LT, d, a, 0);
+ tcg_gen_neg_i64(d, d);
+}
+
+static void gen_clt0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
+{
+ TCGv_vec zero = tcg_const_zeros_vec_matching(d);
+ tcg_gen_cmp_vec(TCG_COND_LT, vece, d, a, zero);
+ tcg_temp_free_vec(zero);
+}
+
+const GVecGen2 clt0_op[4] = {
+ { .fno = gen_helper_gvec_clt0_b,
+ .fniv = gen_clt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_8 },
+ { .fno = gen_helper_gvec_clt0_h,
+ .fniv = gen_clt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_16 },
+ { .fni4 = gen_clt0_i32,
+ .fniv = gen_clt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_32 },
+ { .fni8 = gen_clt0_i64,
+ .fniv = gen_clt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .vece = MO_64 },
+};
+
+static void gen_cgt0_i32(TCGv_i32 d, TCGv_i32 a)
+{
+ tcg_gen_setcondi_i32(TCG_COND_GT, d, a, 0);
+ tcg_gen_neg_i32(d, d);
+}
+
+static void gen_cgt0_i64(TCGv_i64 d, TCGv_i64 a)
+{
+ tcg_gen_setcondi_i64(TCG_COND_GT, d, a, 0);
+ tcg_gen_neg_i64(d, d);
+}
+
+static void gen_cgt0_vec(unsigned vece, TCGv_vec d, TCGv_vec a)
+{
+ TCGv_vec zero = tcg_const_zeros_vec_matching(d);
+ tcg_gen_cmp_vec(TCG_COND_GT, vece, d, a, zero);
+ tcg_temp_free_vec(zero);
+}
+
+const GVecGen2 cgt0_op[4] = {
+ { .fno = gen_helper_gvec_cgt0_b,
+ .fniv = gen_cgt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_8 },
+ { .fno = gen_helper_gvec_cgt0_h,
+ .fniv = gen_cgt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_16 },
+ { .fni4 = gen_cgt0_i32,
+ .fniv = gen_cgt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .vece = MO_32 },
+ { .fni8 = gen_cgt0_i64,
+ .fniv = gen_cgt0_vec,
+ .opt_opc = vecop_list_cmp,
+ .prefer_i64 = TCG_TARGET_REG_BITS == 64,
+ .vece = MO_64 },
+};
+
static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift)
{
tcg_gen_vec_sar8i_i64(a, a, shift);
@@ -6481,6 +6680,27 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
tcg_gen_gvec_abs(size, rd_ofs, rm_ofs, vec_size, vec_size);
break;
+ case NEON_2RM_VCEQ0:
+ tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
+ vec_size, &ceq0_op[size]);
+ break;
+ case NEON_2RM_VCGT0:
+ tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
+ vec_size, &cgt0_op[size]);
+ break;
+ case NEON_2RM_VCLE0:
+ tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
+ vec_size, &cle0_op[size]);
+ break;
+ case NEON_2RM_VCGE0:
+ tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
+ vec_size, &cge0_op[size]);
+ break;
+ case NEON_2RM_VCLT0:
+ tcg_gen_gvec_2(rd_ofs, rm_ofs, vec_size,
+ vec_size, &clt0_op[size]);
+ break;
+
default:
elementwise:
for (pass = 0; pass < (q ? 4 : 2); pass++) {
@@ -6543,42 +6763,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
default: abort();
}
break;
- case NEON_2RM_VCGT0: case NEON_2RM_VCLE0:
- tmp2 = tcg_const_i32(0);
- switch(size) {
- case 0: gen_helper_neon_cgt_s8(tmp, tmp, tmp2); break;
- case 1: gen_helper_neon_cgt_s16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_cgt_s32(tmp, tmp, tmp2); break;
- default: abort();
- }
- tcg_temp_free_i32(tmp2);
- if (op == NEON_2RM_VCLE0) {
- tcg_gen_not_i32(tmp, tmp);
- }
- break;
- case NEON_2RM_VCGE0: case NEON_2RM_VCLT0:
- tmp2 = tcg_const_i32(0);
- switch(size) {
- case 0: gen_helper_neon_cge_s8(tmp, tmp, tmp2); break;
- case 1: gen_helper_neon_cge_s16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_cge_s32(tmp, tmp, tmp2); break;
- default: abort();
- }
- tcg_temp_free_i32(tmp2);
- if (op == NEON_2RM_VCLT0) {
- tcg_gen_not_i32(tmp, tmp);
- }
- break;
- case NEON_2RM_VCEQ0:
- tmp2 = tcg_const_i32(0);
- switch(size) {
- case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break;
- case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break;
- case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break;
- default: abort();
- }
- tcg_temp_free_i32(tmp2);
- break;
case NEON_2RM_VCGT0_F:
{
TCGv_ptr fpstatus = get_fpstatus_ptr(1);
diff --git a/target/arm/translate.h b/target/arm/translate.h
index d9ea0c99cc..98b319f3f6 100644
--- a/target/arm/translate.h
+++ b/target/arm/translate.h
@@ -275,6 +275,11 @@ static inline void gen_swstep_exception(DisasContext *s, int isv, int ex)
uint64_t vfp_expand_imm(int size, uint8_t imm8);
/* Vector operations shared between ARM and AArch64. */
+extern const GVecGen2 ceq0_op[4];
+extern const GVecGen2 clt0_op[4];
+extern const GVecGen2 cgt0_op[4];
+extern const GVecGen2 cle0_op[4];
+extern const GVecGen2 cge0_op[4];
extern const GVecGen3 mla_op[4];
extern const GVecGen3 mls_op[4];
extern const GVecGen3 cmtst_op[4];
diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c
index 8017bd88c4..3d534188a8 100644
--- a/target/arm/vec_helper.c
+++ b/target/arm/vec_helper.c
@@ -1257,3 +1257,28 @@ void HELPER(sve2_pmull_h)(void *vd, void *vn, void *vm, uint32_t desc)
}
}
#endif
+
+#define DO_CMP0(NAME, TYPE, OP) \
+void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \
+{ \
+ intptr_t i, opr_sz = simd_oprsz(desc); \
+ for (i = 0; i < opr_sz; i += sizeof(TYPE)) { \
+ TYPE nn = *(TYPE *)(vn + i); \
+ *(TYPE *)(vd + i) = -(nn OP 0); \
+ } \
+ clear_tail(vd, opr_sz, simd_maxsz(desc)); \
+}
+
+DO_CMP0(gvec_ceq0_b, int8_t, ==)
+DO_CMP0(gvec_clt0_b, int8_t, <)
+DO_CMP0(gvec_cle0_b, int8_t, <=)
+DO_CMP0(gvec_cgt0_b, int8_t, >)
+DO_CMP0(gvec_cge0_b, int8_t, >=)
+
+DO_CMP0(gvec_ceq0_h, int16_t, ==)
+DO_CMP0(gvec_clt0_h, int16_t, <)
+DO_CMP0(gvec_cle0_h, int16_t, <=)
+DO_CMP0(gvec_cgt0_h, int16_t, >)
+DO_CMP0(gvec_cge0_h, int16_t, >=)
+
+#undef DO_CMP0
diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c
index 4e578239d3..059d71f2c7 100644
--- a/target/riscv/cpu.c
+++ b/target/riscv/cpu.c
@@ -164,6 +164,15 @@ static void rv32imacu_nommu_cpu_init(Object *obj)
set_feature(env, RISCV_FEATURE_PMP);
}
+static void rv32imafcu_nommu_cpu_init(Object *obj)
+{
+ CPURISCVState *env = &RISCV_CPU(obj)->env;
+ set_misa(env, RV32 | RVI | RVM | RVA | RVF | RVC | RVU);
+ set_priv_version(env, PRIV_VERSION_1_10_0);
+ set_resetvec(env, DEFAULT_RSTVEC);
+ set_feature(env, RISCV_FEATURE_PMP);
+}
+
#elif defined(TARGET_RISCV64)
static void riscv_base64_cpu_init(Object *obj)
@@ -610,6 +619,7 @@ static const TypeInfo riscv_cpu_type_infos[] = {
#if defined(TARGET_RISCV32)
DEFINE_CPU(TYPE_RISCV_CPU_BASE32, riscv_base32_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E31, rv32imacu_nommu_cpu_init),
+ DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_E34, rv32imafcu_nommu_cpu_init),
DEFINE_CPU(TYPE_RISCV_CPU_SIFIVE_U34, rv32gcsu_priv1_10_0_cpu_init),
/* Depreacted */
DEFINE_CPU(TYPE_RISCV_CPU_RV32IMACU_NOMMU, rv32imacu_nommu_cpu_init),
diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h
index 7d21addbab..d0e7f5b9c5 100644
--- a/target/riscv/cpu.h
+++ b/target/riscv/cpu.h
@@ -36,6 +36,7 @@
#define TYPE_RISCV_CPU_BASE32 RISCV_CPU_TYPE_NAME("rv32")
#define TYPE_RISCV_CPU_BASE64 RISCV_CPU_TYPE_NAME("rv64")
#define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31")
+#define TYPE_RISCV_CPU_SIFIVE_E34 RISCV_CPU_TYPE_NAME("sifive-e34")
#define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51")
#define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34")
#define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54")
diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c
index d3ba9efb02..bc80aa87cf 100644
--- a/target/riscv/cpu_helper.c
+++ b/target/riscv/cpu_helper.c
@@ -452,10 +452,11 @@ restart:
hwaddr pte_addr;
if (two_stage && first_stage) {
+ int vbase_prot;
hwaddr vbase;
/* Do the second stage translation on the base PTE address. */
- get_physical_address(env, &vbase, prot, base, access_type,
+ get_physical_address(env, &vbase, &vbase_prot, base, access_type,
mmu_idx, false, true);
pte_addr = vbase + idx * ptesize;
@@ -558,12 +559,7 @@ restart:
/* for superpage mappings, make a fake leaf PTE for the TLB's
benefit. */
target_ulong vpn = addr >> PGSHIFT;
- if (i == 0) {
- *physical = (ppn | (vpn & ((1L << (ptshift + widened)) - 1))) <<
- PGSHIFT;
- } else {
- *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
- }
+ *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT;
/* set permissions on the TLB entry */
if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) {
@@ -706,7 +702,7 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
#ifndef CONFIG_USER_ONLY
vaddr im_address;
hwaddr pa = 0;
- int prot;
+ int prot, prot2;
bool pmp_violation = false;
bool m_mode_two_stage = false;
bool hs_mode_two_stage = false;
@@ -756,13 +752,15 @@ bool riscv_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
/* Second stage lookup */
im_address = pa;
- ret = get_physical_address(env, &pa, &prot, im_address,
+ ret = get_physical_address(env, &pa, &prot2, im_address,
access_type, mmu_idx, false, true);
qemu_log_mask(CPU_LOG_MMU,
"%s 2nd-stage address=%" VADDR_PRIx " ret %d physical "
TARGET_FMT_plx " prot %d\n",
- __func__, im_address, ret, pa, prot);
+ __func__, im_address, ret, pa, prot2);
+
+ prot &= prot2;
if (riscv_feature(env, RISCV_FEATURE_PMP) &&
(ret == TRANSLATE_SUCCESS) &&
diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c
index 427a46e3e1..f2ccf0a06a 100644
--- a/target/s390x/cpu.c
+++ b/target/s390x/cpu.c
@@ -37,6 +37,7 @@
#include "sysemu/hw_accel.h"
#include "hw/qdev-properties.h"
#ifndef CONFIG_USER_ONLY
+#include "hw/s390x/pv.h"
#include "hw/boards.h"
#include "sysemu/arch_init.h"
#include "sysemu/sysemu.h"
@@ -76,16 +77,24 @@ static bool s390_cpu_has_work(CPUState *cs)
static void s390_cpu_load_normal(CPUState *s)
{
S390CPU *cpu = S390_CPU(s);
- uint64_t spsw = ldq_phys(s->as, 0);
-
- cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL;
- /*
- * Invert short psw indication, so SIE will report a specification
- * exception if it was not set.
- */
- cpu->env.psw.mask ^= PSW_MASK_SHORTPSW;
- cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR;
+ uint64_t spsw;
+ if (!s390_is_pv()) {
+ spsw = ldq_phys(s->as, 0);
+ cpu->env.psw.mask = spsw & PSW_MASK_SHORT_CTRL;
+ /*
+ * Invert short psw indication, so SIE will report a specification
+ * exception if it was not set.
+ */
+ cpu->env.psw.mask ^= PSW_MASK_SHORTPSW;
+ cpu->env.psw.addr = spsw & PSW_MASK_SHORT_ADDR;
+ } else {
+ /*
+ * Firmware requires us to set the load state before we set
+ * the cpu to operating on protected guests.
+ */
+ s390_cpu_set_state(S390_CPU_STATE_LOAD, cpu);
+ }
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
}
#endif
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index 1d17709d6e..035427521c 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -823,7 +823,12 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
#define s390_cpu_virt_mem_check_write(cpu, laddr, ar, len) \
s390_cpu_virt_mem_rw(cpu, laddr, ar, NULL, len, true)
void s390_cpu_virt_mem_handle_exc(S390CPU *cpu, uintptr_t ra);
-
+int s390_cpu_pv_mem_rw(S390CPU *cpu, unsigned int offset, void *hostbuf,
+ int len, bool is_write);
+#define s390_cpu_pv_mem_read(cpu, offset, dest, len) \
+ s390_cpu_pv_mem_rw(cpu, offset, dest, len, false)
+#define s390_cpu_pv_mem_write(cpu, offset, dest, len) \
+ s390_cpu_pv_mem_rw(cpu, offset, dest, len, true)
/* sigp.c */
int s390_cpu_restart(S390CPU *cpu);
diff --git a/target/s390x/cpu_features_def.inc.h b/target/s390x/cpu_features_def.inc.h
index 31dff0d84e..60db28351d 100644
--- a/target/s390x/cpu_features_def.inc.h
+++ b/target/s390x/cpu_features_def.inc.h
@@ -107,6 +107,7 @@ DEF_FEAT(DEFLATE_BASE, "deflate-base", STFL, 151, "Deflate-conversion facility (
DEF_FEAT(VECTOR_PACKED_DECIMAL_ENH, "vxpdeh", STFL, 152, "Vector-Packed-Decimal-Enhancement Facility")
DEF_FEAT(MSA_EXT_9, "msa9-base", STFL, 155, "Message-security-assist-extension-9 facility (excluding subfunctions)")
DEF_FEAT(ETOKEN, "etoken", STFL, 156, "Etoken facility")
+DEF_FEAT(UNPACK, "unpack", STFL, 161, "Unpack facility")
/* Features exposed via SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */
DEF_FEAT(SIE_GSLS, "gsls", SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility")
diff --git a/target/s390x/diag.c b/target/s390x/diag.c
index 54e5670b3f..1a48429564 100644
--- a/target/s390x/diag.c
+++ b/target/s390x/diag.c
@@ -20,6 +20,8 @@
#include "sysemu/cpus.h"
#include "hw/s390x/ipl.h"
#include "hw/s390x/s390-virtio-ccw.h"
+#include "hw/s390x/pv.h"
+#include "kvm_s390x.h"
int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
{
@@ -49,20 +51,13 @@ int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
return diag288_class->handle_timer(diag288, func, timeout);
}
-#define DIAG_308_RC_OK 0x0001
-#define DIAG_308_RC_NO_CONF 0x0102
-#define DIAG_308_RC_INVALID 0x0402
-
-#define DIAG308_RESET_MOD_CLR 0
-#define DIAG308_RESET_LOAD_NORM 1
-#define DIAG308_LOAD_CLEAR 3
-#define DIAG308_LOAD_NORMAL_DUMP 4
-#define DIAG308_SET 5
-#define DIAG308_STORE 6
-
static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
uintptr_t ra, bool write)
{
+ /* Handled by the Ultravisor */
+ if (s390_is_pv()) {
+ return 0;
+ }
if ((r1 & 1) || (addr & ~TARGET_PAGE_MASK)) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return -1;
@@ -78,7 +73,9 @@ static int diag308_parm_check(CPUS390XState *env, uint64_t r1, uint64_t addr,
void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
{
+ bool valid;
CPUState *cs = env_cpu(env);
+ S390CPU *cpu = S390_CPU(cs);
uint64_t addr = env->regs[r1];
uint64_t subcode = env->regs[r3];
IplParameterBlock *iplb;
@@ -93,6 +90,11 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
return;
}
+ if (subcode >= DIAG308_PV_SET && !s390_has_feat(S390_FEAT_UNPACK)) {
+ s390_program_interrupt(env, PGM_SPECIFICATION, ra);
+ return;
+ }
+
switch (subcode) {
case DIAG308_RESET_MOD_CLR:
s390_ipl_reset_request(cs, S390_RESET_MODIFIED_CLEAR);
@@ -105,19 +107,30 @@ void handle_diag_308(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
s390_ipl_reset_request(cs, S390_RESET_REIPL);
break;
case DIAG308_SET:
+ case DIAG308_PV_SET:
if (diag308_parm_check(env, r1, addr, ra, false)) {
return;
}
iplb = g_new0(IplParameterBlock, 1);
- cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
+ if (!s390_is_pv()) {
+ cpu_physical_memory_read(addr, iplb, sizeof(iplb->len));
+ } else {
+ s390_cpu_pv_mem_read(cpu, 0, iplb, sizeof(iplb->len));
+ }
+
if (!iplb_valid_len(iplb)) {
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
goto out;
}
- cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
+ if (!s390_is_pv()) {
+ cpu_physical_memory_read(addr, iplb, be32_to_cpu(iplb->len));
+ } else {
+ s390_cpu_pv_mem_read(cpu, 0, iplb, be32_to_cpu(iplb->len));
+ }
- if (!iplb_valid(iplb)) {
+ valid = subcode == DIAG308_PV_SET ? iplb_valid_pv(iplb) : iplb_valid(iplb);
+ if (!valid) {
env->regs[r1 + 1] = DIAG_308_RC_INVALID;
goto out;
}
@@ -128,17 +141,43 @@ out:
g_free(iplb);
return;
case DIAG308_STORE:
+ case DIAG308_PV_STORE:
if (diag308_parm_check(env, r1, addr, ra, true)) {
return;
}
- iplb = s390_ipl_get_iplb();
- if (iplb) {
- cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
- env->regs[r1 + 1] = DIAG_308_RC_OK;
+ if (subcode == DIAG308_PV_STORE) {
+ iplb = s390_ipl_get_iplb_pv();
} else {
+ iplb = s390_ipl_get_iplb();
+ }
+ if (!iplb) {
env->regs[r1 + 1] = DIAG_308_RC_NO_CONF;
+ return;
}
+
+ if (!s390_is_pv()) {
+ cpu_physical_memory_write(addr, iplb, be32_to_cpu(iplb->len));
+ } else {
+ s390_cpu_pv_mem_write(cpu, 0, iplb, be32_to_cpu(iplb->len));
+ }
+ env->regs[r1 + 1] = DIAG_308_RC_OK;
return;
+ case DIAG308_PV_START:
+ iplb = s390_ipl_get_iplb_pv();
+ if (!iplb) {
+ env->regs[r1 + 1] = DIAG_308_RC_NO_PV_CONF;
+ return;
+ }
+
+ if (kvm_s390_get_hpage_1m()) {
+ error_report("Protected VMs can currently not be backed with "
+ "huge pages");
+ env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV;
+ return;
+ }
+
+ s390_ipl_reset_request(cs, S390_RESET_PV);
+ break;
default:
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
break;
diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c
index 6278845b12..8ddeebc544 100644
--- a/target/s390x/gen-features.c
+++ b/target/s390x/gen-features.c
@@ -562,6 +562,7 @@ static uint16_t full_GEN15_GA1[] = {
S390_FEAT_GROUP_MSA_EXT_9,
S390_FEAT_GROUP_MSA_EXT_9_PCKMO,
S390_FEAT_ETOKEN,
+ S390_FEAT_UNPACK,
};
/* Default features (in order of release)
diff --git a/target/s390x/helper.c b/target/s390x/helper.c
index ed72684911..09f60406aa 100644
--- a/target/s390x/helper.c
+++ b/target/s390x/helper.c
@@ -25,6 +25,7 @@
#include "qemu/timer.h"
#include "qemu/qemu-print.h"
#include "hw/s390x/ioinst.h"
+#include "hw/s390x/pv.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
#ifndef CONFIG_USER_ONLY
@@ -246,6 +247,11 @@ int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch)
hwaddr len = sizeof(*sa);
int i;
+ /* For PVMs storing will occur when this cpu enters SIE again */
+ if (s390_is_pv()) {
+ return 0;
+ }
+
sa = cpu_physical_memory_map(addr, &len, true);
if (!sa) {
return -EFAULT;
diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c
index 0e840cc579..7a14c52c12 100644
--- a/target/s390x/ioinst.c
+++ b/target/s390x/ioinst.c
@@ -16,6 +16,25 @@
#include "hw/s390x/ioinst.h"
#include "trace.h"
#include "hw/s390x/s390-pci-bus.h"
+#include "hw/s390x/pv.h"
+
+/* All I/O instructions but chsc use the s format */
+static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb,
+ uint8_t *ar)
+{
+ /*
+ * Addresses for protected guests are all offsets into the
+ * satellite block which holds the IO control structures. Those
+ * control structures are always starting at offset 0 and are
+ * always aligned and accessible. So we can return 0 here which
+ * will pass the following address checks.
+ */
+ if (s390_is_pv()) {
+ *ar = 0;
+ return 0;
+ }
+ return decode_basedisp_s(env, ipb, ar);
+}
int ioinst_disassemble_sch_ident(uint32_t value, int *m, int *cssid, int *ssid,
int *schid)
@@ -114,12 +133,14 @@ void ioinst_handle_msch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
CPUS390XState *env = &cpu->env;
uint8_t ar;
- addr = decode_basedisp_s(env, ipb, &ar);
+ addr = get_address_from_regs(env, ipb, &ar);
if (addr & 3) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
- if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_read(cpu, addr, &schib, sizeof(schib));
+ } else if (s390_cpu_virt_mem_read(cpu, addr, ar, &schib, sizeof(schib))) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return;
}
@@ -171,12 +192,14 @@ void ioinst_handle_ssch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
CPUS390XState *env = &cpu->env;
uint8_t ar;
- addr = decode_basedisp_s(env, ipb, &ar);
+ addr = get_address_from_regs(env, ipb, &ar);
if (addr & 3) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
}
- if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_read(cpu, addr, &orig_orb, sizeof(orb));
+ } else if (s390_cpu_virt_mem_read(cpu, addr, ar, &orig_orb, sizeof(orb))) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return;
}
@@ -203,7 +226,7 @@ void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
CPUS390XState *env = &cpu->env;
uint8_t ar;
- addr = decode_basedisp_s(env, ipb, &ar);
+ addr = get_address_from_regs(env, ipb, &ar);
if (addr & 3) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
@@ -212,14 +235,19 @@ void ioinst_handle_stcrw(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
cc = css_do_stcrw(&crw);
/* 0 - crw stored, 1 - zeroes stored */
- if (s390_cpu_virt_mem_write(cpu, addr, ar, &crw, sizeof(crw)) == 0) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_write(cpu, addr, &crw, sizeof(crw));
setcc(cpu, cc);
} else {
- if (cc == 0) {
- /* Write failed: requeue CRW since STCRW is suppressing */
- css_undo_stcrw(&crw);
+ if (s390_cpu_virt_mem_write(cpu, addr, ar, &crw, sizeof(crw)) == 0) {
+ setcc(cpu, cc);
+ } else {
+ if (cc == 0) {
+ /* Write failed: requeue CRW since STCRW is suppressing */
+ css_undo_stcrw(&crw);
+ }
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
}
- s390_cpu_virt_mem_handle_exc(cpu, ra);
}
}
@@ -234,7 +262,7 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb,
CPUS390XState *env = &cpu->env;
uint8_t ar;
- addr = decode_basedisp_s(env, ipb, &ar);
+ addr = get_address_from_regs(env, ipb, &ar);
if (addr & 3) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return;
@@ -242,6 +270,13 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb,
if (ioinst_disassemble_sch_ident(reg1, &m, &cssid, &ssid, &schid)) {
/*
+ * The Ultravisor checks schid bit 16 to be one and bits 0-12
+ * to be 0 and injects a operand exception itself.
+ *
+ * Hence we should never end up here.
+ */
+ g_assert(!s390_is_pv());
+ /*
* As operand exceptions have a lower priority than access exceptions,
* we check whether the memory area is writeable (injecting the
* access execption if it is not) first.
@@ -273,14 +308,17 @@ void ioinst_handle_stsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb,
}
}
if (cc != 3) {
- if (s390_cpu_virt_mem_write(cpu, addr, ar, &schib,
- sizeof(schib)) != 0) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_write(cpu, addr, &schib, sizeof(schib));
+ } else if (s390_cpu_virt_mem_write(cpu, addr, ar, &schib,
+ sizeof(schib)) != 0) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return;
}
} else {
/* Access exceptions have a higher priority than cc3 */
- if (s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib)) != 0) {
+ if (!s390_is_pv() &&
+ s390_cpu_virt_mem_check_write(cpu, addr, ar, sizeof(schib)) != 0) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return;
}
@@ -303,7 +341,7 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
return -EIO;
}
trace_ioinst_sch_id("tsch", cssid, ssid, schid);
- addr = decode_basedisp_s(env, ipb, &ar);
+ addr = get_address_from_regs(env, ipb, &ar);
if (addr & 3) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
return -EIO;
@@ -317,7 +355,9 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
}
/* 0 - status pending, 1 - not status pending, 3 - not operational */
if (cc != 3) {
- if (s390_cpu_virt_mem_write(cpu, addr, ar, &irb, irb_len) != 0) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_write(cpu, addr, &irb, irb_len);
+ } else if (s390_cpu_virt_mem_write(cpu, addr, ar, &irb, irb_len) != 0) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return -EFAULT;
}
@@ -325,7 +365,8 @@ int ioinst_handle_tsch(S390CPU *cpu, uint64_t reg1, uint32_t ipb, uintptr_t ra)
} else {
irb_len = sizeof(irb) - sizeof(irb.emw);
/* Access exceptions have a higher priority than cc3 */
- if (s390_cpu_virt_mem_check_write(cpu, addr, ar, irb_len) != 0) {
+ if (!s390_is_pv() &&
+ s390_cpu_virt_mem_check_write(cpu, addr, ar, irb_len) != 0) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return -EFAULT;
}
@@ -601,7 +642,7 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
{
ChscReq *req;
ChscResp *res;
- uint64_t addr;
+ uint64_t addr = 0;
int reg;
uint16_t len;
uint16_t command;
@@ -610,7 +651,9 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
trace_ioinst("chsc");
reg = (ipb >> 20) & 0x00f;
- addr = env->regs[reg];
+ if (!s390_is_pv()) {
+ addr = env->regs[reg];
+ }
/* Page boundary? */
if (addr & 0xfff) {
s390_program_interrupt(env, PGM_SPECIFICATION, ra);
@@ -621,7 +664,9 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
* present CHSC sub-handlers ... if we ever need more, we should take
* care of req->len here first.
*/
- if (s390_cpu_virt_mem_read(cpu, addr, reg, buf, sizeof(ChscReq))) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_read(cpu, addr, buf, sizeof(ChscReq));
+ } else if (s390_cpu_virt_mem_read(cpu, addr, reg, buf, sizeof(ChscReq))) {
s390_cpu_virt_mem_handle_exc(cpu, ra);
return;
}
@@ -654,11 +699,16 @@ void ioinst_handle_chsc(S390CPU *cpu, uint32_t ipb, uintptr_t ra)
break;
}
- if (!s390_cpu_virt_mem_write(cpu, addr + len, reg, res,
- be16_to_cpu(res->len))) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_write(cpu, addr + len, res, be16_to_cpu(res->len));
setcc(cpu, 0); /* Command execution complete */
} else {
- s390_cpu_virt_mem_handle_exc(cpu, ra);
+ if (!s390_cpu_virt_mem_write(cpu, addr + len, reg, res,
+ be16_to_cpu(res->len))) {
+ setcc(cpu, 0); /* Command execution complete */
+ } else {
+ s390_cpu_virt_mem_handle_exc(cpu, ra);
+ }
}
}
diff --git a/target/s390x/kvm-stub.c b/target/s390x/kvm-stub.c
index c4cd497f85..aa185017a2 100644
--- a/target/s390x/kvm-stub.c
+++ b/target/s390x/kvm-stub.c
@@ -39,6 +39,11 @@ int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu)
return 0;
}
+int kvm_s390_get_hpage_1m(void)
+{
+ return 0;
+}
+
int kvm_s390_get_ri(void)
{
return 0;
diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c
index 7f7ebab842..69881a0da0 100644
--- a/target/s390x/kvm.c
+++ b/target/s390x/kvm.c
@@ -50,6 +50,7 @@
#include "exec/memattrs.h"
#include "hw/s390x/s390-virtio-ccw.h"
#include "hw/s390x/s390-virtio-hcall.h"
+#include "hw/s390x/pv.h"
#ifndef DEBUG_KVM
#define DEBUG_KVM 0
@@ -115,6 +116,8 @@
#define ICPT_CPU_STOP 0x28
#define ICPT_OPEREXC 0x2c
#define ICPT_IO 0x40
+#define ICPT_PV_INSTR 0x68
+#define ICPT_PV_INSTR_NOTIFICATION 0x6c
#define NR_LOCAL_IRQS 32
/*
@@ -152,6 +155,7 @@ static int cap_ri;
static int cap_gs;
static int cap_hpage_1m;
static int cap_vcpu_resets;
+static int cap_protected;
static int active_cmma;
@@ -321,6 +325,11 @@ void kvm_s390_set_max_pagesize(uint64_t pagesize, Error **errp)
cap_hpage_1m = 1;
}
+int kvm_s390_get_hpage_1m(void)
+{
+ return cap_hpage_1m;
+}
+
static void ccw_machine_class_foreach(ObjectClass *oc, void *opaque)
{
MachineClass *mc = MACHINE_CLASS(oc);
@@ -344,6 +353,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_mem_op = kvm_check_extension(s, KVM_CAP_S390_MEM_OP);
cap_s390_irq = kvm_check_extension(s, KVM_CAP_S390_INJECT_IRQ);
cap_vcpu_resets = kvm_check_extension(s, KVM_CAP_S390_VCPU_RESETS);
+ cap_protected = kvm_check_extension(s, KVM_CAP_S390_PROTECTED);
if (!kvm_check_extension(s, KVM_CAP_S390_GMAP)
|| !kvm_check_extension(s, KVM_CAP_S390_COW)) {
@@ -844,6 +854,30 @@ int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf,
return ret;
}
+int kvm_s390_mem_op_pv(S390CPU *cpu, uint64_t offset, void *hostbuf,
+ int len, bool is_write)
+{
+ struct kvm_s390_mem_op mem_op = {
+ .sida_offset = offset,
+ .size = len,
+ .op = is_write ? KVM_S390_MEMOP_SIDA_WRITE
+ : KVM_S390_MEMOP_SIDA_READ,
+ .buf = (uint64_t)hostbuf,
+ };
+ int ret;
+
+ if (!cap_mem_op || !cap_protected) {
+ return -ENOSYS;
+ }
+
+ ret = kvm_vcpu_ioctl(CPU(cpu), KVM_S390_MEM_OP, &mem_op);
+ if (ret < 0) {
+ error_report("KVM_S390_MEM_OP failed: %s", strerror(-ret));
+ abort();
+ }
+ return ret;
+}
+
/*
* Legacy layout for s390:
* Older S390 KVM requires the topmost vma of the RAM to be
@@ -1199,12 +1233,27 @@ static void kvm_sclp_service_call(S390CPU *cpu, struct kvm_run *run,
sccb = env->regs[ipbh0 & 0xf];
code = env->regs[(ipbh0 & 0xf0) >> 4];
- r = sclp_service_call(env, sccb, code);
- if (r < 0) {
- kvm_s390_program_interrupt(cpu, -r);
- return;
+ switch (run->s390_sieic.icptcode) {
+ case ICPT_PV_INSTR_NOTIFICATION:
+ g_assert(s390_is_pv());
+ /* The notification intercepts are currently handled by KVM */
+ error_report("unexpected SCLP PV notification");
+ exit(1);
+ break;
+ case ICPT_PV_INSTR:
+ g_assert(s390_is_pv());
+ sclp_service_call_protected(env, sccb, code);
+ /* Setting the CC is done by the Ultravisor. */
+ break;
+ case ICPT_INSTRUCTION:
+ g_assert(!s390_is_pv());
+ r = sclp_service_call(env, sccb, code);
+ if (r < 0) {
+ kvm_s390_program_interrupt(cpu, -r);
+ return;
+ }
+ setcc(cpu, r);
}
- setcc(cpu, r);
}
static int handle_b2(S390CPU *cpu, struct kvm_run *run, uint8_t ipa1)
@@ -1693,6 +1742,8 @@ static int handle_intercept(S390CPU *cpu)
(long)cs->kvm_run->psw_addr);
switch (icpt_code) {
case ICPT_INSTRUCTION:
+ case ICPT_PV_INSTR:
+ case ICPT_PV_INSTR_NOTIFICATION:
r = handle_instruction(cpu, run);
break;
case ICPT_PROGRAM:
@@ -1773,7 +1824,9 @@ static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar)
SysIB_322 sysib;
int del, i;
- if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) {
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_read(cpu, 0, &sysib, sizeof(sysib));
+ } else if (s390_cpu_virt_mem_read(cpu, addr, ar, &sysib, sizeof(sysib))) {
return;
}
/* Shift the stack of Extended Names to prepare for our own data */
@@ -1826,7 +1879,11 @@ static void insert_stsi_3_2_2(S390CPU *cpu, __u64 addr, uint8_t ar)
/* Insert UUID */
memcpy(sysib.vm[0].uuid, &qemu_uuid, sizeof(sysib.vm[0].uuid));
- s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, sizeof(sysib));
+ if (s390_is_pv()) {
+ s390_cpu_pv_mem_write(cpu, 0, &sysib, sizeof(sysib));
+ } else {
+ s390_cpu_virt_mem_write(cpu, addr, ar, &sysib, sizeof(sysib));
+ }
}
static int handle_stsi(S390CPU *cpu)
@@ -2368,6 +2425,14 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp)
clear_bit(S390_FEAT_BPB, model->features);
}
+ /*
+ * If we have support for protected virtualization, indicate
+ * the protected virtualization IPL unpack facility.
+ */
+ if (cap_protected) {
+ set_bit(S390_FEAT_UNPACK, model->features);
+ }
+
/* We emulate a zPCI bus and AEN, therefore we don't need HW support */
set_bit(S390_FEAT_ZPCI, model->features);
set_bit(S390_FEAT_ADAPTER_EVENT_NOTIFICATION, model->features);
diff --git a/target/s390x/kvm_s390x.h b/target/s390x/kvm_s390x.h
index 0b21789796..6ab17c81b7 100644
--- a/target/s390x/kvm_s390x.h
+++ b/target/s390x/kvm_s390x.h
@@ -19,10 +19,13 @@ void kvm_s390_vcpu_interrupt(S390CPU *cpu, struct kvm_s390_irq *irq);
void kvm_s390_access_exception(S390CPU *cpu, uint16_t code, uint64_t te_code);
int kvm_s390_mem_op(S390CPU *cpu, vaddr addr, uint8_t ar, void *hostbuf,
int len, bool is_write);
+int kvm_s390_mem_op_pv(S390CPU *cpu, vaddr addr, void *hostbuf, int len,
+ bool is_write);
void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code);
int kvm_s390_set_cpu_state(S390CPU *cpu, uint8_t cpu_state);
void kvm_s390_vcpu_interrupt_pre_save(S390CPU *cpu);
int kvm_s390_vcpu_interrupt_post_load(S390CPU *cpu);
+int kvm_s390_get_hpage_1m(void);
int kvm_s390_get_ri(void);
int kvm_s390_get_gs(void);
int kvm_s390_get_clock(uint8_t *tod_high, uint64_t *tod_clock);
diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c
index 0be2f300bb..7d9f3059cd 100644
--- a/target/s390x/mmu_helper.c
+++ b/target/s390x/mmu_helper.c
@@ -474,6 +474,20 @@ static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages,
return 0;
}
+int s390_cpu_pv_mem_rw(S390CPU *cpu, unsigned int offset, void *hostbuf,
+ int len, bool is_write)
+{
+ int ret;
+
+ if (kvm_enabled()) {
+ ret = kvm_s390_mem_op_pv(cpu, offset, hostbuf, len, is_write);
+ } else {
+ /* Protected Virtualization is a KVM/Hardware only feature */
+ g_assert_not_reached();
+ }
+ return ret;
+}
+
/**
* s390_cpu_virt_mem_rw:
* @laddr: the logical start address
diff --git a/tests/Makefile.include b/tests/Makefile.include
index 51de676298..03a74b60f6 100644
--- a/tests/Makefile.include
+++ b/tests/Makefile.include
@@ -439,6 +439,7 @@ tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
hw/core/fw-path-provider.o \
hw/core/reset.o \
hw/core/vmstate-if.o \
+ hw/core/clock.o hw/core/qdev-clock.o \
$(test-qapi-obj-y)
tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
migration/vmstate.o migration/vmstate-types.o migration/qemu-file.o \
diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py
index f825cd9ef5..c6b06a1a13 100644
--- a/tests/acceptance/boot_linux_console.py
+++ b/tests/acceptance/boot_linux_console.py
@@ -336,13 +336,13 @@ class BootLinuxConsole(Test):
"""
uboot_url = ('https://raw.githubusercontent.com/'
'Subbaraya-Sundeep/qemu-test-binaries/'
- 'fa030bd77a014a0b8e360d3b7011df89283a2f0b/u-boot')
- uboot_hash = 'abba5d9c24cdd2d49cdc2a8aa92976cf20737eff'
+ 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot')
+ uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2'
uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash)
spi_url = ('https://raw.githubusercontent.com/'
'Subbaraya-Sundeep/qemu-test-binaries/'
- 'fa030bd77a014a0b8e360d3b7011df89283a2f0b/spi.bin')
- spi_hash = '85f698329d38de63aea6e884a86fbde70890a78a'
+ 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin')
+ spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501'
spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash)
self.vm.set_console()
@@ -352,7 +352,12 @@ class BootLinuxConsole(Test):
'-drive', 'file=' + spi_path + ',if=mtd,format=raw',
'-no-reboot')
self.vm.launch()
- self.wait_for_console_pattern('init started: BusyBox')
+ self.wait_for_console_pattern('Enter \'help\' for a list')
+
+ exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15',
+ 'eth0: link becomes ready')
+ exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2',
+ '3 packets transmitted, 3 packets received, 0% packet loss')
def do_test_arm_raspi2(self, uart_id):
"""
diff --git a/tests/check-qlist.c b/tests/check-qlist.c
index ece83e293d..3cd0ccbf19 100644
--- a/tests/check-qlist.c
+++ b/tests/check-qlist.c
@@ -61,40 +61,31 @@ static void qobject_to_qlist_test(void)
qobject_unref(qlist);
}
-static int iter_called;
-static const int iter_max = 42;
-
-static void iter_func(QObject *obj, void *opaque)
-{
- QNum *qi;
- int64_t val;
-
- g_assert(opaque == NULL);
-
- qi = qobject_to(QNum, obj);
- g_assert(qi != NULL);
-
- g_assert(qnum_get_try_int(qi, &val));
- g_assert_cmpint(val, >=, 0);
- g_assert_cmpint(val, <=, iter_max);
-
- iter_called++;
-}
-
static void qlist_iter_test(void)
{
+ const int iter_max = 42;
int i;
QList *qlist;
+ QListEntry *entry;
+ QNum *qi;
+ int64_t val;
qlist = qlist_new();
for (i = 0; i < iter_max; i++)
qlist_append_int(qlist, i);
- iter_called = 0;
- qlist_iter(qlist, iter_func, NULL);
+ i = 0;
+ QLIST_FOREACH_ENTRY(qlist, entry) {
+ qi = qobject_to(QNum, qlist_entry_obj(entry));
+ g_assert(qi != NULL);
+
+ g_assert(qnum_get_try_int(qi, &val));
+ g_assert_cmpint(val, ==, i);
+ i++;
+ }
- g_assert(iter_called == iter_max);
+ g_assert(i == iter_max);
qobject_unref(qlist);
}
diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c
index d7761ebf84..1c856d9bd2 100644
--- a/tests/test-qobject-output-visitor.c
+++ b/tests/test-qobject-output-visitor.c
@@ -141,21 +141,6 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
}
}
-static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
- const void *unused)
-{
- EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 };
-
- for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
- Error *err = NULL;
-
- visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
- error_free_or_abort(&err);
- visitor_reset(data);
- }
-}
-
-
static void test_visitor_out_struct(TestOutputVisitorData *data,
const void *unused)
{
@@ -234,26 +219,6 @@ static void test_visitor_out_struct_nested(TestOutputVisitorData *data,
qapi_free_UserDefTwo(ud2);
}
-static void test_visitor_out_struct_errors(TestOutputVisitorData *data,
- const void *unused)
-{
- EnumOne bad_values[] = { ENUM_ONE__MAX, -1 };
- UserDefOne u = {0};
- UserDefOne *pu = &u;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
- Error *err = NULL;
-
- u.has_enum1 = true;
- u.enum1 = bad_values[i];
- visit_type_UserDefOne(data->ov, "unused", &pu, &err);
- error_free_or_abort(&err);
- visitor_reset(data);
- }
-}
-
-
static void test_visitor_out_list(TestOutputVisitorData *data,
const void *unused)
{
@@ -821,14 +786,10 @@ int main(int argc, char **argv)
&out_visitor_data, test_visitor_out_no_string);
output_visitor_test_add("/visitor/output/enum",
&out_visitor_data, test_visitor_out_enum);
- output_visitor_test_add("/visitor/output/enum-errors",
- &out_visitor_data, test_visitor_out_enum_errors);
output_visitor_test_add("/visitor/output/struct",
&out_visitor_data, test_visitor_out_struct);
output_visitor_test_add("/visitor/output/struct-nested",
&out_visitor_data, test_visitor_out_struct_nested);
- output_visitor_test_add("/visitor/output/struct-errors",
- &out_visitor_data, test_visitor_out_struct_errors);
output_visitor_test_add("/visitor/output/list",
&out_visitor_data, test_visitor_out_list);
output_visitor_test_add("/visitor/output/any",
diff --git a/tests/test-string-output-visitor.c b/tests/test-string-output-visitor.c
index 1be1540767..3bd732222c 100644
--- a/tests/test-string-output-visitor.c
+++ b/tests/test-string-output-visitor.c
@@ -203,19 +203,6 @@ static void test_visitor_out_enum(TestOutputVisitorData *data,
}
}
-static void test_visitor_out_enum_errors(TestOutputVisitorData *data,
- const void *unused)
-{
- EnumOne i, bad_values[] = { ENUM_ONE__MAX, -1 };
-
- for (i = 0; i < ARRAY_SIZE(bad_values) ; i++) {
- Error *err = NULL;
-
- visit_type_EnumOne(data->ov, "unused", &bad_values[i], &err);
- error_free_or_abort(&err);
- }
-}
-
static void
output_visitor_test_add(const char *testpath,
TestOutputVisitorData *data,
@@ -260,12 +247,6 @@ int main(int argc, char **argv)
&out_visitor_data, test_visitor_out_enum, false);
output_visitor_test_add("/string-visitor/output/enum-human",
&out_visitor_data, test_visitor_out_enum, true);
- output_visitor_test_add("/string-visitor/output/enum-errors",
- &out_visitor_data, test_visitor_out_enum_errors,
- false);
- output_visitor_test_add("/string-visitor/output/enum-errors-human",
- &out_visitor_data, test_visitor_out_enum_errors,
- true);
output_visitor_test_add("/string-visitor/output/intList",
&out_visitor_data, test_visitor_out_intList, false);
output_visitor_test_add("/string-visitor/output/intList-human",
diff --git a/util/qemu-option.c b/util/qemu-option.c
index 9542988183..0ebfd97a98 100644
--- a/util/qemu-option.c
+++ b/util/qemu-option.c
@@ -965,18 +965,16 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params,
assert(opts);
}
-typedef struct OptsFromQDictState {
- QemuOpts *opts;
- Error **errp;
-} OptsFromQDictState;
-
-static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
+static void qemu_opts_from_qdict_entry(QemuOpts *opts,
+ const QDictEntry *entry,
+ Error **errp)
{
- OptsFromQDictState *state = opaque;
+ const char *key = qdict_entry_key(entry);
+ QObject *obj = qdict_entry_value(entry);
char buf[32], *tmp = NULL;
const char *value;
- if (!strcmp(key, "id") || *state->errp) {
+ if (!strcmp(key, "id")) {
return;
}
@@ -997,7 +995,7 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
return;
}
- qemu_opt_set(state->opts, key, value, state->errp);
+ qemu_opt_set(opts, key, value, errp);
g_free(tmp);
}
@@ -1010,9 +1008,9 @@ static void qemu_opts_from_qdict_1(const char *key, QObject *obj, void *opaque)
QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
Error **errp)
{
- OptsFromQDictState state;
Error *local_err = NULL;
QemuOpts *opts;
+ const QDictEntry *entry;
opts = qemu_opts_create(list, qdict_get_try_str(qdict, "id"), 1,
&local_err);
@@ -1023,13 +1021,15 @@ QemuOpts *qemu_opts_from_qdict(QemuOptsList *list, const QDict *qdict,
assert(opts != NULL);
- state.errp = &local_err;
- state.opts = opts;
- qdict_iter(qdict, qemu_opts_from_qdict_1, &state);
- if (local_err) {
- error_propagate(errp, local_err);
- qemu_opts_del(opts);
- return NULL;
+ for (entry = qdict_first(qdict);
+ entry;
+ entry = qdict_next(qdict, entry)) {
+ qemu_opts_from_qdict_entry(opts, entry, &local_err);
+ if (local_err) {
+ error_propagate(errp, local_err);
+ qemu_opts_del(opts);
+ return NULL;
+ }
}
return opts;
@@ -1048,21 +1048,16 @@ void qemu_opts_absorb_qdict(QemuOpts *opts, QDict *qdict, Error **errp)
while (entry != NULL) {
Error *local_err = NULL;
- OptsFromQDictState state = {
- .errp = &local_err,
- .opts = opts,
- };
next = qdict_next(qdict, entry);
if (find_desc_by_name(opts->list->desc, entry->key)) {
- qemu_opts_from_qdict_1(entry->key, entry->value, &state);
+ qemu_opts_from_qdict_entry(opts, entry, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
- } else {
- qdict_del(qdict, entry->key);
}
+ qdict_del(qdict, entry->key);
}
entry = next;