diff options
136 files changed, 4101 insertions, 1344 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 018ed62560..8e8a7d5be5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -687,7 +687,7 @@ M: Peter Maydell <peter.maydell@linaro.org> L: qemu-arm@nongnu.org S: Maintained F: hw/intc/arm* -F: hw/intc/gic_internal.h +F: hw/intc/gic*_internal.h F: hw/misc/a9scu.c F: hw/misc/arm11scu.c F: hw/misc/arm_l2x0.c @@ -1283,6 +1283,7 @@ F: include/hw/char/goldfish_tty.h F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h +F: docs/specs/virt-ctlr.rst MicroBlaze Machines ------------------- @@ -1882,6 +1883,7 @@ EDU M: Jiri Slaby <jslaby@suse.cz> S: Maintained F: hw/misc/edu.c +F: docs/specs/edu.rst IDE M: John Snow <jsnow@redhat.com> @@ -2350,6 +2352,7 @@ S: Maintained F: hw/net/vmxnet* F: hw/scsi/vmw_pvscsi* F: tests/qtest/vmxnet3-test.c +F: docs/specs/vwm_pvscsi-spec.rst Rocker M: Jiri Pirko <jiri@resnulli.us> @@ -2434,7 +2437,7 @@ S: Orphan R: Ani Sinha <ani@anisinha.ca> F: hw/acpi/vmgenid.c F: include/hw/acpi/vmgenid.h -F: docs/specs/vmgenid.txt +F: docs/specs/vmgenid.rst F: tests/qtest/vmgenid-test.c LED @@ -2466,6 +2469,7 @@ F: hw/display/vga* F: hw/display/bochs-display.c F: include/hw/display/vga.h F: include/hw/display/bochs-vbe.h +F: docs/specs/standard-vga.rst ramfb M: Gerd Hoffmann <kraxel@redhat.com> @@ -2880,6 +2884,7 @@ F: include/sysemu/dump.h F: qapi/dump.json F: scripts/dump-guest-memory.py F: stubs/dump.c +F: docs/specs/vmcoreinfo.rst Error reporting M: Markus Armbruster <armbru@redhat.com> diff --git a/audio/audio.c b/audio/audio.c index e9815d6812..f91e05b72c 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1781,7 +1781,7 @@ static AudioState *audio_init(Audiodev *dev, Error **errp) QTAILQ_INSERT_TAIL(&audio_states, s, list); QLIST_INIT (&s->card_head); - vmstate_register (NULL, 0, &vmstate_audio, s); + vmstate_register_any(NULL, &vmstate_audio, s); return s; out: diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index 57369ec0f2..a9d8cb0acd 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -426,8 +426,7 @@ dbus_vmstate_complete(UserCreatable *uc, Error **errp) return; } - if (vmstate_register(VMSTATE_IF(self), VMSTATE_INSTANCE_ID_ANY, - &dbus_vmstate, self) < 0) { + if (vmstate_register_any(VMSTATE_IF(self), &dbus_vmstate, self) < 0) { error_setg(errp, "Failed to register vmstate"); } } diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index bf1a90f5d7..f7f1b4ad7a 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -975,8 +975,7 @@ static void tpm_emulator_inst_init(Object *obj) qemu_add_vm_change_state_handler(tpm_emulator_vm_state_change, tpm_emu); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_tpm_emulator, obj); + vmstate_register_any(NULL, &vmstate_tpm_emulator, obj); } /* diff --git a/block/parallels.c b/block/parallels.c index 1d695ce7fb..6318dd02e7 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -1369,7 +1369,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/qcow.c b/block/qcow.c index fdd4c83948..eab68e387c 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -307,7 +307,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vdi.c b/block/vdi.c index fd7e365383..c647d72895 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -498,7 +498,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail_free_bmap; } diff --git a/block/vhdx.c b/block/vhdx.c index e37f8c0926..a9d08742f9 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -1096,7 +1096,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vhdx format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vmdk.c b/block/vmdk.c index 1335d39e16..85864b8045 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1386,7 +1386,7 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, error_setg(&s->migration_blocker, "The vmdk format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vpc.c b/block/vpc.c index c30cf8689a..aa1a48ae0e 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -452,7 +452,7 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs)); bdrv_graph_rdunlock_main_loop(); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/block/vvfat.c b/block/vvfat.c index 266e036dcd..9d050ba3ae 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -1268,7 +1268,7 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, "The vvfat (rw) format used by node '%s' " "does not support live migration", bdrv_get_device_or_node_name(bs)); - ret = migrate_add_blocker(&s->migration_blocker, errp); + ret = migrate_add_blocker_normal(&s->migration_blocker, errp); if (ret < 0) { goto fail; } diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index be913630c3..240eb16d90 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -167,13 +167,17 @@ An example (from hw/input/pckbd.c) } }; -We are declaring the state with name "pckbd". -The ``version_id`` is 3, and the fields are 4 uint8_t in a KBDState structure. -We registered this with: +We are declaring the state with name "pckbd". The ``version_id`` is +3, and there are 4 uint8_t fields in the KBDState structure. We +registered this ``VMSTATEDescription`` with one of the following +functions. The first one will generate a device ``instance_id`` +different for each registration. Use the second one if you already +have an id that is different for each instance of the device: .. code:: c - vmstate_register(NULL, 0, &vmstate_kbd, s); + vmstate_register_any(NULL, &vmstate_kbd, s); + vmstate_register(NULL, instance_id, &vmstate_kbd, s); For devices that are ``qdev`` based, we can register the device in the class init function: diff --git a/docs/specs/edu.txt b/docs/specs/edu.rst index 0876310809..ae72737dbb 100644 --- a/docs/specs/edu.txt +++ b/docs/specs/edu.rst @@ -2,9 +2,10 @@ EDU device ========== -Copyright (c) 2014-2015 Jiri Slaby +.. + Copyright (c) 2014-2015 Jiri Slaby -This document is licensed under the GPLv2 (or later). + This document is licensed under the GPLv2 (or later). This is an educational device for writing (kernel) drivers. Its original intention was to support the Linux kernel lectures taught at the Masaryk @@ -15,10 +16,11 @@ The devices behaves very similar to the PCI bridge present in the COMBO6 cards developed under the Liberouter wings. Both PCI device ID and PCI space is inherited from that device. -Command line switches: - -device edu[,dma_mask=mask] +Command line switches +--------------------- - dma_mask makes the virtual device work with DMA addresses with the given +``-device edu[,dma_mask=mask]`` + ``dma_mask`` makes the virtual device work with DMA addresses with the given mask. For educational purposes, the device supports only 28 bits (256 MiB) by default. Students shall set dma_mask for the device in the OS driver properly. @@ -26,7 +28,8 @@ Command line switches: PCI specs --------- -PCI ID: 1234:11e8 +PCI ID: + ``1234:11e8`` PCI Region 0: I/O memory, 1 MB in size. Users are supposed to communicate with the card @@ -35,24 +38,29 @@ PCI Region 0: MMIO area spec -------------- -Only size == 4 accesses are allowed for addresses < 0x80. size == 4 or -size == 8 for the rest. +Only ``size == 4`` accesses are allowed for addresses ``< 0x80``. +``size == 4`` or ``size == 8`` for the rest. -0x00 (RO) : identification (0xRRrr00edu) - RR -- major version - rr -- minor version +0x00 (RO) : identification + Value is in the form ``0xRRrr00edu`` where: + - ``RR`` -- major version + - ``rr`` -- minor version 0x04 (RW) : card liveness check - It is a simple value inversion (~ C operator). + It is a simple value inversion (``~`` C operator). 0x08 (RW) : factorial computation The stored value is taken and factorial of it is put back here. This happens only after factorial bit in the status register (0x20 below) is cleared. -0x20 (RW) : status register, bitwise OR - 0x01 -- computing factorial (RO) - 0x80 -- raise interrupt after finishing factorial computation +0x20 (RW) : status register + Bitwise OR of: + + 0x01 + computing factorial (RO) + 0x80 + raise interrupt after finishing factorial computation 0x24 (RO) : interrupt status register It contains values which raised the interrupt (see interrupt raise @@ -76,13 +84,19 @@ size == 8 for the rest. 0x90 (RW) : DMA transfer count The size of the area to perform the DMA on. -0x98 (RW) : DMA command register, bitwise OR - 0x01 -- start transfer - 0x02 -- direction (0: from RAM to EDU, 1: from EDU to RAM) - 0x04 -- raise interrupt 0x100 after finishing the DMA +0x98 (RW) : DMA command register + Bitwise OR of: + + 0x01 + start transfer + 0x02 + direction (0: from RAM to EDU, 1: from EDU to RAM) + 0x04 + raise interrupt 0x100 after finishing the DMA IRQ controller -------------- + An IRQ is generated when written to the interrupt raise register. The value appears in interrupt status register when the interrupt is raised and has to be written to the interrupt acknowledge register to lower it. @@ -94,22 +108,28 @@ routine. DMA controller -------------- + One has to specify, source, destination, size, and start the transfer. One 4096 bytes long buffer at offset 0x40000 is available in the EDU device. I.e. one can perform DMA to/from this space when programmed properly. Example of transferring a 100 byte block to and from the buffer using a given -PCI address 'addr': -addr -> DMA source address -0x40000 -> DMA destination address -100 -> DMA transfer count -1 -> DMA command register -while (DMA command register & 1) - ; - -0x40000 -> DMA source address -addr+100 -> DMA destination address -100 -> DMA transfer count -3 -> DMA command register -while (DMA command register & 1) - ; +PCI address ``addr``: + +:: + + addr -> DMA source address + 0x40000 -> DMA destination address + 100 -> DMA transfer count + 1 -> DMA command register + while (DMA command register & 1) + ; + +:: + + 0x40000 -> DMA source address + addr+100 -> DMA destination address + 100 -> DMA transfer count + 3 -> DMA command register + while (DMA command register & 1) + ; diff --git a/docs/specs/index.rst b/docs/specs/index.rst index e58be38c41..b3f482b0aa 100644 --- a/docs/specs/index.rst +++ b/docs/specs/index.rst @@ -24,3 +24,11 @@ guest hardware that is specific to QEMU. acpi_erst sev-guest-firmware fw_cfg + vmw_pvscsi-spec + edu + ivshmem-spec + pvpanic + standard-vga + virt-ctlr + vmcoreinfo + vmgenid diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.rst index 1beb3a01ec..2d8e80055b 100644 --- a/docs/specs/ivshmem-spec.txt +++ b/docs/specs/ivshmem-spec.rst @@ -1,4 +1,6 @@ -= Device Specification for Inter-VM shared memory device = +====================================================== +Device Specification for Inter-VM shared memory device +====================================================== The Inter-VM shared memory device (ivshmem) is designed to share a memory region between multiple QEMU processes running different guests @@ -12,42 +14,17 @@ can obtain one from an ivshmem server. In the latter case, the device can additionally interrupt its peers, and get interrupted by its peers. +For information on configuring the ivshmem device on the QEMU +command line, see :doc:`../system/devices/ivshmem`. -== Configuring the ivshmem PCI device == - -There are two basic configurations: - -- Just shared memory: - - -device ivshmem-plain,memdev=HMB,... - - This uses host memory backend HMB. It should have option "share" - set. - -- Shared memory plus interrupts: - - -device ivshmem-doorbell,chardev=CHR,vectors=N,... - - An ivshmem server must already be running on the host. The device - connects to the server's UNIX domain socket via character device - CHR. - - Each peer gets assigned a unique ID by the server. IDs must be - between 0 and 65535. - - Interrupts are message-signaled (MSI-X). vectors=N configures the - number of vectors to use. - -For more details on ivshmem device properties, see the QEMU Emulator -user documentation. - - -== The ivshmem PCI device's guest interface == +The ivshmem PCI device's guest interface +======================================== The device has vendor ID 1af4, device ID 1110, revision 1. Before QEMU 2.6.0, it had revision 0. -=== PCI BARs === +PCI BARs +-------- The ivshmem PCI device has two or three BARs: @@ -59,8 +36,7 @@ There are two ways to use this device: - If you only need the shared memory part, BAR2 suffices. This way, you have access to the shared memory in the guest and can use it as - you see fit. Memnic, for example, uses ivshmem this way from guest - user space (see http://dpdk.org/browse/memnic). + you see fit. - If you additionally need the capability for peers to interrupt each other, you need BAR0 and BAR1. You will most likely want to write a @@ -77,10 +53,13 @@ accessing BAR2. Revision 0 of the device is not capable to tell guest software whether it is configured for interrupts. -=== PCI device registers === +PCI device registers +-------------------- BAR 0 contains the following registers: +:: + Offset Size Access On reset Function 0 4 read/write 0 Interrupt Mask bit 0: peer interrupt (rev 0) @@ -145,18 +124,20 @@ With multiple MSI-X vectors, different vectors can be used to indicate different events have occurred. The semantics of interrupt vectors are left to the application. - -== Interrupt infrastructure == +Interrupt infrastructure +======================== When configured for interrupts, the peers share eventfd objects in addition to shared memory. The shared resources are managed by an ivshmem server. -=== The ivshmem server === +The ivshmem server +------------------ The server listens on a UNIX domain socket. For each new client that connects to the server, the server + - picks an ID, - creates eventfd file descriptors for the interrupt vectors, - sends the ID and the file descriptor for the shared memory to the @@ -189,7 +170,8 @@ vectors. A standalone client is in contrib/ivshmem-client/. It can be useful for debugging. -=== The ivshmem Client-Server Protocol === +The ivshmem Client-Server Protocol +---------------------------------- An ivshmem device configured for interrupts connects to an ivshmem server. This section details the protocol between the two. @@ -245,7 +227,8 @@ Known bugs: * The protocol is poorly designed. -=== The ivshmem Client-Client Protocol === +The ivshmem Client-Client Protocol +---------------------------------- An ivshmem device configured for interrupts receives eventfd file descriptors for interrupting peers and getting interrupted by peers diff --git a/docs/specs/pci-ids.rst b/docs/specs/pci-ids.rst index d6707fa069..c0a3dec2e7 100644 --- a/docs/specs/pci-ids.rst +++ b/docs/specs/pci-ids.rst @@ -50,7 +50,7 @@ maintained as part of the virtio specification. by QEMU. 1af4:1110 - ivshmem device (shared memory, ``docs/specs/ivshmem-spec.txt``) + ivshmem device (:doc:`ivshmem-spec`) All other device IDs are reserved. diff --git a/docs/specs/pvpanic.txt b/docs/specs/pvpanic.rst index 8afcde11cc..f894bc1955 100644 --- a/docs/specs/pvpanic.txt +++ b/docs/specs/pvpanic.rst @@ -21,18 +21,21 @@ recognize. On write, the bits not recognized by the device are ignored. Software should set only bits both itself and the device recognize. Bit Definition --------------- -bit 0: a guest panic has happened and should be processed by the host -bit 1: a guest panic has happened and will be handled by the guest; - the host should record it or report it, but should not affect - the execution of the guest. +~~~~~~~~~~~~~~ + +bit 0 + a guest panic has happened and should be processed by the host +bit 1 + a guest panic has happened and will be handled by the guest; + the host should record it or report it, but should not affect + the execution of the guest. PCI Interface ------------- The PCI interface is similar to the ISA interface except that it uses an MMIO address space provided by its BAR0, 1 byte long. Any machine with a PCI bus -can enable a pvpanic device by adding '-device pvpanic-pci' to the command +can enable a pvpanic device by adding ``-device pvpanic-pci`` to the command line. ACPI Interface @@ -40,15 +43,25 @@ ACPI Interface pvpanic device is defined with ACPI ID "QEMU0001". Custom methods: -RDPT: To determine whether guest panic notification is supported. -Arguments: None -Return: Returns a byte, with the same semantics as the I/O port - interface. +RDPT +~~~~ + +To determine whether guest panic notification is supported. + +Arguments + None +Return + Returns a byte, with the same semantics as the I/O port interface. + +WRPT +~~~~ + +To send a guest panic event. -WRPT: To send a guest panic event -Arguments: Arg0 is a byte to be written, with the same semantics as - the I/O interface. -Return: None +Arguments + Arg0 is a byte to be written, with the same semantics as the I/O interface. +Return + None The ACPI device will automatically refer to the right port in case it is modified. diff --git a/docs/specs/standard-vga.rst b/docs/specs/standard-vga.rst new file mode 100644 index 0000000000..992f429ced --- /dev/null +++ b/docs/specs/standard-vga.rst @@ -0,0 +1,94 @@ + +QEMU Standard VGA +================= + +Exists in two variants, for isa and pci. + +command line switches: + +``-vga std`` + picks isa for -M isapc, otherwise pci +``-device VGA`` + pci variant +``-device isa-vga`` + isa variant +``-device secondary-vga`` + legacy-free pci variant + + +PCI spec +-------- + +Applies to the pci variant only for obvious reasons. + +PCI ID + ``1234:1111`` + +PCI Region 0 + Framebuffer memory, 16 MB in size (by default). + Size is tunable via vga_mem_mb property. + +PCI Region 1 + Reserved (so we have the option to make the framebuffer bar 64bit). + +PCI Region 2 + MMIO bar, 4096 bytes in size (QEMU 1.3+) + +PCI ROM Region + Holds the vgabios (QEMU 0.14+). + + +The legacy-free variant has no ROM and has ``PCI_CLASS_DISPLAY_OTHER`` +instead of ``PCI_CLASS_DISPLAY_VGA``. + + +IO ports used +------------- + +Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. + +``03c0 - 03df`` + standard vga ports +``01ce`` + bochs vbe interface index port +``01cf`` + bochs vbe interface data port (x86 only) +``01d0`` + bochs vbe interface data port + + +Memory regions used +------------------- + +``0xe0000000`` + Framebuffer memory, isa variant only. + +The pci variant used to mirror the framebuffer bar here, QEMU 0.14+ +stops doing that (except when in ``-M pc-$old`` compat mode). + + +MMIO area spec +-------------- + +Likewise applies to the pci variant only for obvious reasons. + +``0000 - 03ff`` + edid data blob. +``0400 - 041f`` + vga ioports (``0x3c0`` to ``0x3df``), remapped 1:1. Word access + is supported, bytes are written in little endian order (aka index + port first), so indexed registers can be updated with a single + mmio write (and thus only one vmexit). +``0500 - 0515`` + bochs dispi interface registers, mapped flat without index/data ports. + Use ``(index << 1)`` as offset for (16bit) register access. +``0600 - 0607`` + QEMU extended registers. QEMU 2.2+ only. + The pci revision is 2 (or greater) when these registers are present. + The registers are 32bit. +``0600`` + QEMU extended register region size, in bytes. +``0604`` + framebuffer endianness register. + - ``0xbebebebe`` indicates big endian. + - ``0x1e1e1e1e`` indicates little endian. diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt deleted file mode 100644 index 18f75f1b30..0000000000 --- a/docs/specs/standard-vga.txt +++ /dev/null @@ -1,81 +0,0 @@ - -QEMU Standard VGA -================= - -Exists in two variants, for isa and pci. - -command line switches: - -vga std [ picks isa for -M isapc, otherwise pci ] - -device VGA [ pci variant ] - -device isa-vga [ isa variant ] - -device secondary-vga [ legacy-free pci variant ] - - -PCI spec --------- - -Applies to the pci variant only for obvious reasons. - -PCI ID: 1234:1111 - -PCI Region 0: - Framebuffer memory, 16 MB in size (by default). - Size is tunable via vga_mem_mb property. - -PCI Region 1: - Reserved (so we have the option to make the framebuffer bar 64bit). - -PCI Region 2: - MMIO bar, 4096 bytes in size (qemu 1.3+) - -PCI ROM Region: - Holds the vgabios (qemu 0.14+). - - -The legacy-free variant has no ROM and has PCI_CLASS_DISPLAY_OTHER -instead of PCI_CLASS_DISPLAY_VGA. - - -IO ports used -------------- - -Doesn't apply to the legacy-free pci variant, use the MMIO bar instead. - -03c0 - 03df : standard vga ports -01ce : bochs vbe interface index port -01cf : bochs vbe interface data port (x86 only) -01d0 : bochs vbe interface data port - - -Memory regions used -------------------- - -0xe0000000 : Framebuffer memory, isa variant only. - -The pci variant used to mirror the framebuffer bar here, qemu 0.14+ -stops doing that (except when in -M pc-$old compat mode). - - -MMIO area spec --------------- - -Likewise applies to the pci variant only for obvious reasons. - -0000 - 03ff : edid data blob. -0400 - 041f : vga ioports (0x3c0 -> 0x3df), remapped 1:1. - word access is supported, bytes are written - in little endia order (aka index port first), - so indexed registers can be updated with a - single mmio write (and thus only one vmexit). -0500 - 0515 : bochs dispi interface registers, mapped flat - without index/data ports. Use (index << 1) - as offset for (16bit) register access. - -0600 - 0607 : qemu extended registers. qemu 2.2+ only. - The pci revision is 2 (or greater) when - these registers are present. The registers - are 32bit. - 0600 : qemu extended register region size, in bytes. - 0604 : framebuffer endianness register. - - 0xbebebebe indicates big endian. - - 0x1e1e1e1e indicates little endian. diff --git a/docs/specs/virt-ctlr.txt b/docs/specs/virt-ctlr.rst index 24d38084f7..ad3edde82d 100644 --- a/docs/specs/virt-ctlr.txt +++ b/docs/specs/virt-ctlr.rst @@ -1,9 +1,9 @@ Virtual System Controller ========================= -This device is a simple interface defined for the pure virtual machine with no -hardware reference implementation to allow the guest kernel to send command -to the host hypervisor. +The ``virt-ctrl`` device is a simple interface defined for the pure +virtual machine with no hardware reference implementation to allow the +guest kernel to send command to the host hypervisor. The specification can evolve, the current state is defined as below. @@ -11,14 +11,12 @@ This is a MMIO mapped device using 256 bytes. Two 32bit registers are defined: -1- the features register (read-only, address 0x00) - +the features register (read-only, address 0x00) This register allows the device to report features supported by the controller. The only feature supported for the moment is power control (0x01). -2- the command register (write-only, address 0x04) - +the command register (write-only, address 0x04) This register allows the kernel to send the commands to the hypervisor. The implemented commands are part of the power control feature and are reset (1), halt (2) and panic (3). diff --git a/docs/specs/vmcoreinfo.rst b/docs/specs/vmcoreinfo.rst new file mode 100644 index 0000000000..6541aa116f --- /dev/null +++ b/docs/specs/vmcoreinfo.rst @@ -0,0 +1,54 @@ +================= +VMCoreInfo device +================= + +The ``-device vmcoreinfo`` will create a ``fw_cfg`` entry for a guest to +store dump details. + +``etc/vmcoreinfo`` +================== + +A guest may use this ``fw_cfg`` entry to add information details to QEMU +dumps. + +The entry of 16 bytes has the following layout, in little-endian:: + + #define VMCOREINFO_FORMAT_NONE 0x0 + #define VMCOREINFO_FORMAT_ELF 0x1 + + struct FWCfgVMCoreInfo { + uint16_t host_format; /* formats host supports */ + uint16_t guest_format; /* format guest supplies */ + uint32_t size; /* size of vmcoreinfo region */ + uint64_t paddr; /* physical address of vmcoreinfo region */ + }; + +Only full write (of 16 bytes) are considered valid for further +processing of entry values. + +A write of 0 in ``guest_format`` will disable further processing of +vmcoreinfo entry values & content. + +You may write a ``guest_format`` that is not supported by the host, in +which case the entry data can be ignored by QEMU (but you may still +access it through a debugger, via ``vmcoreinfo_realize::vmcoreinfo_state``). + +Format & content +================ + +As of QEMU 2.11, only ``VMCOREINFO_FORMAT_ELF`` is supported. + +The entry gives location and size of an ELF note that is appended in +qemu dumps. + +The note format/class must be of the target bitness and the size must +be less than 1Mb. + +If the ELF note name is ``VMCOREINFO``, it is expected to be the Linux +vmcoreinfo note (see `the kernel documentation for its format +<https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-kernel-vmcoreinfo>`_). +In this case, qemu dump code will read the content +as a key=value text file, looking for ``NUMBER(phys_base)`` key +value. The value is expected to be more accurate than architecture +guess of the value. This is useful for KASLR-enabled guest with +ancient tools not handling the ``VMCOREINFO`` note. diff --git a/docs/specs/vmcoreinfo.txt b/docs/specs/vmcoreinfo.txt deleted file mode 100644 index bcbca6fe47..0000000000 --- a/docs/specs/vmcoreinfo.txt +++ /dev/null @@ -1,53 +0,0 @@ -================= -VMCoreInfo device -================= - -The `-device vmcoreinfo` will create a fw_cfg entry for a guest to -store dump details. - -etc/vmcoreinfo -************** - -A guest may use this fw_cfg entry to add information details to qemu -dumps. - -The entry of 16 bytes has the following layout, in little-endian:: - -#define VMCOREINFO_FORMAT_NONE 0x0 -#define VMCOREINFO_FORMAT_ELF 0x1 - - struct FWCfgVMCoreInfo { - uint16_t host_format; /* formats host supports */ - uint16_t guest_format; /* format guest supplies */ - uint32_t size; /* size of vmcoreinfo region */ - uint64_t paddr; /* physical address of vmcoreinfo region */ - }; - -Only full write (of 16 bytes) are considered valid for further -processing of entry values. - -A write of 0 in guest_format will disable further processing of -vmcoreinfo entry values & content. - -You may write a guest_format that is not supported by the host, in -which case the entry data can be ignored by qemu (but you may still -access it through a debugger, via vmcoreinfo_realize::vmcoreinfo_state). - -Format & content -**************** - -As of qemu 2.11, only VMCOREINFO_FORMAT_ELF is supported. - -The entry gives location and size of an ELF note that is appended in -qemu dumps. - -The note format/class must be of the target bitness and the size must -be less than 1Mb. - -If the ELF note name is "VMCOREINFO", it is expected to be the Linux -vmcoreinfo note (see Documentation/ABI/testing/sysfs-kernel-vmcoreinfo -in Linux source). In this case, qemu dump code will read the content -as a key=value text file, looking for "NUMBER(phys_base)" key -value. The value is expected to be more accurate than architecture -guess of the value. This is useful for KASLR-enabled guest with -ancient tools not handling the VMCOREINFO note. diff --git a/docs/specs/vmgenid.rst b/docs/specs/vmgenid.rst new file mode 100644 index 0000000000..9a3cefcd82 --- /dev/null +++ b/docs/specs/vmgenid.rst @@ -0,0 +1,246 @@ +Virtual Machine Generation ID Device +==================================== + +.. + Copyright (C) 2016 Red Hat, Inc. + Copyright (C) 2017 Skyport Systems, Inc. + + This work is licensed under the terms of the GNU GPL, version 2 or later. + See the COPYING file in the top-level directory. + +The VM generation ID (``vmgenid``) device is an emulated device which +exposes a 128-bit, cryptographically random, integer value identifier, +referred to as a Globally Unique Identifier, or GUID. + +This allows management applications (e.g. libvirt) to notify the guest +operating system when the virtual machine is executed with a different +configuration (e.g. snapshot execution or creation from a template). The +guest operating system notices the change, and is then able to react as +appropriate by marking its copies of distributed databases as dirty, +re-initializing its random number generator etc. + + +Requirements +------------ + +These requirements are extracted from the "How to implement virtual machine +generation ID support in a virtualization platform" section of +`the Microsoft Virtual Machine Generation ID specification +<http://go.microsoft.com/fwlink/?LinkId=260709>`_ dated August 1, 2012. + +- **R1a** The generation ID shall live in an 8-byte aligned buffer. + +- **R1b** The buffer holding the generation ID shall be in guest RAM, + ROM, or device MMIO range. + +- **R1c** The buffer holding the generation ID shall be kept separate from + areas used by the operating system. + +- **R1d** The buffer shall not be covered by an AddressRangeMemory or + AddressRangeACPI entry in the E820 or UEFI memory map. + +- **R1e** The generation ID shall not live in a page frame that could be + mapped with caching disabled. (In other words, regardless of whether the + generation ID lives in RAM, ROM or MMIO, it shall only be mapped as + cacheable.) + +- **R2** to **R5** [These AML requirements are isolated well enough in the + Microsoft specification for us to simply refer to them here.] + +- **R6** The hypervisor shall expose a _HID (hardware identifier) object + in the VMGenId device's scope that is unique to the hypervisor vendor. + + +QEMU Implementation +------------------- + +The above-mentioned specification does not dictate which ACPI descriptor table +will contain the VM Generation ID device. Other implementations (Hyper-V and +Xen) put it in the main descriptor table (Differentiated System Description +Table or DSDT). For ease of debugging and implementation, we have decided to +put it in its own Secondary System Description Table, or SSDT. + +The following is a dump of the contents from a running system:: + + # iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT + + Intel ACPI Component Architecture + ASL+ Optimizing Compiler version 20150717-64 + Copyright (c) 2000 - 2015 Intel Corporation + + Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length + 00000198 (0x0000C6) + ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS VMGENID 00000001 BXPC 00000001) + Acpi table [SSDT] successfully installed and loaded + Pass 1 parse of [SSDT] + Pass 2 parse of [SSDT] + Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions) + + Parsing completed + Disassembly completed + ASL Output: ./SSDT.dsl - 1631 bytes + # cat SSDT.dsl + /* + * Intel ACPI Component Architecture + * AML/ASL+ Disassembler version 20150717-64 + * Copyright (c) 2000 - 2015 Intel Corporation + * + * Disassembling to symbolic ASL+ operators + * + * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb 5 00:19:37 2017 + * + * Original Table Header: + * Signature "SSDT" + * Length 0x000000CA (202) + * Revision 0x01 + * Checksum 0x4B + * OEM ID "BOCHS " + * OEM Table ID "VMGENID" + * OEM Revision 0x00000001 (1) + * Compiler ID "BXPC" + * Compiler Version 0x00000001 (1) + */ + DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ", "VMGENID", 0x00000001) + { + Name (VGIA, 0x07FFF000) + Scope (\_SB) + { + Device (VGEN) + { + Name (_HID, "QEMUVGID") // _HID: Hardware ID + Name (_CID, "VM_Gen_Counter") // _CID: Compatible ID + Name (_DDN, "VM_Gen_Counter") // _DDN: DOS Device Name + Method (_STA, 0, NotSerialized) // _STA: Status + { + Local0 = 0x0F + If ((VGIA == Zero)) + { + Local0 = Zero + } + + Return (Local0) + } + + Method (ADDR, 0, NotSerialized) + { + Local0 = Package (0x02) {} + Index (Local0, Zero) = (VGIA + 0x28) + Index (Local0, One) = Zero + Return (Local0) + } + } + } + + Method (\_GPE._E05, 0, NotSerialized) // _Exx: Edge-Triggered GPE + { + Notify (\_SB.VGEN, 0x80) // Status Change + } + } + + +Design Details: +--------------- + +Requirements R1a through R1e dictate that the memory holding the +VM Generation ID must be allocated and owned by the guest firmware, +in this case BIOS or UEFI. However, to be useful, QEMU must be able to +change the contents of the memory at runtime, specifically when starting a +backed-up or snapshotted image. In order to do this, QEMU must know the +address that has been allocated. + +The mechanism chosen for this memory sharing is writable fw_cfg blobs. +These are data object that are visible to both QEMU and guests, and are +addressable as sequential files. + +More information about fw_cfg can be found in :doc:`fw_cfg`. + +Two fw_cfg blobs are used in this case: + +``/etc/vmgenid_guid`` + +- contains the actual VM Generation ID GUID +- read-only to the guest + +``/etc/vmgenid_addr`` + +- contains the address of the downloaded vmgenid blob +- writable by the guest + + +QEMU sends the following commands to the guest at startup: + +1. Allocate memory for vmgenid_guid fw_cfg blob. +2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as + shown above in the iasl dump). Note that this change is not propagated + back to QEMU. +3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr + via the fw_cfg DMA interface. + +After step 3, QEMU is able to update the contents of vmgenid_guid at will. + +Since BIOS or UEFI does not necessarily run when we wish to change the GUID, +the value of VGIA is persisted via the VMState mechanism. + +As spelled out in the specification, any change to the GUID executes an +ACPI notification. The exact handler to use is not specified, so the vmgenid +device uses the first unused one: ``\_GPE._E05``. + + +Endian-ness Considerations: +--------------------------- + +Although not specified in Microsoft's document, it is assumed that the +device is expected to use little-endian format. + +All GUID passed in via command line or monitor are treated as big-endian. +GUID values displayed via monitor are shown in big-endian format. + + +GUID Storage Format: +-------------------- + +In order to implement an OVMF "SDT Header Probe Suppressor", the contents of +the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID. There is also +significant padding in order to align and fill a memory page, as shown in the +following diagram:: + + +----------------------------------+ + | SSDT with OEM Table ID = VMGENID | + +----------------------------------+ + | ... | TOP OF PAGE + | VGIA dword object ---------------|-----> +---------------------------+ + | ... | | fw-allocated array for | + | _STA method referring to VGIA | | "etc/vmgenid_guid" | + | ... | +---------------------------+ + | ADDR method referring to VGIA | | 0: OVMF SDT Header probe | + | ... | | suppressor | + +----------------------------------+ | 36: padding for 8-byte | + | alignment | + | 40: GUID | + | 56: padding to page size | + +---------------------------+ + END OF PAGE + + +Device Usage: +------------- + +The device has one property, which may be only be set using the command line: + +``guid`` + sets the value of the GUID. A special value ``auto`` instructs + QEMU to generate a new random GUID. + +For example:: + + QEMU -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" + QEMU -device vmgenid,guid=auto + +The property may be queried via QMP/HMP:: + + (QEMU) query-vm-generation-id + {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}} + +Setting of this parameter is intentionally left out from the QMP/HMP +interfaces. There are no known use cases for changing the GUID once QEMU is +running, and adding this capability would greatly increase the complexity. diff --git a/docs/specs/vmgenid.txt b/docs/specs/vmgenid.txt deleted file mode 100644 index 80ff69f31c..0000000000 --- a/docs/specs/vmgenid.txt +++ /dev/null @@ -1,245 +0,0 @@ -VIRTUAL MACHINE GENERATION ID -============================= - -Copyright (C) 2016 Red Hat, Inc. -Copyright (C) 2017 Skyport Systems, Inc. - -This work is licensed under the terms of the GNU GPL, version 2 or later. -See the COPYING file in the top-level directory. - -=== - -The VM generation ID (vmgenid) device is an emulated device which -exposes a 128-bit, cryptographically random, integer value identifier, -referred to as a Globally Unique Identifier, or GUID. - -This allows management applications (e.g. libvirt) to notify the guest -operating system when the virtual machine is executed with a different -configuration (e.g. snapshot execution or creation from a template). The -guest operating system notices the change, and is then able to react as -appropriate by marking its copies of distributed databases as dirty, -re-initializing its random number generator etc. - - -Requirements ------------- - -These requirements are extracted from the "How to implement virtual machine -generation ID support in a virtualization platform" section of the -specification, dated August 1, 2012. - - -The document may be found on the web at: - http://go.microsoft.com/fwlink/?LinkId=260709 - -R1a. The generation ID shall live in an 8-byte aligned buffer. - -R1b. The buffer holding the generation ID shall be in guest RAM, ROM, or device - MMIO range. - -R1c. The buffer holding the generation ID shall be kept separate from areas - used by the operating system. - -R1d. The buffer shall not be covered by an AddressRangeMemory or - AddressRangeACPI entry in the E820 or UEFI memory map. - -R1e. The generation ID shall not live in a page frame that could be mapped with - caching disabled. (In other words, regardless of whether the generation ID - lives in RAM, ROM or MMIO, it shall only be mapped as cacheable.) - -R2 to R5. [These AML requirements are isolated well enough in the Microsoft - specification for us to simply refer to them here.] - -R6. The hypervisor shall expose a _HID (hardware identifier) object in the - VMGenId device's scope that is unique to the hypervisor vendor. - - -QEMU Implementation -------------------- - -The above-mentioned specification does not dictate which ACPI descriptor table -will contain the VM Generation ID device. Other implementations (Hyper-V and -Xen) put it in the main descriptor table (Differentiated System Description -Table or DSDT). For ease of debugging and implementation, we have decided to -put it in its own Secondary System Description Table, or SSDT. - -The following is a dump of the contents from a running system: - -# iasl -p ./SSDT -d /sys/firmware/acpi/tables/SSDT - -Intel ACPI Component Architecture -ASL+ Optimizing Compiler version 20150717-64 -Copyright (c) 2000 - 2015 Intel Corporation - -Reading ACPI table from file /sys/firmware/acpi/tables/SSDT - Length -00000198 (0x0000C6) -ACPI: SSDT 0x0000000000000000 0000C6 (v01 BOCHS VMGENID 00000001 BXPC -00000001) -Acpi table [SSDT] successfully installed and loaded -Pass 1 parse of [SSDT] -Pass 2 parse of [SSDT] -Parsing Deferred Opcodes (Methods/Buffers/Packages/Regions) - -Parsing completed -Disassembly completed -ASL Output: ./SSDT.dsl - 1631 bytes -# cat SSDT.dsl -/* - * Intel ACPI Component Architecture - * AML/ASL+ Disassembler version 20150717-64 - * Copyright (c) 2000 - 2015 Intel Corporation - * - * Disassembling to symbolic ASL+ operators - * - * Disassembly of /sys/firmware/acpi/tables/SSDT, Sun Feb 5 00:19:37 2017 - * - * Original Table Header: - * Signature "SSDT" - * Length 0x000000CA (202) - * Revision 0x01 - * Checksum 0x4B - * OEM ID "BOCHS " - * OEM Table ID "VMGENID" - * OEM Revision 0x00000001 (1) - * Compiler ID "BXPC" - * Compiler Version 0x00000001 (1) - */ -DefinitionBlock ("/sys/firmware/acpi/tables/SSDT.aml", "SSDT", 1, "BOCHS ", -"VMGENID", 0x00000001) -{ - Name (VGIA, 0x07FFF000) - Scope (\_SB) - { - Device (VGEN) - { - Name (_HID, "QEMUVGID") // _HID: Hardware ID - Name (_CID, "VM_Gen_Counter") // _CID: Compatible ID - Name (_DDN, "VM_Gen_Counter") // _DDN: DOS Device Name - Method (_STA, 0, NotSerialized) // _STA: Status - { - Local0 = 0x0F - If ((VGIA == Zero)) - { - Local0 = Zero - } - - Return (Local0) - } - - Method (ADDR, 0, NotSerialized) - { - Local0 = Package (0x02) {} - Index (Local0, Zero) = (VGIA + 0x28) - Index (Local0, One) = Zero - Return (Local0) - } - } - } - - Method (\_GPE._E05, 0, NotSerialized) // _Exx: Edge-Triggered GPE - { - Notify (\_SB.VGEN, 0x80) // Status Change - } -} - - -Design Details: ---------------- - -Requirements R1a through R1e dictate that the memory holding the -VM Generation ID must be allocated and owned by the guest firmware, -in this case BIOS or UEFI. However, to be useful, QEMU must be able to -change the contents of the memory at runtime, specifically when starting a -backed-up or snapshotted image. In order to do this, QEMU must know the -address that has been allocated. - -The mechanism chosen for this memory sharing is writable fw_cfg blobs. -These are data object that are visible to both QEMU and guests, and are -addressable as sequential files. - -More information about fw_cfg can be found in "docs/specs/fw_cfg.txt" - -Two fw_cfg blobs are used in this case: - -/etc/vmgenid_guid - contains the actual VM Generation ID GUID - - read-only to the guest -/etc/vmgenid_addr - contains the address of the downloaded vmgenid blob - - writable by the guest - - -QEMU sends the following commands to the guest at startup: - -1. Allocate memory for vmgenid_guid fw_cfg blob. -2. Write the address of vmgenid_guid into the SSDT (VGIA ACPI variable as - shown above in the iasl dump). Note that this change is not propagated - back to QEMU. -3. Write the address of vmgenid_guid back to QEMU's copy of vmgenid_addr - via the fw_cfg DMA interface. - -After step 3, QEMU is able to update the contents of vmgenid_guid at will. - -Since BIOS or UEFI does not necessarily run when we wish to change the GUID, -the value of VGIA is persisted via the VMState mechanism. - -As spelled out in the specification, any change to the GUID executes an -ACPI notification. The exact handler to use is not specified, so the vmgenid -device uses the first unused one: \_GPE._E05. - - -Endian-ness Considerations: ---------------------------- - -Although not specified in Microsoft's document, it is assumed that the -device is expected to use little-endian format. - -All GUID passed in via command line or monitor are treated as big-endian. -GUID values displayed via monitor are shown in big-endian format. - - -GUID Storage Format: --------------------- - -In order to implement an OVMF "SDT Header Probe Suppressor", the contents of -the vmgenid_guid fw_cfg blob are not simply a 128-bit GUID. There is also -significant padding in order to align and fill a memory page, as shown in the -following diagram: - -+----------------------------------+ -| SSDT with OEM Table ID = VMGENID | -+----------------------------------+ -| ... | TOP OF PAGE -| VGIA dword object ---------------|-----> +---------------------------+ -| ... | | fw-allocated array for | -| _STA method referring to VGIA | | "etc/vmgenid_guid" | -| ... | +---------------------------+ -| ADDR method referring to VGIA | | 0: OVMF SDT Header probe | -| ... | | suppressor | -+----------------------------------+ | 36: padding for 8-byte | - | alignment | - | 40: GUID | - | 56: padding to page size | - +---------------------------+ - END OF PAGE - - -Device Usage: -------------- - -The device has one property, which may be only be set using the command line: - - guid - sets the value of the GUID. A special value "auto" instructs - QEMU to generate a new random GUID. - -For example: - - QEMU -device vmgenid,guid="324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" - QEMU -device vmgenid,guid=auto - -The property may be queried via QMP/HMP: - - (QEMU) query-vm-generation-id - {"return": {"guid": "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87"}} - -Setting of this parameter is intentionally left out from the QMP/HMP -interfaces. There are no known use cases for changing the GUID once QEMU is -running, and adding this capability would greatly increase the complexity. diff --git a/docs/specs/vmw_pvscsi-spec.rst b/docs/specs/vmw_pvscsi-spec.rst new file mode 100644 index 0000000000..b6f434a418 --- /dev/null +++ b/docs/specs/vmw_pvscsi-spec.rst @@ -0,0 +1,115 @@ +============================== +VMWare PVSCSI Device Interface +============================== + +.. + Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. + +This document describes the VMWare PVSCSI device interface specification, +based on the source code of the PVSCSI Linux driver from kernel 3.0.4. + +Overview +======== + +The interface is based on a memory area shared between hypervisor and VM. +The memory area is obtained by driver as a device IO memory resource of +``PVSCSI_MEM_SPACE_SIZE`` length. +The shared memory consists of a registers area and a rings area. +The registers area is used to raise hypervisor interrupts and issue device +commands. The rings area is used to transfer data descriptors and SCSI +commands from VM to hypervisor and to transfer messages produced by +hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. + +PVSCSI Device Registers +======================= + +The length of the registers area is 1 page +(``PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES``). The structure of the +registers area is described by the ``PVSCSIRegOffset`` enum. There +are registers to issue device commands (with optional short data), +issue device interrupts, and control interrupt masking. + +PVSCSI Device Rings +=================== + +There are three rings in shared memory: + +Request ring (``struct PVSCSIRingReqDesc *req_ring``) + ring for OS to device requests + +Completion ring (``struct PVSCSIRingCmpDesc *cmp_ring``) + ring for device request completions + +Message ring (``struct PVSCSIRingMsgDesc *msg_ring``) + ring for messages from device. This ring is optional and the + guest might not configure it. + +There is a control area (``struct PVSCSIRingsState *rings_state``) +used to control rings operation. + +PVSCSI Device to Host Interrupts +================================ + +The following interrupt types are supported by the PVSCSI device: + +Completion interrupts (completion ring notifications): + +- ``PVSCSI_INTR_CMPL_0`` +- ``PVSCSI_INTR_CMPL_1`` + +Message interrupts (message ring notifications): + +- ``PVSCSI_INTR_MSG_0`` +- ``PVSCSI_INTR_MSG_1`` + +Interrupts are controlled via the ``PVSCSI_REG_OFFSET_INTR_MASK`` +register. If a bit is set it means the interrupt is enabled, and if +it is clear then the interrupt is disabled. + +The interrupt modes supported are legacy, MSI and MSI-X. +In the case of legacy interrupts, the ``PVSCSI_REG_OFFSET_INTR_STATUS`` +register is used to check which interrupt has arrived. Interrupts are +acknowledged when the corresponding bit is written to the interrupt +status register. + +PVSCSI Device Operation Sequences +================================= + +Startup sequence +---------------- + +a. Issue ``PVSCSI_CMD_ADAPTER_RESET`` command +b. Windows driver reads interrupt status register here +c. Issue ``PVSCSI_CMD_SETUP_MSG_RING`` command with no additional data, + check status and disable device messages if error returned + (Omitted if device messages disabled by driver configuration) +d. Issue ``PVSCSI_CMD_SETUP_RINGS`` command, provide rings configuration + as ``struct PVSCSICmdDescSetupRings`` +e. Issue ``PVSCSI_CMD_SETUP_MSG_RING`` command again, provide + rings configuration as ``struct PVSCSICmdDescSetupMsgRing`` +f. Unmask completion and message (if device messages enabled) interrupts + +Shutdown sequence +----------------- + +a. Mask interrupts +b. Flush request ring using ``PVSCSI_REG_OFFSET_KICK_NON_RW_IO`` +c. Issue ``PVSCSI_CMD_ADAPTER_RESET`` command + +Send request +------------ + +a. Fill next free request ring descriptor +b. Issue ``PVSCSI_REG_OFFSET_KICK_RW_IO`` for R/W operations + or ``PVSCSI_REG_OFFSET_KICK_NON_RW_IO`` for other operations + +Abort command +------------- + +a. Issue ``PVSCSI_CMD_ABORT_CMD`` command + +Request completion processing +----------------------------- + +a. Upon completion interrupt arrival process completion + and message (if enabled) rings diff --git a/docs/specs/vmw_pvscsi-spec.txt b/docs/specs/vmw_pvscsi-spec.txt deleted file mode 100644 index 49affb2a42..0000000000 --- a/docs/specs/vmw_pvscsi-spec.txt +++ /dev/null @@ -1,92 +0,0 @@ -General Description -=================== - -This document describes VMWare PVSCSI device interface specification. -Created by Dmitry Fleytman (dmitry@daynix.com), Daynix Computing LTD. -Based on source code of PVSCSI Linux driver from kernel 3.0.4 - -PVSCSI Device Interface Overview -================================ - -The interface is based on memory area shared between hypervisor and VM. -Memory area is obtained by driver as device IO memory resource of -PVSCSI_MEM_SPACE_SIZE length. -The shared memory consists of registers area and rings area. -The registers area is used to raise hypervisor interrupts and issue device -commands. The rings area is used to transfer data descriptors and SCSI -commands from VM to hypervisor and to transfer messages produced by -hypervisor to VM. Data itself is transferred via virtual scatter-gather DMA. - -PVSCSI Device Registers -======================= - -The length of the registers area is 1 page (PVSCSI_MEM_SPACE_COMMAND_NUM_PAGES). -The structure of the registers area is described by the PVSCSIRegOffset enum. -There are registers to issue device command (with optional short data), -issue device interrupt, control interrupts masking. - -PVSCSI Device Rings -=================== - -There are three rings in shared memory: - - 1. Request ring (struct PVSCSIRingReqDesc *req_ring) - - ring for OS to device requests - 2. Completion ring (struct PVSCSIRingCmpDesc *cmp_ring) - - ring for device request completions - 3. Message ring (struct PVSCSIRingMsgDesc *msg_ring) - - ring for messages from device. - This ring is optional and the guest might not configure it. -There is a control area (struct PVSCSIRingsState *rings_state) used to control -rings operation. - -PVSCSI Device to Host Interrupts -================================ -There are following interrupt types supported by PVSCSI device: - 1. Completion interrupts (completion ring notifications): - PVSCSI_INTR_CMPL_0 - PVSCSI_INTR_CMPL_1 - 2. Message interrupts (message ring notifications): - PVSCSI_INTR_MSG_0 - PVSCSI_INTR_MSG_1 - -Interrupts are controlled via PVSCSI_REG_OFFSET_INTR_MASK register -Bit set means interrupt enabled, bit cleared - disabled - -Interrupt modes supported are legacy, MSI and MSI-X -In case of legacy interrupts, register PVSCSI_REG_OFFSET_INTR_STATUS -is used to check which interrupt has arrived. Interrupts are -acknowledged when the corresponding bit is written to the interrupt -status register. - -PVSCSI Device Operation Sequences -================================= - -1. Startup sequence: - a. Issue PVSCSI_CMD_ADAPTER_RESET command; - aa. Windows driver reads interrupt status register here; - b. Issue PVSCSI_CMD_SETUP_MSG_RING command with no additional data, - check status and disable device messages if error returned; - (Omitted if device messages disabled by driver configuration) - c. Issue PVSCSI_CMD_SETUP_RINGS command, provide rings configuration - as struct PVSCSICmdDescSetupRings; - d. Issue PVSCSI_CMD_SETUP_MSG_RING command again, provide - rings configuration as struct PVSCSICmdDescSetupMsgRing; - e. Unmask completion and message (if device messages enabled) interrupts. - -2. Shutdown sequences - a. Mask interrupts; - b. Flush request ring using PVSCSI_REG_OFFSET_KICK_NON_RW_IO; - c. Issue PVSCSI_CMD_ADAPTER_RESET command. - -3. Send request - a. Fill next free request ring descriptor; - b. Issue PVSCSI_REG_OFFSET_KICK_RW_IO for R/W operations; - or PVSCSI_REG_OFFSET_KICK_NON_RW_IO for other operations. - -4. Abort command - a. Issue PVSCSI_CMD_ABORT_CMD command; - -5. Request completion processing - a. Upon completion interrupt arrival process completion - and message (if enabled) rings. diff --git a/docs/system/devices/ivshmem.rst b/docs/system/devices/ivshmem.rst index e7aaf34c20..ce71e25663 100644 --- a/docs/system/devices/ivshmem.rst +++ b/docs/system/devices/ivshmem.rst @@ -33,7 +33,7 @@ syntax when using the shared memory server is: When using the server, the guest will be assigned a VM ID (>=0) that allows guests using the same server to communicate via interrupts. Guests can read their VM ID from a device register (see -ivshmem-spec.txt). +:doc:`../../specs/ivshmem-spec`). Migration with ivshmem ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7e68348440..e35007ed41 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -291,7 +291,7 @@ config STELLARIS select SSD0303 # OLED display select SSD0323 # OLED display select SSI_SD - select STELLARIS_INPUT + select STELLARIS_GAMEPAD select STELLARIS_ENET # ethernet select STELLARIS_GPTM # general purpose timer module select UNIMP @@ -482,6 +482,7 @@ config XLNX_VERSAL select XLNX_BBRAM select XLNX_EFUSE_VERSAL select XLNX_USB_SUBSYS + select XLNX_VERSAL_TRNG config NPCM7XX bool diff --git a/hw/arm/pxa2xx_gpio.c b/hw/arm/pxa2xx_gpio.c index e7c3d99224..c8db5e8e2b 100644 --- a/hw/arm/pxa2xx_gpio.c +++ b/hw/arm/pxa2xx_gpio.c @@ -32,7 +32,6 @@ struct PXA2xxGPIOInfo { MemoryRegion iomem; qemu_irq irq0, irq1, irqX; int lines; - int ncpu; ARMCPU *cpu; /* XXX: GNU C vectors are more suitable */ @@ -266,12 +265,11 @@ static const MemoryRegionOps pxa_gpio_ops = { DeviceState *pxa2xx_gpio_init(hwaddr base, ARMCPU *cpu, DeviceState *pic, int lines) { - CPUState *cs = CPU(cpu); DeviceState *dev; dev = qdev_new(TYPE_PXA2XX_GPIO); qdev_prop_set_int32(dev, "lines", lines); - qdev_prop_set_int32(dev, "ncpu", cs->cpu_index); + object_property_set_link(OBJECT(dev), "cpu", OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); @@ -303,8 +301,6 @@ static void pxa2xx_gpio_realize(DeviceState *dev, Error **errp) { PXA2xxGPIOInfo *s = PXA2XX_GPIO(dev); - s->cpu = ARM_CPU(qemu_get_cpu(s->ncpu)); - qdev_init_gpio_in(dev, pxa2xx_gpio_set, s->lines); qdev_init_gpio_out(dev, s->handler, s->lines); } @@ -339,7 +335,7 @@ static const VMStateDescription vmstate_pxa2xx_gpio_regs = { static Property pxa2xx_gpio_properties[] = { DEFINE_PROP_INT32("lines", PXA2xxGPIOInfo, lines, 0), - DEFINE_PROP_INT32("ncpu", PXA2xxGPIOInfo, ncpu, 0), + DEFINE_PROP_LINK("cpu", PXA2xxGPIOInfo, cpu, TYPE_ARM_CPU, ARMCPU *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index aa5b0ddfaa..dd90f686bf 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -23,7 +23,7 @@ #include "sysemu/sysemu.h" #include "hw/arm/armv7m.h" #include "hw/char/pl011.h" -#include "hw/input/gamepad.h" +#include "hw/input/stellaris_gamepad.h" #include "hw/irq.h" #include "hw/watchdog/cmsdk-apb-watchdog.h" #include "migration/vmstate.h" @@ -31,6 +31,8 @@ #include "hw/timer/stellaris-gptm.h" #include "hw/qdev-clock.h" #include "qom/object.h" +#include "qapi/qmp/qlist.h" +#include "ui/input.h" #define GPIO_A 0 #define GPIO_B 1 @@ -1274,16 +1276,30 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) sysbus_connect_irq(SYS_BUS_DEVICE(enet), 0, qdev_get_gpio_in(nvic, 42)); } if (board->peripherals & BP_GAMEPAD) { - qemu_irq gpad_irq[5]; - static const int gpad_keycode[5] = { 0xc8, 0xd0, 0xcb, 0xcd, 0x1d }; - - gpad_irq[0] = qemu_irq_invert(gpio_in[GPIO_E][0]); /* up */ - gpad_irq[1] = qemu_irq_invert(gpio_in[GPIO_E][1]); /* down */ - gpad_irq[2] = qemu_irq_invert(gpio_in[GPIO_E][2]); /* left */ - gpad_irq[3] = qemu_irq_invert(gpio_in[GPIO_E][3]); /* right */ - gpad_irq[4] = qemu_irq_invert(gpio_in[GPIO_F][1]); /* select */ - - stellaris_gamepad_init(5, gpad_irq, gpad_keycode); + QList *gpad_keycode_list = qlist_new(); + static const int gpad_keycode[5] = { + Q_KEY_CODE_UP, Q_KEY_CODE_DOWN, Q_KEY_CODE_LEFT, + Q_KEY_CODE_RIGHT, Q_KEY_CODE_CTRL, + }; + DeviceState *gpad; + + gpad = qdev_new(TYPE_STELLARIS_GAMEPAD); + for (i = 0; i < ARRAY_SIZE(gpad_keycode); i++) { + qlist_append_int(gpad_keycode_list, gpad_keycode[i]); + } + qdev_prop_set_array(gpad, "keycodes", gpad_keycode_list); + sysbus_realize_and_unref(SYS_BUS_DEVICE(gpad), &error_fatal); + + qdev_connect_gpio_out(gpad, 0, + qemu_irq_invert(gpio_in[GPIO_E][0])); /* up */ + qdev_connect_gpio_out(gpad, 1, + qemu_irq_invert(gpio_in[GPIO_E][1])); /* down */ + qdev_connect_gpio_out(gpad, 2, + qemu_irq_invert(gpio_in[GPIO_E][2])); /* left */ + qdev_connect_gpio_out(gpad, 3, + qemu_irq_invert(gpio_in[GPIO_E][3])); /* right */ + qdev_connect_gpio_out(gpad, 4, + qemu_irq_invert(gpio_in[GPIO_F][1])); /* select */ } for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index fa556d8764..4f74a64a0d 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -373,6 +373,21 @@ static void versal_create_rtc(Versal *s, qemu_irq *pic) qdev_get_gpio_in(DEVICE(&s->pmc.apb_irq_orgate), 0)); } +static void versal_create_trng(Versal *s, qemu_irq *pic) +{ + SysBusDevice *sbd; + MemoryRegion *mr; + + object_initialize_child(OBJECT(s), "trng", &s->pmc.trng, + TYPE_XLNX_VERSAL_TRNG); + sbd = SYS_BUS_DEVICE(&s->pmc.trng); + sysbus_realize(sbd, &error_fatal); + + mr = sysbus_mmio_get_region(sbd, 0); + memory_region_add_subregion(&s->mr_ps, MM_PMC_TRNG, mr); + sysbus_connect_irq(sbd, 0, pic[VERSAL_TRNG_IRQ]); +} + static void versal_create_xrams(Versal *s, qemu_irq *pic) { int nr_xrams = ARRAY_SIZE(s->lpd.xram.ctrl); @@ -909,6 +924,7 @@ static void versal_realize(DeviceState *dev, Error **errp) versal_create_sds(s, pic); versal_create_pmc_apb_irq_orgate(s, pic); versal_create_rtc(s, pic); + versal_create_trng(s, pic); versal_create_xrams(s, pic); versal_create_bbram(s, pic); versal_create_efuse(s, pic); diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 6fa4ac502c..f9cbc9bdc4 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -342,25 +342,26 @@ static void mcf_uart_register(void) type_init(mcf_uart_register) -void *mcf_uart_init(qemu_irq irq, Chardev *chrdrv) +DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chrdrv) { - DeviceState *dev; + DeviceState *dev; dev = qdev_new(TYPE_MCF_UART); if (chrdrv) { qdev_prop_set_chr(dev, "chardev", chrdrv); } sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); return dev; } -void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chrdrv) +DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chrdrv) { - DeviceState *dev; + DeviceState *dev; - dev = mcf_uart_init(irq, chrdrv); + dev = mcf_uart_create(irq, chrdrv); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + + return dev; } diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index fde67f4f03..8753afeb2b 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -53,6 +53,17 @@ static int stm32f2xx_usart_can_receive(void *opaque) return 0; } +static void stm32f2xx_update_irq(STM32F2XXUsartState *s) +{ + uint32_t mask = s->usart_sr & s->usart_cr1; + + if (mask & (USART_SR_TXE | USART_SR_TC | USART_SR_RXNE)) { + qemu_set_irq(s->irq, 1); + } else { + qemu_set_irq(s->irq, 0); + } +} + static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) { STM32F2XXUsartState *s = opaque; @@ -66,9 +77,7 @@ static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) s->usart_dr = *buf; s->usart_sr |= USART_SR_RXNE; - if (s->usart_cr1 & USART_CR1_RXNEIE) { - qemu_set_irq(s->irq, 1); - } + stm32f2xx_update_irq(s); DB_PRINT("Receiving: %c\n", s->usart_dr); } @@ -85,7 +94,7 @@ static void stm32f2xx_usart_reset(DeviceState *dev) s->usart_cr3 = 0x00000000; s->usart_gtpr = 0x00000000; - qemu_set_irq(s->irq, 0); + stm32f2xx_update_irq(s); } static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, @@ -106,7 +115,7 @@ static uint64_t stm32f2xx_usart_read(void *opaque, hwaddr addr, retvalue = s->usart_dr & 0x3FF; s->usart_sr &= ~USART_SR_RXNE; qemu_chr_fe_accept_input(&s->chr); - qemu_set_irq(s->irq, 0); + stm32f2xx_update_irq(s); return retvalue; case USART_BRR: return s->usart_brr; @@ -145,9 +154,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, } else { s->usart_sr &= value; } - if (!(s->usart_sr & USART_SR_RXNE)) { - qemu_set_irq(s->irq, 0); - } + stm32f2xx_update_irq(s); return; case USART_DR: if (value < 0xF000) { @@ -161,6 +168,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, clear TC by writing 0 to the SR register, so set it again on each write. */ s->usart_sr |= USART_SR_TC; + stm32f2xx_update_irq(s); } return; case USART_BRR: @@ -168,10 +176,7 @@ static void stm32f2xx_usart_write(void *opaque, hwaddr addr, return; case USART_CR1: s->usart_cr1 = value; - if (s->usart_cr1 & USART_CR1_RXNEIE && - s->usart_sr & USART_SR_RXNE) { - qemu_set_irq(s->irq, 1); - } + stm32f2xx_update_irq(s); return; case USART_CR2: s->usart_cr2 = value; diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 7c6dfab128..2f1dbb3fd7 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -673,6 +673,20 @@ const PropertyInfo qdev_prop_multifd_compression = { .set_default_value = qdev_propinfo_set_default_value_enum, }; +/* --- MigMode --- */ + +QEMU_BUILD_BUG_ON(sizeof(MigMode) != sizeof(int)); + +const PropertyInfo qdev_prop_mig_mode = { + .name = "MigMode", + .description = "mig_mode values, " + "normal,cpr-reboot", + .enum_table = &MigMode_lookup, + .get = qdev_propinfo_get_enum, + .set = qdev_propinfo_set_enum, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + /* --- Reserved Region --- */ /* diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 357b8761b5..950ef48e01 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -3,12 +3,14 @@ #include "qapi/error.h" #include "qapi/qapi-types-misc.h" #include "qapi/qmp/qerror.h" +#include "qapi/qmp/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" #include "qapi/visitor.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qdev-prop-internal.h" +#include "qom/qom-qobject.h" void qdev_prop_set_after_realize(DeviceState *dev, const char *name, Error **errp) @@ -739,6 +741,25 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value) &error_abort); } +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values) +{ + const QListEntry *entry; + g_autofree char *prop_len = g_strdup_printf("len-%s", name); + uint32_t i = 0; + + object_property_set_int(OBJECT(dev), prop_len, qlist_size(values), + &error_abort); + + QLIST_FOREACH_ENTRY(values, entry) { + g_autofree char *prop_idx = g_strdup_printf("%s[%u]", name, i); + object_property_set_qobject(OBJECT(dev), prop_idx, entry->value, + &error_abort); + i++; + } + + qobject_unref(values); +} + static GPtrArray *global_props(void) { static GPtrArray *gp; diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 2a5437d803..c096ec93e5 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -1,7 +1,7 @@ /* * QEMU ISA VGA Emulator. * - * see docs/specs/standard-vga.txt for virtual hardware specs. + * see docs/specs/standard-vga.rst for virtual hardware specs. * * Copyright (c) 2003 Fabrice Bellard * diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index b351b8f299..e4f45b4476 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -1,7 +1,7 @@ /* * QEMU PCI VGA Emulator. * - * see docs/specs/standard-vga.txt for virtual hardware specs. + * see docs/specs/standard-vga.rst for virtual hardware specs. * * Copyright (c) 2003 Fabrice Bellard * diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 09591fbd39..7490d43881 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1264,7 +1264,7 @@ static void vmsvga_init(DeviceState *dev, struct vmsvga_state_s *s, vga_common_init(&s->vga, OBJECT(dev), &error_fatal); vga_init(&s->vga, OBJECT(dev), address_space, io, true); - vmstate_register(NULL, 0, &vmstate_vga_common, &s->vga); + vmstate_register_any(NULL, &vmstate_vga_common, &s->vga); s->new_depth = 32; } diff --git a/hw/i2c/core.c b/hw/i2c/core.c index bed594fe59..879a1d45cb 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -64,7 +64,7 @@ I2CBus *i2c_init_bus(DeviceState *parent, const char *name) bus = I2C_BUS(qbus_new(TYPE_I2C_BUS, parent, name)); QLIST_INIT(&bus->current_devs); QSIMPLEQ_INIT(&bus->pending_masters); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_i2c_bus, bus); + vmstate_register_any(NULL, &vmstate_i2c_bus, bus); return bus; } diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 4e1b8a5182..78e7c229a8 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -23,6 +23,7 @@ #include "hw/i2c/pm_smbus.h" #include "hw/i2c/smbus_master.h" #include "migration/vmstate.h" +#include "trace.h" #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 @@ -64,15 +65,6 @@ #define AUX_BLK (1 << 1) #define AUX_MASK 0x3 -/*#define DEBUG*/ - -#ifdef DEBUG -# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) -#else -# define SMBUS_DPRINTF(format, ...) do { } while (0) -#endif - - static void smb_transaction(PMSMBus *s) { uint8_t prot = (s->smb_ctl >> 2) & 0x07; @@ -82,7 +74,7 @@ static void smb_transaction(PMSMBus *s) I2CBus *bus = s->smbus; int ret; - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); + trace_smbus_transaction(addr, prot); /* Transaction isn't exec if STS_DEV_ERR bit set */ if ((s->smb_stat & STS_DEV_ERR) != 0) { goto error; @@ -258,8 +250,7 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, PMSMBus *s = opaque; uint8_t clear_byte_done; - SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx - " val=0x%02" PRIx64 "\n", addr, val); + trace_smbus_ioport_writeb(addr, val); switch(addr) { case SMBHSTSTS: clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; @@ -429,8 +420,7 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = 0; break; } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", - addr, val); + trace_smbus_ioport_readb(addr, val); if (s->set_irq) { s->set_irq(s, smb_irq_value(s)); diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index d7b1e25858..6900e06eda 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -15,6 +15,12 @@ i2c_send_async(uint8_t address, uint8_t data) "send_async(addr:0x%02x) data:0x%0 i2c_recv(uint8_t address, uint8_t data) "recv(addr:0x%02x) data:0x%02x" i2c_ack(void) "" +# pm_smbus.c + +smbus_ioport_readb(uint16_t addr, uint8_t data) "[0x%04" PRIx16 "] -> val=0x%02x" +smbus_ioport_writeb(uint16_t addr, uint8_t data) "[0x%04" PRIx16 "] <- val=0x%02x" +smbus_transaction(uint8_t addr, uint8_t prot) "addr=0x%02x prot=0x%02x" + # allwinner_i2c.c allwinner_i2c_read(const char* reg_name, uint64_t offset, uint64_t value) "read %s [0x%" PRIx64 "]: -> 0x%" PRIx64 diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 95053e026f..ea60c08116 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -73,7 +73,7 @@ static void isa_ide_realizefn(DeviceState *dev, Error **errp) ide_bus_init(&s->bus, sizeof(s->bus), dev, 0, 2); ide_init_ioport(&s->bus, isadev, s->iobase, s->iobase2); ide_bus_init_output_irq(&s->bus, isa_get_irq(isadev, s->irqnum)); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_ide_isa, s); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_ide_isa, s); ide_bus_register_restart_cb(&s->bus); } diff --git a/hw/input/Kconfig b/hw/input/Kconfig index 55865bb386..f86e98c829 100644 --- a/hw/input/Kconfig +++ b/hw/input/Kconfig @@ -20,7 +20,7 @@ config PL050 config PS2 bool -config STELLARIS_INPUT +config STELLARIS_GAMEPAD bool config TSC2005 diff --git a/hw/input/adb.c b/hw/input/adb.c index 214ae6f42b..8aed0da2cd 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -247,7 +247,7 @@ static void adb_bus_realize(BusState *qbus, Error **errp) adb_bus->autopoll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, adb_autopoll, adb_bus); - vmstate_register(NULL, -1, &vmstate_adb_bus, adb_bus); + vmstate_register_any(NULL, &vmstate_adb_bus, adb_bus); } static void adb_bus_unrealize(BusState *qbus) diff --git a/hw/input/ads7846.c b/hw/input/ads7846.c index dc0998ac79..91116c6bdb 100644 --- a/hw/input/ads7846.c +++ b/hw/input/ads7846.c @@ -158,7 +158,7 @@ static void ads7846_realize(SSIPeripheral *d, Error **errp) ads7846_int_update(s); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_ads7846, s); + vmstate_register_any(NULL, &vmstate_ads7846, s); } static void ads7846_class_init(ObjectClass *klass, void *data) diff --git a/hw/input/meson.build b/hw/input/meson.build index c0d4482180..640556bbbc 100644 --- a/hw/input/meson.build +++ b/hw/input/meson.build @@ -5,7 +5,7 @@ system_ss.add(when: 'CONFIG_LM832X', if_true: files('lm832x.c')) system_ss.add(when: 'CONFIG_PCKBD', if_true: files('pckbd.c')) system_ss.add(when: 'CONFIG_PL050', if_true: files('pl050.c')) system_ss.add(when: 'CONFIG_PS2', if_true: files('ps2.c')) -system_ss.add(when: 'CONFIG_STELLARIS_INPUT', if_true: files('stellaris_input.c')) +system_ss.add(when: 'CONFIG_STELLARIS_GAMEPAD', if_true: files('stellaris_gamepad.c')) system_ss.add(when: 'CONFIG_TSC2005', if_true: files('tsc2005.c')) system_ss.add(when: 'CONFIG_VIRTIO_INPUT', if_true: files('virtio-input.c')) diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c new file mode 100644 index 0000000000..06a0c0ce83 --- /dev/null +++ b/hw/input/stellaris_gamepad.c @@ -0,0 +1,99 @@ +/* + * Gamepad style buttons connected to IRQ/GPIO lines + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the GPL. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/input/stellaris_gamepad.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" +#include "ui/console.h" + +static void stellaris_gamepad_event(DeviceState *dev, QemuConsole *src, + InputEvent *evt) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(dev); + InputKeyEvent *key = evt->u.key.data; + int qcode = qemu_input_key_value_to_qcode(key->key); + int i; + + for (i = 0; i < s->num_buttons; i++) { + if (s->keycodes[i] == qcode && s->pressed[i] != key->down) { + s->pressed[i] = key->down; + qemu_set_irq(s->irqs[i], key->down); + } + } +} + +static const VMStateDescription vmstate_stellaris_gamepad = { + .name = "stellaris_gamepad", + .version_id = 4, + .minimum_version_id = 4, + .fields = (VMStateField[]) { + VMSTATE_VARRAY_UINT32(pressed, StellarisGamepad, num_buttons, + 0, vmstate_info_uint8, uint8_t), + VMSTATE_END_OF_LIST() + } +}; + +static const QemuInputHandler stellaris_gamepad_handler = { + .name = "Stellaris Gamepad", + .mask = INPUT_EVENT_MASK_KEY, + .event = stellaris_gamepad_event, +}; + +static void stellaris_gamepad_realize(DeviceState *dev, Error **errp) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(dev); + + if (s->num_buttons == 0) { + error_setg(errp, "keycodes property array must be set"); + return; + } + + s->irqs = g_new0(qemu_irq, s->num_buttons); + s->pressed = g_new0(uint8_t, s->num_buttons); + qdev_init_gpio_out(dev, s->irqs, s->num_buttons); + qemu_input_handler_register(dev, &stellaris_gamepad_handler); +} + +static void stellaris_gamepad_reset_enter(Object *obj, ResetType type) +{ + StellarisGamepad *s = STELLARIS_GAMEPAD(obj); + + memset(s->pressed, 0, s->num_buttons * sizeof(uint8_t)); +} + +static Property stellaris_gamepad_properties[] = { + DEFINE_PROP_ARRAY("keycodes", StellarisGamepad, num_buttons, + keycodes, qdev_prop_uint32, uint32_t), + DEFINE_PROP_END_OF_LIST(), +}; + +static void stellaris_gamepad_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.enter = stellaris_gamepad_reset_enter; + dc->realize = stellaris_gamepad_realize; + dc->vmsd = &vmstate_stellaris_gamepad; + device_class_set_props(dc, stellaris_gamepad_properties); +} + +static const TypeInfo stellaris_gamepad_info[] = { + { + .name = TYPE_STELLARIS_GAMEPAD, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(StellarisGamepad), + .class_init = stellaris_gamepad_class_init, + }, +}; + +DEFINE_TYPES(stellaris_gamepad_info); diff --git a/hw/input/stellaris_input.c b/hw/input/stellaris_input.c deleted file mode 100644 index e6ee5e11f1..0000000000 --- a/hw/input/stellaris_input.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Gamepad style buttons connected to IRQ/GPIO lines - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the GPL. - */ - -#include "qemu/osdep.h" -#include "hw/input/gamepad.h" -#include "hw/irq.h" -#include "migration/vmstate.h" -#include "ui/console.h" - -typedef struct { - qemu_irq irq; - int keycode; - uint8_t pressed; -} gamepad_button; - -typedef struct { - gamepad_button *buttons; - int num_buttons; - int extension; -} gamepad_state; - -static void stellaris_gamepad_put_key(void * opaque, int keycode) -{ - gamepad_state *s = (gamepad_state *)opaque; - int i; - int down; - - if (keycode == 0xe0 && !s->extension) { - s->extension = 0x80; - return; - } - - down = (keycode & 0x80) == 0; - keycode = (keycode & 0x7f) | s->extension; - - for (i = 0; i < s->num_buttons; i++) { - if (s->buttons[i].keycode == keycode - && s->buttons[i].pressed != down) { - s->buttons[i].pressed = down; - qemu_set_irq(s->buttons[i].irq, down); - } - } - - s->extension = 0; -} - -static const VMStateDescription vmstate_stellaris_button = { - .name = "stellaris_button", - .version_id = 0, - .minimum_version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT8(pressed, gamepad_button), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_stellaris_gamepad = { - .name = "stellaris_gamepad", - .version_id = 2, - .minimum_version_id = 2, - .fields = (VMStateField[]) { - VMSTATE_INT32(extension, gamepad_state), - VMSTATE_STRUCT_VARRAY_POINTER_INT32(buttons, gamepad_state, - num_buttons, - vmstate_stellaris_button, - gamepad_button), - VMSTATE_END_OF_LIST() - } -}; - -/* Returns an array of 5 output slots. */ -void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode) -{ - gamepad_state *s; - int i; - - s = g_new0(gamepad_state, 1); - s->buttons = g_new0(gamepad_button, n); - for (i = 0; i < n; i++) { - s->buttons[i].irq = irq[i]; - s->buttons[i].keycode = keycode[i]; - } - s->num_buttons = n; - qemu_add_kbd_event_handler(stellaris_gamepad_put_key, s); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_stellaris_gamepad, s); -} diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 0c515e4ecb..e09705eeaf 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -11,6 +11,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "monitor/monitor.h" +#include "hw/qdev-properties.h" #include "hw/nmi.h" #include "hw/intc/intc.h" #include "hw/intc/m68k_irqc.h" @@ -35,7 +36,7 @@ static void m68k_irqc_print_info(InterruptStatsProvider *obj, Monitor *mon) static void m68k_set_irq(void *opaque, int irq, int level) { M68KIRQCState *s = opaque; - M68kCPU *cpu = M68K_CPU(first_cpu); + M68kCPU *cpu = M68K_CPU(s->cpu); int i; if (level) { @@ -85,12 +86,19 @@ static const VMStateDescription vmstate_m68k_irqc = { } }; +static Property m68k_irqc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", M68KIRQCState, cpu, + TYPE_M68K_CPU, ArchCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void m68k_irqc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); + device_class_set_props(dc, m68k_irqc_properties); nc->nmi_monitor_handler = m68k_nmi; dc->reset = m68k_irqc_reset; dc->vmsd = &vmstate_m68k_irqc; diff --git a/hw/intc/xics.c b/hw/intc/xics.c index c7f8abd71e..c77e986136 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -335,8 +335,22 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } } - - vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); + /* + * The way that pre_2_10_icp is handling is really, really hacky. + * We used to have here this call: + * + * vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); + * + * But we were doing: + * pre_2_10_vmstate_register_dummy_icp() + * this vmstate_register() + * pre_2_10_vmstate_unregister_dummy_icp() + * + * So for a short amount of time we had to vmstate entries with + * the same name. This fixes it. + */ + vmstate_replace_hack_for_ppc(NULL, icp->cs->cpu_index, + &vmstate_icp_server, icp); } static void icp_unrealize(DeviceState *dev) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index e232d35ba2..2117dad35a 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -453,19 +453,6 @@ static void ipmi_bmc_extern_handle_reset(IPMIBmc *b) continue_send(ibe); } -static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) -{ - IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); - - if (!qemu_chr_fe_backend_connected(&ibe->chr)) { - error_setg(errp, "IPMI external bmc requires chardev attribute"); - return; - } - - qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, - chr_event, NULL, ibe, NULL, true); -} - static int ipmi_bmc_extern_post_migrate(void *opaque, int version_id) { IPMIBmcExtern *ibe = opaque; @@ -499,12 +486,26 @@ static const VMStateDescription vmstate_ipmi_bmc_extern = { } }; +static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) +{ + IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(dev); + + if (!qemu_chr_fe_backend_connected(&ibe->chr)) { + error_setg(errp, "IPMI external bmc requires chardev attribute"); + return; + } + + qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, + chr_event, NULL, ibe, NULL, true); + + vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); +} + static void ipmi_bmc_extern_init(Object *obj) { IPMIBmcExtern *ibe = IPMI_BMC_EXTERN(obj); ibe->extern_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, extern_timeout, ibe); - vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); } static void ipmi_bmc_extern_finalize(Object *obj) diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index a83e7243d6..aec064d3cd 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -68,6 +68,21 @@ static void isa_ipmi_bt_lower_irq(IPMIBT *ib) qemu_irq_lower(iib->irq); } +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", + .version_id = 2, + .minimum_version_id = 2, + /* + * Version 1 had messed up the array transfer, it's not even usable + * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer + * the buffer length, so random things would happen. + */ + .fields = (VMStateField[]) { + VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), + VMSTATE_END_OF_LIST() + } +}; + static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) { Error *err = NULL; @@ -102,30 +117,15 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); -} -static const VMStateDescription vmstate_ISAIPMIBTDevice = { - .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", - .version_id = 2, - .minimum_version_id = 2, - /* - * Version 1 had messed up the array transfer, it's not even usable - * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer - * the buffer length, so random things would happen. - */ - .fields = (VMStateField[]) { - VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), - VMSTATE_END_OF_LIST() - } -}; + vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, dev); +} static void isa_ipmi_bt_init(Object *obj) { ISAIPMIBTDevice *iib = ISA_IPMI_BT(obj); ipmi_bmc_find_and_link(obj, (Object **) &iib->bt.bmc); - - vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, iib); } static void *isa_ipmi_bt_get_backend_data(IPMIInterface *ii) diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index b2ed70b9da..b5dcb64616 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -67,6 +67,24 @@ static void isa_ipmi_kcs_lower_irq(IPMIKCS *ik) qemu_irq_lower(iik->irq); } +static bool vmstate_kcs_before_version2(void *opaque, int version) +{ + return version <= 1; +} + +static const VMStateDescription vmstate_ISAIPMIKCSDevice = { + .name = TYPE_IPMI_INTERFACE, + .version_id = 2, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, + 0, vmstate_IPMIKCS, IPMIKCS, 1), + VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, + IPMIKCS, 2), + VMSTATE_END_OF_LIST() + } +}; + static void ipmi_isa_realize(DeviceState *dev, Error **errp) { Error *err = NULL; @@ -101,38 +119,20 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); -} -static bool vmstate_kcs_before_version2(void *opaque, int version) -{ - return version <= 1; + /* + * Version 1 had an incorrect name, it clashed with the BT + * IPMI device, so receive it, but transmit a different + * version. + */ + vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } -static const VMStateDescription vmstate_ISAIPMIKCSDevice = { - .name = TYPE_IPMI_INTERFACE, - .version_id = 2, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_VSTRUCT_TEST(kcs, ISAIPMIKCSDevice, vmstate_kcs_before_version2, - 0, vmstate_IPMIKCS, IPMIKCS, 1), - VMSTATE_VSTRUCT_V(kcs, ISAIPMIKCSDevice, 2, vmstate_IPMIKCS, - IPMIKCS, 2), - VMSTATE_END_OF_LIST() - } -}; - static void isa_ipmi_kcs_init(Object *obj) { ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(obj); ipmi_bmc_find_and_link(obj, (Object **) &iik->kcs.bmc); - - /* - * Version 1 had an incorrect name, it clashed with the BT - * IPMI device, so receive it, but transmit a different - * version. - */ - vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } static void *isa_ipmi_kcs_get_backend_data(IPMIInterface *ii) diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 11ae4c9795..1e8e64f8bd 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -20,12 +20,14 @@ #define AN5206_MBAR_ADDR 0x10000000 #define AN5206_RAMBAR_ADDR 0x20000000 -static void mcf5206_init(MemoryRegion *sysmem, uint32_t base) +static void mcf5206_init(M68kCPU *cpu, MemoryRegion *sysmem, uint32_t base) { DeviceState *dev; SysBusDevice *s; dev = qdev_new(TYPE_MCF5206_MBAR); + object_property_set_link(OBJECT(dev), "m68k-cpu", + OBJECT(cpu), &error_abort); s = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(s, &error_fatal); @@ -60,7 +62,7 @@ static void an5206_init(MachineState *machine) memory_region_init_ram(sram, NULL, "an5206.sram", 512, &error_fatal); memory_region_add_subregion(address_space_mem, AN5206_RAMBAR_ADDR, sram); - mcf5206_init(address_space_mem, AN5206_MBAR_ADDR); + mcf5206_init(cpu, address_space_mem, AN5206_MBAR_ADDR); /* Load kernel. */ if (!kernel_filename) { diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index 2ab1b4f059..a46a23538d 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -10,6 +10,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "cpu.h" +#include "hw/qdev-properties.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/m68k/mcf.h" @@ -167,7 +168,7 @@ typedef struct { MemoryRegion iomem; qemu_irq *pic; m5206_timer_state *timer[2]; - void *uart[2]; + DeviceState *uart[2]; uint8_t scr; uint8_t icr[14]; uint16_t imr; /* 1 == interrupt is masked. */ @@ -599,15 +600,21 @@ static void mcf5206_mbar_realize(DeviceState *dev, Error **errp) s->pic = qemu_allocate_irqs(m5206_mbar_set_irq, s, 14); s->timer[0] = m5206_timer_init(s->pic[9]); s->timer[1] = m5206_timer_init(s->pic[10]); - s->uart[0] = mcf_uart_init(s->pic[12], serial_hd(0)); - s->uart[1] = mcf_uart_init(s->pic[13], serial_hd(1)); - s->cpu = M68K_CPU(qemu_get_cpu(0)); + s->uart[0] = mcf_uart_create(s->pic[12], serial_hd(0)); + s->uart[1] = mcf_uart_create(s->pic[13], serial_hd(1)); } +static Property mcf5206_mbar_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", m5206_mbar_state, cpu, + TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void mcf5206_mbar_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, mcf5206_mbar_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->desc = "MCF5206 system integration module"; dc->realize = mcf5206_mbar_realize; diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index be1033f84f..d22d8536db 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -261,9 +261,9 @@ static void mcf5208evb_init(MachineState *machine) /* Internal peripherals. */ pic = mcf_intc_init(address_space_mem, 0xfc048000, cpu); - mcf_uart_mm_init(0xfc060000, pic[26], serial_hd(0)); - mcf_uart_mm_init(0xfc064000, pic[27], serial_hd(1)); - mcf_uart_mm_init(0xfc068000, pic[28], serial_hd(2)); + mcf_uart_create_mmap(0xfc060000, pic[26], serial_hd(0)); + mcf_uart_create_mmap(0xfc064000, pic[27], serial_hd(1)); + mcf_uart_create_mmap(0xfc068000, pic[28], serial_hd(2)); mcf5208_sys_init(address_space_mem, pic); diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 4cd30188c0..1d3b34e18c 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -14,6 +14,7 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/m68k/mcf.h" +#include "hw/qdev-properties.h" #include "qom/object.h" #define TYPE_MCF_INTC "mcf-intc" @@ -173,12 +174,20 @@ static void mcf_intc_instance_init(Object *obj) mcf_intc_state *s = MCF_INTC(obj); memory_region_init_io(&s->iomem, obj, &mcf_intc_ops, s, "mcf", 0x100); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); } +static Property mcf_intc_properties[] = { + DEFINE_PROP_LINK("m68k-cpu", mcf_intc_state, cpu, + TYPE_M68K_CPU, M68kCPU *), + DEFINE_PROP_END_OF_LIST(), +}; + static void mcf_intc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + device_class_set_props(dc, mcf_intc_properties); set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->reset = mcf_intc_reset; } @@ -203,15 +212,13 @@ qemu_irq *mcf_intc_init(MemoryRegion *sysmem, M68kCPU *cpu) { DeviceState *dev; - mcf_intc_state *s; dev = qdev_new(TYPE_MCF_INTC); + object_property_set_link(OBJECT(dev), "m68k-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + memory_region_add_subregion(sysmem, base, + sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - s = MCF_INTC(dev); - s->cpu = cpu; - - memory_region_add_subregion(sysmem, base, &s->iomem); - - return qemu_allocate_irqs(mcf_intc_set_irq, s, 64); + return qemu_allocate_irqs(mcf_intc_set_irq, dev, 64); } diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 5d244b3b95..fabd861941 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -90,10 +90,13 @@ struct NeXTPC { uint32_t scr1; uint32_t scr2; - uint8_t scsi_csr_1; - uint8_t scsi_csr_2; uint32_t int_mask; uint32_t int_status; + uint8_t scsi_csr_1; + uint8_t scsi_csr_2; + + qemu_irq scsi_reset; + qemu_irq scsi_dma; NextRtc rtc; }; @@ -466,7 +469,7 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) DPRINTF("SCSICSR FIFO Flush\n"); /* will have to add another irq to the esp if this is needed */ /* esp_puflush_fifo(esp_g); */ - /* qemu_irq_pulse(s->scsi_dma); */ + qemu_irq_pulse(s->scsi_dma); } if (value & SCSICSR_ENABLE) { @@ -486,9 +489,9 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) if (value & SCSICSR_RESET) { DPRINTF("SCSICSR Reset\n"); /* I think this should set DMADIR. CPUDMA and INTMASK to 0 */ - /* qemu_irq_raise(s->scsi_reset); */ - /* s->scsi_csr_1 &= ~(SCSICSR_INTMASK |0x80|0x1); */ - + qemu_irq_raise(s->scsi_reset); + s->scsi_csr_1 &= ~(SCSICSR_INTMASK | 0x80 | 0x1); + qemu_irq_lower(s->scsi_reset); } if (value & SCSICSR_DMADIR) { DPRINTF("SCSICSR DMAdir\n"); @@ -496,10 +499,11 @@ static void scr_writeb(NeXTPC *s, hwaddr addr, uint32_t value) if (value & SCSICSR_CPUDMA) { DPRINTF("SCSICSR CPUDMA\n"); /* qemu_irq_raise(s->scsi_dma); */ - s->int_status |= 0x4000000; } else { + /* fprintf(stderr,"SCSICSR CPUDMA disabled\n"); */ s->int_status &= ~(0x4000000); + /* qemu_irq_lower(s->scsi_dma); */ } if (value & SCSICSR_INTMASK) { DPRINTF("SCSICSR INTMASK\n"); @@ -828,6 +832,103 @@ static void next_irq(void *opaque, int number, int level) } } +static void nextdma_write(void *opaque, uint8_t *buf, int size, int type) +{ + uint32_t base_addr; + int irq = 0; + uint8_t align = 16; + NeXTState *next_state = NEXT_MACHINE(qdev_get_machine()); + + if (type == NEXTDMA_ENRX || type == NEXTDMA_ENTX) { + align = 32; + } + /* Most DMA is supposedly 16 byte aligned */ + if ((size % align) != 0) { + size -= size % align; + size += align; + } + + /* + * prom sets the dma start using initbuf while the bootloader uses next + * so we check to see if initbuf is 0 + */ + if (next_state->dma[type].next_initbuf == 0) { + base_addr = next_state->dma[type].next; + } else { + base_addr = next_state->dma[type].next_initbuf; + } + + cpu_physical_memory_write(base_addr, buf, size); + + next_state->dma[type].next_initbuf = 0; + + /* saved limit is checked to calculate packet size by both, rom and netbsd */ + next_state->dma[type].saved_limit = (next_state->dma[type].next + size); + next_state->dma[type].saved_next = (next_state->dma[type].next); + + /* + * 32 bytes under savedbase seems to be some kind of register + * of which the purpose is unknown as of yet + */ + /* stl_phys(s->rx_dma.base-32,0xFFFFFFFF); */ + + if (!(next_state->dma[type].csr & DMA_SUPDATE)) { + next_state->dma[type].next = next_state->dma[type].start; + next_state->dma[type].limit = next_state->dma[type].stop; + } + + /* Set dma registers and raise an irq */ + next_state->dma[type].csr |= DMA_COMPLETE; /* DON'T CHANGE THIS! */ + + switch (type) { + case NEXTDMA_SCSI: + irq = NEXT_SCSI_DMA_I; + break; + } + + next_irq(opaque, irq, 1); + next_irq(opaque, irq, 0); +} + +static void nextscsi_read(void *opaque, uint8_t *buf, int len) +{ + DPRINTF("SCSI READ: %x\n", len); + abort(); +} + +static void nextscsi_write(void *opaque, uint8_t *buf, int size) +{ + DPRINTF("SCSI WRITE: %i\n", size); + nextdma_write(opaque, buf, size, NEXTDMA_SCSI); +} + +static void next_scsi_init(DeviceState *pcdev, M68kCPU *cpu) +{ + struct NeXTPC *next_pc = NEXT_PC(pcdev); + DeviceState *dev; + SysBusDevice *sysbusdev; + SysBusESPState *sysbus_esp; + ESPState *esp; + + dev = qdev_new(TYPE_SYSBUS_ESP); + sysbus_esp = SYSBUS_ESP(dev); + esp = &sysbus_esp->esp; + esp->dma_memory_read = nextscsi_read; + esp->dma_memory_write = nextscsi_write; + esp->dma_opaque = pcdev; + sysbus_esp->it_shift = 0; + esp->dma_enabled = 1; + sysbusdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sysbusdev, &error_fatal); + sysbus_connect_irq(sysbusdev, 0, qdev_get_gpio_in(pcdev, NEXT_SCSI_I)); + sysbus_mmio_map(sysbusdev, 0, 0x2114000); + + next_pc->scsi_reset = qdev_get_gpio_in(dev, 0); + next_pc->scsi_dma = qdev_get_gpio_in(dev, 1); + + scsi_bus_legacy_handle_cmdline(&esp->bus); +} + static void next_escc_init(DeviceState *pcdev) { DeviceState *dev; @@ -945,12 +1046,12 @@ static void next_cube_init(MachineState *machine) M68kCPU *cpu; CPUM68KState *env; MemoryRegion *rom = g_new(MemoryRegion, 1); + MemoryRegion *rom2 = g_new(MemoryRegion, 1); MemoryRegion *dmamem = g_new(MemoryRegion, 1); MemoryRegion *bmapm1 = g_new(MemoryRegion, 1); MemoryRegion *bmapm2 = g_new(MemoryRegion, 1); MemoryRegion *sysmem = get_system_memory(); const char *bios_name = machine->firmware ?: ROM_FILE; - DeviceState *dev; DeviceState *pcdev; /* Initialize the cpu core */ @@ -974,9 +1075,7 @@ static void next_cube_init(MachineState *machine) memory_region_add_subregion(sysmem, 0x04000000, machine->ram); /* Framebuffer */ - dev = qdev_new(TYPE_NEXTFB); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0B000000); + sysbus_create_simple(TYPE_NEXTFB, 0x0B000000, NULL); /* MMIO */ sysbus_mmio_map(SYS_BUS_DEVICE(pcdev), 0, 0x02000000); @@ -993,14 +1092,13 @@ static void next_cube_init(MachineState *machine) memory_region_add_subregion(sysmem, 0x820c0000, bmapm2); /* KBD */ - dev = qdev_new(TYPE_NEXTKBD); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x0200e000); + sysbus_create_simple(TYPE_NEXTKBD, 0x0200e000, NULL); /* Load ROM here */ - /* still not sure if the rom should also be mapped at 0x0*/ memory_region_init_rom(rom, NULL, "next.rom", 0x20000, &error_fatal); memory_region_add_subregion(sysmem, 0x01000000, rom); + memory_region_init_alias(rom2, NULL, "next.rom2", rom, 0x0, 0x20000); + memory_region_add_subregion(sysmem, 0x0, rom2); if (load_image_targphys(bios_name, 0x01000000, 0x20000) < 8) { if (!qtest_enabled()) { error_report("Failed to load firmware '%s'.", bios_name); @@ -1024,6 +1122,7 @@ static void next_cube_init(MachineState *machine) /* TODO: */ /* Network */ /* SCSI */ + next_scsi_init(pcdev, cpu); /* DMA */ memory_region_init_io(dmamem, NULL, &dma_ops, machine, "next.dma", 0x5000); @@ -1036,6 +1135,7 @@ static void next_machine_class_init(ObjectClass *oc, void *data) mc->desc = "NeXT Cube"; mc->init = next_cube_init; + mc->block_default_type = IF_SCSI; mc->default_ram_size = RAM_SIZE; mc->default_ram_id = "next.ram"; mc->default_cpu_type = M68K_CPU_TYPE_NAME("m68040"); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 2dd3c99894..2e49e262ee 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -155,6 +155,8 @@ static void virt_init(MachineState *machine) /* IRQ Controller */ irqc_dev = qdev_new(TYPE_M68K_IRQC); + object_property_set_link(OBJECT(irqc_dev), "m68k-cpu", + OBJECT(cpu), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(irqc_dev), &error_fatal); /* @@ -199,11 +201,8 @@ static void virt_init(MachineState *machine) sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_GF_TTY_IRQ_BASE)); /* virt controller */ - dev = qdev_new(TYPE_VIRT_CTRL); - sysbus = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(sysbus, &error_fatal); - sysbus_mmio_map(sysbus, 0, VIRT_CTRL_MMIO_BASE); - sysbus_connect_irq(sysbus, 0, PIC_GPIO(VIRT_CTRL_IRQ_BASE)); + dev = sysbus_create_simple(TYPE_VIRT_CTRL, VIRT_CTRL_MMIO_BASE, + PIC_GPIO(VIRT_CTRL_IRQ_BASE)); /* virtio-mmio */ io_base = VIRT_VIRTIO_MMIO_BASE; diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index dba41afe67..cc8a8c1418 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -197,4 +197,7 @@ config DJMEMC config IOSB bool +config XLNX_VERSAL_TRNG + bool + source macio/Kconfig diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 4c830fd89a..85af466c2b 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -15,18 +15,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" - -#ifndef DEBUG_IMX6_CCM -#define DEBUG_IMX6_CCM 0 -#endif - -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_IMX6_CCM) { \ - fprintf(stderr, "[%s]%s: " fmt , TYPE_IMX6_CCM, \ - __func__, ##args); \ - } \ - } while (0) +#include "trace.h" static const char *imx6_ccm_reg_name(uint32_t reg) { @@ -263,7 +252,7 @@ static uint64_t imx6_analog_get_pll2_clk(IMX6CCMState *dev) freq *= 20; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_clk(freq); return freq; } @@ -275,7 +264,7 @@ static uint64_t imx6_analog_get_pll2_pfd0_clk(IMX6CCMState *dev) freq = imx6_analog_get_pll2_clk(dev) * 18 / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD0_FRAC); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_pfd0_clk(freq); return freq; } @@ -287,7 +276,7 @@ static uint64_t imx6_analog_get_pll2_pfd2_clk(IMX6CCMState *dev) freq = imx6_analog_get_pll2_clk(dev) * 18 / EXTRACT(dev->analog[CCM_ANALOG_PFD_528], PFD2_FRAC); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_pll2_pfd2_clk(freq); return freq; } @@ -315,7 +304,7 @@ static uint64_t imx6_analog_get_periph_clk(IMX6CCMState *dev) break; } - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_analog_get_periph_clk(freq); return freq; } @@ -327,7 +316,7 @@ static uint64_t imx6_ccm_get_ahb_clk(IMX6CCMState *dev) freq = imx6_analog_get_periph_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CBCDR], AHB_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_ahb_clk(freq); return freq; } @@ -339,7 +328,7 @@ static uint64_t imx6_ccm_get_ipg_clk(IMX6CCMState *dev) freq = imx6_ccm_get_ahb_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CBCDR], IPG_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_ipg_clk(freq); return freq; } @@ -351,7 +340,7 @@ static uint64_t imx6_ccm_get_per_clk(IMX6CCMState *dev) freq = imx6_ccm_get_ipg_clk(dev) / (1 + EXTRACT(dev->ccm[CCM_CSCMR1], PERCLK_PODF)); - DPRINTF("freq = %u\n", (uint32_t)freq); + trace_imx6_ccm_get_per_clk(freq); return freq; } @@ -385,7 +374,7 @@ static uint32_t imx6_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) break; } - DPRINTF("Clock = %d) = %u\n", clock, freq); + trace_imx6_ccm_get_clock_frequency(clock, freq); return freq; } @@ -394,7 +383,7 @@ static void imx6_ccm_reset(DeviceState *dev) { IMX6CCMState *s = IMX6_CCM(dev); - DPRINTF("\n"); + trace_imx6_ccm_reset(); s->ccm[CCM_CCR] = 0x040116FF; s->ccm[CCM_CCDR] = 0x00000000; @@ -483,7 +472,7 @@ static uint64_t imx6_ccm_read(void *opaque, hwaddr offset, unsigned size) value = s->ccm[index]; - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), value); + trace_imx6_ccm_read(imx6_ccm_reg_name(index), value); return (uint64_t)value; } @@ -494,8 +483,7 @@ static void imx6_ccm_write(void *opaque, hwaddr offset, uint64_t value, uint32_t index = offset >> 2; IMX6CCMState *s = (IMX6CCMState *)opaque; - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_ccm_reg_name(index), - (uint32_t)value); + trace_imx6_ccm_write(imx6_ccm_reg_name(index), (uint32_t)value); /* * We will do a better implementation later. In particular some bits @@ -591,7 +579,7 @@ static uint64_t imx6_analog_read(void *opaque, hwaddr offset, unsigned size) break; } - DPRINTF("reg[%s] => 0x%" PRIx32 "\n", imx6_analog_reg_name(index), value); + trace_imx6_analog_read(imx6_analog_reg_name(index), value); return (uint64_t)value; } @@ -602,8 +590,7 @@ static void imx6_analog_write(void *opaque, hwaddr offset, uint64_t value, uint32_t index = offset >> 2; IMX6CCMState *s = (IMX6CCMState *)opaque; - DPRINTF("reg[%s] <= 0x%" PRIx32 "\n", imx6_analog_reg_name(index), - (uint32_t)value); + trace_imx6_analog_write(imx6_analog_reg_name(index), (uint32_t)value); switch (index) { case CCM_ANALOG_PLL_ARM_SET: diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index ee7698bd9c..a245f96cd4 100644 --- a/hw/misc/imx7_snvs.c +++ b/hw/misc/imx7_snvs.c @@ -16,9 +16,12 @@ #include "hw/misc/imx7_snvs.h" #include "qemu/module.h" #include "sysemu/runstate.h" +#include "trace.h" static uint64_t imx7_snvs_read(void *opaque, hwaddr offset, unsigned size) { + trace_imx7_snvs_read(offset, 0); + return 0; } @@ -28,6 +31,8 @@ static void imx7_snvs_write(void *opaque, hwaddr offset, const uint32_t value = v; const uint32_t mask = SNVS_LPCR_TOP | SNVS_LPCR_DP_EN; + trace_imx7_snvs_write(offset, value); + if (offset == SNVS_LPCR && ((value & mask) == mask)) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } diff --git a/hw/misc/meson.build b/hw/misc/meson.build index f60de33f9a..36c20d5637 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -104,6 +104,9 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files( 'xlnx-cfi-if.c', 'xlnx-versal-cframe-reg.c', )) +system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files( + 'xlnx-versal-trng.c', +)) system_ss.add(when: 'CONFIG_STM32F2XX_SYSCFG', if_true: files('stm32f2xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_SYSCFG', if_true: files('stm32f4xx_syscfg.c')) system_ss.add(when: 'CONFIG_STM32F4XX_EXTI', if_true: files('stm32f4xx_exti.c')) diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 24ba7cc4d0..05ff692441 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -115,6 +115,10 @@ msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status regist imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 +# imx7_snvs.c +imx7_snvs_read(uint64_t offset, uint32_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx32 +imx7_snvs_write(uint64_t offset, uint32_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx32 + # mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d counter=0x%"PRIx64 " delta_next=0x%"PRIx64 @@ -192,6 +196,21 @@ iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit Sec iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u" iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u" +# imx6_ccm.c +imx6_analog_get_periph_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_pfd0_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_get_pll2_pfd2_clk(uint32_t freq) "freq = %u Hz" +imx6_analog_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_analog_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32 +imx6_ccm_get_ahb_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_ipg_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_per_clk(uint32_t freq) "freq = %u Hz" +imx6_ccm_get_clock_frequency(unsigned clock, uint32_t freq) "(Clock = %d) = %u" +imx6_ccm_read(const char *reg, uint32_t value) "reg[%s] => 0x%" PRIx32 +imx6_ccm_reset(void) "" +imx6_ccm_write(const char *reg, uint32_t value) "reg[%s] <= 0x%" PRIx32 + # imx6ul_ccm.c ccm_entry(void) "" ccm_freq(uint32_t freq) "freq = %d" diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c new file mode 100644 index 0000000000..4d41c262c4 --- /dev/null +++ b/hw/misc/xlnx-versal-trng.c @@ -0,0 +1,717 @@ +/* + * Non-crypto strength model of the True Random Number Generator + * in the AMD/Xilinx Versal device family. + * + * Copyright (c) 2017-2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.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 "qemu/osdep.h" +#include "hw/misc/xlnx-versal-trng.h" + +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qemu/guest-random.h" +#include "qemu/timer.h" +#include "qapi/visitor.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" + +#ifndef XLNX_VERSAL_TRNG_ERR_DEBUG +#define XLNX_VERSAL_TRNG_ERR_DEBUG 0 +#endif + +REG32(INT_CTRL, 0x0) + FIELD(INT_CTRL, CERTF_RST, 5, 1) + FIELD(INT_CTRL, DTF_RST, 4, 1) + FIELD(INT_CTRL, DONE_RST, 3, 1) + FIELD(INT_CTRL, CERTF_EN, 2, 1) + FIELD(INT_CTRL, DTF_EN, 1, 1) + FIELD(INT_CTRL, DONE_EN, 0, 1) +REG32(STATUS, 0x4) + FIELD(STATUS, QCNT, 9, 3) + FIELD(STATUS, EAT, 4, 5) + FIELD(STATUS, CERTF, 3, 1) + FIELD(STATUS, DTF, 1, 1) + FIELD(STATUS, DONE, 0, 1) +REG32(CTRL, 0x8) + FIELD(CTRL, PERSODISABLE, 10, 1) + FIELD(CTRL, SINGLEGENMODE, 9, 1) + FIELD(CTRL, EUMODE, 8, 1) + FIELD(CTRL, PRNGMODE, 7, 1) + FIELD(CTRL, TSTMODE, 6, 1) + FIELD(CTRL, PRNGSTART, 5, 1) + FIELD(CTRL, EATAU, 4, 1) + FIELD(CTRL, PRNGXS, 3, 1) + FIELD(CTRL, TRSSEN, 2, 1) + FIELD(CTRL, QERTUEN, 1, 1) + FIELD(CTRL, PRNGSRST, 0, 1) +REG32(CTRL_2, 0xc) + FIELD(CTRL_2, REPCOUNTTESTCUTOFF, 8, 9) + FIELD(CTRL_2, RESERVED_7_5, 5, 3) + FIELD(CTRL_2, DIT, 0, 5) +REG32(CTRL_3, 0x10) + FIELD(CTRL_3, ADAPTPROPTESTCUTOFF, 8, 10) + FIELD(CTRL_3, DLEN, 0, 8) +REG32(CTRL_4, 0x14) + FIELD(CTRL_4, SINGLEBITRAW, 0, 1) +REG32(EXT_SEED_0, 0x40) +REG32(EXT_SEED_1, 0x44) +REG32(EXT_SEED_2, 0x48) +REG32(EXT_SEED_3, 0x4c) +REG32(EXT_SEED_4, 0x50) +REG32(EXT_SEED_5, 0x54) +REG32(EXT_SEED_6, 0x58) +REG32(EXT_SEED_7, 0x5c) +REG32(EXT_SEED_8, 0x60) +REG32(EXT_SEED_9, 0x64) +REG32(EXT_SEED_10, 0x68) +REG32(EXT_SEED_11, 0x6c) +REG32(PER_STRNG_0, 0x80) +REG32(PER_STRNG_1, 0x84) +REG32(PER_STRNG_2, 0x88) +REG32(PER_STRNG_3, 0x8c) +REG32(PER_STRNG_4, 0x90) +REG32(PER_STRNG_5, 0x94) +REG32(PER_STRNG_6, 0x98) +REG32(PER_STRNG_7, 0x9c) +REG32(PER_STRNG_8, 0xa0) +REG32(PER_STRNG_9, 0xa4) +REG32(PER_STRNG_10, 0xa8) +REG32(PER_STRNG_11, 0xac) +REG32(CORE_OUTPUT, 0xc0) +REG32(RESET, 0xd0) + FIELD(RESET, VAL, 0, 1) +REG32(OSC_EN, 0xd4) + FIELD(OSC_EN, VAL, 0, 1) +REG32(TRNG_ISR, 0xe0) + FIELD(TRNG_ISR, SLVERR, 1, 1) + FIELD(TRNG_ISR, CORE_INT, 0, 1) +REG32(TRNG_IMR, 0xe4) + FIELD(TRNG_IMR, SLVERR, 1, 1) + FIELD(TRNG_IMR, CORE_INT, 0, 1) +REG32(TRNG_IER, 0xe8) + FIELD(TRNG_IER, SLVERR, 1, 1) + FIELD(TRNG_IER, CORE_INT, 0, 1) +REG32(TRNG_IDR, 0xec) + FIELD(TRNG_IDR, SLVERR, 1, 1) + FIELD(TRNG_IDR, CORE_INT, 0, 1) +REG32(SLV_ERR_CTRL, 0xf0) + FIELD(SLV_ERR_CTRL, ENABLE, 0, 1) + +#define R_MAX (R_SLV_ERR_CTRL + 1) + +QEMU_BUILD_BUG_ON(R_MAX * 4 != sizeof_field(XlnxVersalTRng, regs)); + +#define TRNG_GUEST_ERROR(D, FMT, ...) \ + do { \ + g_autofree char *p = object_get_canonical_path(OBJECT(D)); \ + qemu_log_mask(LOG_GUEST_ERROR, "%s: " FMT, p, ## __VA_ARGS__); \ + } while (0) + +#define TRNG_WARN(D, FMT, ...) \ + do { \ + g_autofree char *p = object_get_canonical_path(OBJECT(D)); \ + warn_report("%s: " FMT, p, ## __VA_ARGS__); \ + } while (0) + +static bool trng_older_than_v2(XlnxVersalTRng *s) +{ + return s->hw_version < 0x0200; +} + +static bool trng_in_reset(XlnxVersalTRng *s) +{ + if (ARRAY_FIELD_EX32(s->regs, RESET, VAL)) { + return true; + } + if (ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSRST)) { + return true; + } + + return false; +} + +static bool trng_test_enabled(XlnxVersalTRng *s) +{ + return ARRAY_FIELD_EX32(s->regs, CTRL, TSTMODE); +} + +static bool trng_trss_enabled(XlnxVersalTRng *s) +{ + if (trng_in_reset(s)) { + return false; + } + if (!ARRAY_FIELD_EX32(s->regs, CTRL, TRSSEN)) { + return false; + } + if (!ARRAY_FIELD_EX32(s->regs, OSC_EN, VAL)) { + return false; + } + + return true; +} + +static void trng_seed_128(uint32_t *seed, uint64_t h00, uint64_t h64) +{ + seed[0] = extract64(h00, 0, 32); + seed[1] = extract64(h00, 32, 32); + seed[2] = extract64(h64, 0, 32); + seed[3] = extract64(h64, 32, 32); +} + +static void trng_reseed(XlnxVersalTRng *s) +{ + bool ext_seed = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGXS); + bool pers_disabled = ARRAY_FIELD_EX32(s->regs, CTRL, PERSODISABLE); + + enum { + U384_U8 = 384 / 8, + U384_U32 = 384 / 32, + }; + + /* + * Maximum seed length is len(personalized string) + len(ext seed). + * + * g_rand_set_seed_array() takes array of uint32 in host endian. + */ + guint32 gs[U384_U32 * 2], *seed = &gs[U384_U32]; + + /* + * A disabled personalized string is the same as + * a string with all zeros. + * + * The device's hardware spec defines 3 modes (all selectable + * by guest at will and at anytime): + * 1) External seeding + * This is a PRNG mode, in which the produced sequence shall + * be reproducible if reseeded by the same 384-bit seed, as + * supplied by guest software. + * 2) Test seeding + * This is a PRNG mode, in which the produced sequence shall + * be reproducible if reseeded by a 128-bit test seed, as + * supplied by guest software. + * 3) Truly-random seeding + * This is the TRNG mode, in which the produced sequence is + * periodically reseeded by a crypto-strength entropy source. + * + * To assist debugging of certain classes of software defects, + * this QEMU model implements a 4th mode, + * 4) Forced PRNG + * When in this mode, a reproducible sequence is generated + * if software has selected the TRNG mode (mode 2). + * + * This emulation-only mode can only be selected by setting + * the uint64 property 'forced-prng' to a non-zero value. + * Guest software cannot select this mode. + */ + memset(gs, 0, sizeof(gs)); + + if (!pers_disabled) { + memcpy(gs, &s->regs[R_PER_STRNG_0], U384_U8); + } + + if (ext_seed) { + memcpy(seed, &s->regs[R_EXT_SEED_0], U384_U8); + } else if (trng_test_enabled(s)) { + trng_seed_128(seed, s->tst_seed[0], s->tst_seed[1]); + } else if (s->forced_prng_seed) { + s->forced_prng_count++; + trng_seed_128(seed, s->forced_prng_count, s->forced_prng_seed); + } else { + qemu_guest_getrandom_nofail(seed, U384_U8); + } + + g_rand_set_seed_array(s->prng, gs, ARRAY_SIZE(gs)); + + s->rand_count = 0; + s->rand_reseed = 1ULL << 48; +} + +static void trng_regen(XlnxVersalTRng *s) +{ + if (s->rand_reseed == 0) { + TRNG_GUEST_ERROR(s, "Too many generations without a reseed"); + trng_reseed(s); + } + s->rand_reseed--; + + /* + * In real hardware, each regen creates 256 bits, but QCNT + * reports a max of 4. + */ + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, 4); + s->rand_count = 256 / 32; +} + +static uint32_t trng_rdout(XlnxVersalTRng *s) +{ + assert(s->rand_count); + + s->rand_count--; + if (s->rand_count < 4) { + ARRAY_FIELD_DP32(s->regs, STATUS, QCNT, s->rand_count); + } + + return g_rand_int(s->prng); +} + +static void trng_irq_update(XlnxVersalTRng *s) +{ + bool pending = s->regs[R_TRNG_ISR] & ~s->regs[R_TRNG_IMR]; + qemu_set_irq(s->irq, pending); +} + +static void trng_isr_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + trng_irq_update(s); +} + +static uint64_t trng_ier_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t val = val64; + + s->regs[R_TRNG_IMR] &= ~val; + trng_irq_update(s); + return 0; +} + +static uint64_t trng_idr_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t val = val64; + + s->regs[R_TRNG_IMR] |= val; + trng_irq_update(s); + return 0; +} + +static void trng_core_int_update(XlnxVersalTRng *s) +{ + bool pending = false; + uint32_t st = s->regs[R_STATUS]; + uint32_t en = s->regs[R_INT_CTRL]; + + if (FIELD_EX32(st, STATUS, CERTF) && FIELD_EX32(en, INT_CTRL, CERTF_EN)) { + pending = true; + } + + if (FIELD_EX32(st, STATUS, DTF) && FIELD_EX32(en, INT_CTRL, DTF_EN)) { + pending = true; + } + + if (FIELD_EX32(st, STATUS, DONE) && FIELD_EX32(en, INT_CTRL, DONE_EN)) { + pending = true; + } + + ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, pending); + trng_irq_update(s); +} + +static void trng_int_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + uint32_t v32 = val64; + uint32_t clr_mask = 0; + + if (FIELD_EX32(v32, INT_CTRL, CERTF_RST)) { + clr_mask |= R_STATUS_CERTF_MASK; + } + if (FIELD_EX32(v32, INT_CTRL, DTF_RST)) { + clr_mask |= R_STATUS_DTF_MASK; + } + if (FIELD_EX32(v32, INT_CTRL, DONE_RST)) { + clr_mask |= R_STATUS_DONE_MASK; + } + + s->regs[R_STATUS] &= ~clr_mask; + trng_core_int_update(s); +} + +static void trng_done(XlnxVersalTRng *s) +{ + ARRAY_FIELD_DP32(s->regs, STATUS, DONE, true); + trng_core_int_update(s); +} + +static void trng_fault_event_set(XlnxVersalTRng *s, uint32_t events) +{ + bool pending = false; + + /* Disabled TRSS cannot generate any fault event */ + if (!trng_trss_enabled(s)) { + return; + } + + if (FIELD_EX32(events, STATUS, CERTF)) { + /* In older version, ERTU must be enabled explicitly to get CERTF */ + if (trng_older_than_v2(s) && + !ARRAY_FIELD_EX32(s->regs, CTRL, QERTUEN)) { + TRNG_WARN(s, "CERTF injection ignored: ERTU disabled"); + } else { + ARRAY_FIELD_DP32(s->regs, STATUS, CERTF, true); + pending = true; + } + } + + if (FIELD_EX32(events, STATUS, DTF)) { + ARRAY_FIELD_DP32(s->regs, STATUS, DTF, true); + pending = true; + } + + if (pending) { + trng_core_int_update(s); + } +} + +static void trng_soft_reset(XlnxVersalTRng *s) +{ + s->rand_count = 0; + s->regs[R_STATUS] = 0; + + ARRAY_FIELD_DP32(s->regs, TRNG_ISR, CORE_INT, 0); +} + +static void trng_ctrl_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + if (trng_in_reset(s)) { + return; + } + + if (FIELD_EX32(val64, CTRL, PRNGSRST)) { + trng_soft_reset(s); + trng_irq_update(s); + return; + } + + if (!FIELD_EX32(val64, CTRL, PRNGSTART)) { + return; + } + + if (FIELD_EX32(val64, CTRL, PRNGMODE)) { + trng_regen(s); + } else { + trng_reseed(s); + } + + trng_done(s); +} + +static void trng_ctrl4_postw(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + /* Only applies to test mode with TRSS enabled */ + if (!trng_test_enabled(s) || !trng_trss_enabled(s)) { + return; + } + + /* Shift in a single bit. */ + s->tst_seed[1] <<= 1; + s->tst_seed[1] |= s->tst_seed[0] >> 63; + s->tst_seed[0] <<= 1; + s->tst_seed[0] |= val64 & 1; + + trng_reseed(s); + trng_regen(s); +} + +static uint64_t trng_core_out_postr(RegisterInfo *reg, uint64_t val) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + bool oneshot = ARRAY_FIELD_EX32(s->regs, CTRL, SINGLEGENMODE); + bool start = ARRAY_FIELD_EX32(s->regs, CTRL, PRNGSTART); + uint32_t r = 0xbad; + + if (trng_in_reset(s)) { + TRNG_GUEST_ERROR(s, "Reading random number while in reset!"); + return r; + } + + if (s->rand_count == 0) { + TRNG_GUEST_ERROR(s, "Reading random number when unavailable!"); + return r; + } + + r = trng_rdout(s); + + /* Automatic mode regenerates when half the output reg is empty. */ + if (!oneshot && start && s->rand_count <= 3) { + trng_regen(s); + } + + return r; +} + +static void trng_reset(XlnxVersalTRng *s) +{ + unsigned int i; + + s->forced_prng_count = 0; + + for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { + register_reset(&s->regs_info[i]); + } + trng_soft_reset(s); + trng_irq_update(s); +} + +static uint64_t trng_reset_prew(RegisterInfo *reg, uint64_t val64) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg->opaque); + + if (!ARRAY_FIELD_EX32(s->regs, RESET, VAL) && + FIELD_EX32(val64, RESET, VAL)) { + trng_reset(s); + } + + return val64; +} + +static uint64_t trng_register_read(void *opaque, hwaddr addr, unsigned size) +{ + /* + * Guest provided seed and personalized strings cannot be + * read back, and read attempts return value of A_STATUS. + */ + switch (addr) { + case A_EXT_SEED_0 ... A_PER_STRNG_11: + addr = A_STATUS; + break; + } + + return register_read_memory(opaque, addr, size); +} + +static void trng_register_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + RegisterInfoArray *reg_array = opaque; + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(reg_array->r[0]->opaque); + + if (trng_older_than_v2(s)) { + switch (addr) { + case A_CTRL: + value = FIELD_DP64(value, CTRL, PERSODISABLE, 0); + value = FIELD_DP64(value, CTRL, SINGLEGENMODE, 0); + break; + case A_CTRL_2: + case A_CTRL_3: + case A_CTRL_4: + return; + } + } else { + switch (addr) { + case A_CTRL: + value = FIELD_DP64(value, CTRL, EATAU, 0); + value = FIELD_DP64(value, CTRL, QERTUEN, 0); + break; + } + } + + register_write_memory(opaque, addr, value, size); +} + +static RegisterAccessInfo trng_regs_info[] = { + { .name = "INT_CTRL", .addr = A_INT_CTRL, + .post_write = trng_int_ctrl_postw, + },{ .name = "STATUS", .addr = A_STATUS, + .ro = 0xfff, + },{ .name = "CTRL", .addr = A_CTRL, + .post_write = trng_ctrl_postw, + },{ .name = "CTRL_2", .addr = A_CTRL_2, + .reset = 0x210c, + },{ .name = "CTRL_3", .addr = A_CTRL_3, + .reset = 0x26f09, + },{ .name = "CTRL_4", .addr = A_CTRL_4, + .post_write = trng_ctrl4_postw, + },{ .name = "EXT_SEED_0", .addr = A_EXT_SEED_0, + },{ .name = "EXT_SEED_1", .addr = A_EXT_SEED_1, + },{ .name = "EXT_SEED_2", .addr = A_EXT_SEED_2, + },{ .name = "EXT_SEED_3", .addr = A_EXT_SEED_3, + },{ .name = "EXT_SEED_4", .addr = A_EXT_SEED_4, + },{ .name = "EXT_SEED_5", .addr = A_EXT_SEED_5, + },{ .name = "EXT_SEED_6", .addr = A_EXT_SEED_6, + },{ .name = "EXT_SEED_7", .addr = A_EXT_SEED_7, + },{ .name = "EXT_SEED_8", .addr = A_EXT_SEED_8, + },{ .name = "EXT_SEED_9", .addr = A_EXT_SEED_9, + },{ .name = "EXT_SEED_10", .addr = A_EXT_SEED_10, + },{ .name = "EXT_SEED_11", .addr = A_EXT_SEED_11, + },{ .name = "PER_STRNG_0", .addr = A_PER_STRNG_0, + },{ .name = "PER_STRNG_1", .addr = A_PER_STRNG_1, + },{ .name = "PER_STRNG_2", .addr = A_PER_STRNG_2, + },{ .name = "PER_STRNG_3", .addr = A_PER_STRNG_3, + },{ .name = "PER_STRNG_4", .addr = A_PER_STRNG_4, + },{ .name = "PER_STRNG_5", .addr = A_PER_STRNG_5, + },{ .name = "PER_STRNG_6", .addr = A_PER_STRNG_6, + },{ .name = "PER_STRNG_7", .addr = A_PER_STRNG_7, + },{ .name = "PER_STRNG_8", .addr = A_PER_STRNG_8, + },{ .name = "PER_STRNG_9", .addr = A_PER_STRNG_9, + },{ .name = "PER_STRNG_10", .addr = A_PER_STRNG_10, + },{ .name = "PER_STRNG_11", .addr = A_PER_STRNG_11, + },{ .name = "CORE_OUTPUT", .addr = A_CORE_OUTPUT, + .ro = 0xffffffff, + .post_read = trng_core_out_postr, + },{ .name = "RESET", .addr = A_RESET, + .reset = 0x1, + .pre_write = trng_reset_prew, + },{ .name = "OSC_EN", .addr = A_OSC_EN, + },{ .name = "TRNG_ISR", .addr = A_TRNG_ISR, + .w1c = 0x3, + .post_write = trng_isr_postw, + },{ .name = "TRNG_IMR", .addr = A_TRNG_IMR, + .reset = 0x3, + .ro = 0x3, + },{ .name = "TRNG_IER", .addr = A_TRNG_IER, + .pre_write = trng_ier_prew, + },{ .name = "TRNG_IDR", .addr = A_TRNG_IDR, + .pre_write = trng_idr_prew, + },{ .name = "SLV_ERR_CTRL", .addr = A_SLV_ERR_CTRL, + } +}; + +static const MemoryRegionOps trng_ops = { + .read = trng_register_read, + .write = trng_register_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static void trng_init(Object *obj) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + RegisterInfoArray *reg_array; + + reg_array = + register_init_block32(DEVICE(obj), trng_regs_info, + ARRAY_SIZE(trng_regs_info), + s->regs_info, s->regs, + &trng_ops, + XLNX_VERSAL_TRNG_ERR_DEBUG, + R_MAX * 4); + + sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_irq(sbd, &s->irq); + + s->prng = g_rand_new(); +} + +static void trng_unrealize(DeviceState *dev) +{ + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(dev); + + g_rand_free(s->prng); + s->prng = NULL; +} + +static void trng_reset_hold(Object *obj) +{ + trng_reset(XLNX_VERSAL_TRNG(obj)); +} + +static void trng_prop_fault_event_set(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + uint32_t *events = object_field_prop_ptr(obj, prop); + + visit_type_uint32(v, name, events, errp); + if (*errp) { + return; + } + + trng_fault_event_set(XLNX_VERSAL_TRNG(obj), *events); +} + +static const PropertyInfo trng_prop_fault_events = { + .name = "uint32:bits", + .description = "Set to trigger TRNG fault events", + .set = trng_prop_fault_event_set, + .realized_set_allowed = true, +}; + +static PropertyInfo trng_prop_uint64; /* to extend qdev_prop_uint64 */ + +static Property trng_props[] = { + DEFINE_PROP_UINT64("forced-prng", XlnxVersalTRng, forced_prng_seed, 0), + DEFINE_PROP_UINT32("hw-version", XlnxVersalTRng, hw_version, 0x0200), + DEFINE_PROP("fips-fault-events", XlnxVersalTRng, forced_faults, + trng_prop_fault_events, uint32_t), + + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_trng = { + .name = TYPE_XLNX_VERSAL_TRNG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(rand_count, XlnxVersalTRng), + VMSTATE_UINT64(rand_reseed, XlnxVersalTRng), + VMSTATE_UINT64(forced_prng_count, XlnxVersalTRng), + VMSTATE_UINT64_ARRAY(tst_seed, XlnxVersalTRng, 2), + VMSTATE_UINT32_ARRAY(regs, XlnxVersalTRng, R_MAX), + VMSTATE_END_OF_LIST(), + } +}; + +static void trng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + dc->vmsd = &vmstate_trng; + dc->unrealize = trng_unrealize; + rc->phases.hold = trng_reset_hold; + + /* Clone uint64 property with set allowed after realized */ + trng_prop_uint64 = qdev_prop_uint64; + trng_prop_uint64.realized_set_allowed = true; + trng_props[0].info = &trng_prop_uint64; + + device_class_set_props(dc, trng_props); +} + +static const TypeInfo trng_info = { + .name = TYPE_XLNX_VERSAL_TRNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XlnxVersalTRng), + .class_init = trng_class_init, + .instance_init = trng_init, +}; + +static void trng_register_types(void) +{ + type_register_static(&trng_info); +} + +type_init(trng_register_types) diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index dc07984ae9..94ce9e18ff 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -1883,8 +1883,7 @@ static void e100_nic_realize(PCIDevice *pci_dev, Error **errp) s->vmstate = g_memdup(&vmstate_eepro100, sizeof(vmstate_eepro100)); s->vmstate->name = qemu_get_queue(s->nic)->model; - vmstate_register(VMSTATE_IF(&pci_dev->qdev), VMSTATE_INSTANCE_ID_ANY, - s->vmstate, s); + vmstate_register_any(VMSTATE_IF(&pci_dev->qdev), s->vmstate, s); } static void eepro100_instance_init(Object *obj) diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 1081e2cc0d..57d63638d7 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -321,7 +321,7 @@ eeprom_t *eeprom93xx_new(DeviceState *dev, uint16_t nwords) /* Output DO is tristate, read results in 1. */ eeprom->eedo = 1; logout("eeprom = 0x%p, nwords = %u\n", eeprom, nwords); - vmstate_register(VMSTATE_IF(dev), 0, &vmstate_eeprom, eeprom); + vmstate_register_any(VMSTATE_IF(dev), &vmstate_eeprom, eeprom); return eeprom; } diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 7d09e1a39d..885c04b6f5 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -147,7 +147,7 @@ static void pci_bus_realize(BusState *qbus, Error **errp) bus->machine_done.notify = pcibus_machine_done; qemu_add_machine_init_done_notifier(&bus->machine_done); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_pcibus, bus); + vmstate_register_any(NULL, &vmstate_pcibus, bus); } static void pcie_bus_realize(BusState *qbus, Error **errp) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b25093be28..df09aa9d6a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -143,6 +143,11 @@ static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque) } static const VMStateDescription pre_2_10_vmstate_dummy_icp = { + /* + * Hack ahead. We can't have two devices with the same name and + * instance id. So I rename this to pass make check. + * Real help from people who knows the hardware is needed. + */ .name = "icp/server", .version_id = 1, .minimum_version_id = 1, @@ -155,16 +160,32 @@ static const VMStateDescription pre_2_10_vmstate_dummy_icp = { }, }; +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * You have to remove vmstate_replace_hack_for_ppc() when you remove + * the machine types that need the following function. + */ static void pre_2_10_vmstate_register_dummy_icp(int i) { vmstate_register(NULL, i, &pre_2_10_vmstate_dummy_icp, (void *)(uintptr_t) i); } +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * You have to remove vmstate_replace_hack_for_ppc() when you remove + * the machine types that need the following function. + */ static void pre_2_10_vmstate_unregister_dummy_icp(int i) { - vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, - (void *)(uintptr_t) i); + /* + * This used to be: + * + * vmstate_unregister(NULL, &pre_2_10_vmstate_dummy_icp, + * (void *)(uintptr_t) i); + */ } int spapr_max_server_number(SpaprMachineState *spapr) diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index b2f009c816..ad7afe7544 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -876,8 +876,7 @@ static void spapr_nvdimm_realize(NVDIMMDevice *dimm, Error **errp) s_nvdimm->hcall_flush_required = true; } - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, - &vmstate_spapr_nvdimm_states, dimm); + vmstate_register_any(NULL, &vmstate_spapr_nvdimm_states, dimm); } static void spapr_nvdimm_unrealize(NVDIMMDevice *dimm) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 5024faf411..8f5159d85d 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "hw/boards.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc-target.h" @@ -432,58 +433,39 @@ static int s390_storage_keys_load(QEMUFile *f, void *opaque, int version_id) return ret; } -static inline bool s390_skeys_get_migration_enabled(Object *obj, Error **errp) -{ - S390SKeysState *ss = S390_SKEYS(obj); - - return ss->migration_enabled; -} - static SaveVMHandlers savevm_s390_storage_keys = { .save_state = s390_storage_keys_save, .load_state = s390_storage_keys_load, }; -static inline void s390_skeys_set_migration_enabled(Object *obj, bool value, - Error **errp) +static void s390_skeys_realize(DeviceState *dev, Error **errp) { - S390SKeysState *ss = S390_SKEYS(obj); - - /* Prevent double registration of savevm handler */ - if (ss->migration_enabled == value) { - return; - } - - ss->migration_enabled = value; + S390SKeysState *ss = S390_SKEYS(dev); if (ss->migration_enabled) { register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); - } else { - unregister_savevm(VMSTATE_IF(ss), TYPE_S390_SKEYS, ss); } } -static void s390_skeys_instance_init(Object *obj) -{ - object_property_add_bool(obj, "migration-enabled", - s390_skeys_get_migration_enabled, - s390_skeys_set_migration_enabled); - object_property_set_bool(obj, "migration-enabled", true, NULL); -} +static Property s390_skeys_props[] = { + DEFINE_PROP_BOOL("migration-enabled", S390SKeysState, migration_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; static void s390_skeys_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->hotpluggable = false; + dc->realize = s390_skeys_realize; + device_class_set_props(dc, s390_skeys_props); set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static const TypeInfo s390_skeys_info = { .name = TYPE_S390_SKEYS, .parent = TYPE_DEVICE, - .instance_init = s390_skeys_instance_init, .instance_size = sizeof(S390SKeysState), .class_init = s390_skeys_class_init, .class_size = sizeof(S390SKeysClass), diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 220e845d12..c483b62a9b 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -13,6 +13,7 @@ #include "qemu/units.h" #include "migration/qemu-file.h" #include "migration/register.h" +#include "hw/qdev-properties.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" #include "exec/ram_addr.h" @@ -330,6 +331,17 @@ static const TypeInfo qemu_s390_stattrib_info = { /* Generic abstract object: */ +static SaveVMHandlers savevm_s390_stattrib_handlers = { + .save_setup = cmma_save_setup, + .save_live_iterate = cmma_save_iterate, + .save_live_complete_precopy = cmma_save_complete, + .state_pending_exact = cmma_state_pending, + .state_pending_estimate = cmma_state_pending, + .save_cleanup = cmma_save_cleanup, + .load_state = cmma_load, + .is_active = cmma_active, +}; + static void s390_stattrib_realize(DeviceState *dev, Error **errp) { bool ambiguous = false; @@ -337,9 +349,18 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) object_resolve_path_type("", TYPE_S390_STATTRIB, &ambiguous); if (ambiguous) { error_setg(errp, "storage_attributes device already exists"); + return; } + + register_savevm_live(TYPE_S390_STATTRIB, 0, 0, + &savevm_s390_stattrib_handlers, dev); } +static Property s390_stattrib_props[] = { + DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), + DEFINE_PROP_END_OF_LIST(), +}; + static void s390_stattrib_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -347,46 +368,13 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->realize = s390_stattrib_realize; + device_class_set_props(dc, s390_stattrib_props); } -static inline bool s390_stattrib_get_migration_enabled(Object *obj, - Error **errp) -{ - S390StAttribState *s = S390_STATTRIB(obj); - - return s->migration_enabled; -} - -static inline void s390_stattrib_set_migration_enabled(Object *obj, bool value, - Error **errp) -{ - S390StAttribState *s = S390_STATTRIB(obj); - - s->migration_enabled = value; -} - -static SaveVMHandlers savevm_s390_stattrib_handlers = { - .save_setup = cmma_save_setup, - .save_live_iterate = cmma_save_iterate, - .save_live_complete_precopy = cmma_save_complete, - .state_pending_exact = cmma_state_pending, - .state_pending_estimate = cmma_state_pending, - .save_cleanup = cmma_save_cleanup, - .load_state = cmma_load, - .is_active = cmma_active, -}; - static void s390_stattrib_instance_init(Object *obj) { S390StAttribState *sas = S390_STATTRIB(obj); - register_savevm_live(TYPE_S390_STATTRIB, 0, 0, - &savevm_s390_stattrib_handlers, sas); - - object_property_add_bool(obj, "migration-enabled", - s390_stattrib_get_migration_enabled, - s390_stattrib_set_migration_enabled); - object_property_set_bool(obj, "migration-enabled", true, NULL); sas->migration_cur_gfn = 0; } diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 5d9e06a9bb..3126df9e1d 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -210,7 +210,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) "When external environment supports it (Orchestrator migrates " "target SCSI device state or use shared storage over network), " "set 'migratable' property to true to enable migration."); - if (migrate_add_blocker(&vsc->migration_blocker, errp) < 0) { + if (migrate_add_blocker_normal(&vsc->migration_blocker, errp) < 0) { goto free_virtio; } } diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 69c8863472..9afe8da831 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -181,7 +181,7 @@ static arm_timer_state *arm_timer_init(uint32_t freq) s->control = TIMER_CTRL_IE; s->timer = ptimer_init(arm_timer_tick, s, PTIMER_POLICY_LEGACY); - vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_arm_timer, s); + vmstate_register_any(NULL, &vmstate_arm_timer, s); return s; } diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index aa7b272452..9c9ae7109e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1527,7 +1527,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, } if (hdev->migration_blocker != NULL) { - r = migrate_add_blocker(&hdev->migration_blocker, errp); + r = migrate_add_blocker_normal(&hdev->migration_blocker, errp); if (r < 0) { goto fail_busyloop; } diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 9dc3c61b5a..a5ea3be414 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1119,8 +1119,8 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) host_memory_backend_set_mapped(vmem->memdev, true); vmstate_register_ram(&vmem->memdev->mr, DEVICE(vmem)); if (vmem->early_migration) { - vmstate_register(VMSTATE_IF(vmem), VMSTATE_INSTANCE_ID_ANY, - &vmstate_virtio_mem_device_early, vmem); + vmstate_register_any(VMSTATE_IF(vmem), + &vmstate_virtio_mem_device_early, vmem); } qemu_register_reset(virtio_mem_system_reset, vmem); diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events index 2739570652..ad3be1e9bd 100644 --- a/hw/watchdog/trace-events +++ b/hw/watchdog/trace-events @@ -17,6 +17,12 @@ cmsdk_apb_watchdog_lock(uint32_t lock) "CMSDK APB watchdog: lock %" PRIu32 aspeed_wdt_read(uint64_t addr, uint32_t size) "@0x%" PRIx64 " size=%d" aspeed_wdt_write(uint64_t addr, uint32_t size, uint64_t data) "@0x%" PRIx64 " size=%d value=0x%"PRIx64 +# wdt_imx2.c +imx2_wdt_read(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] -> 0x%" PRIx16 +imx2_wdt_write(uint32_t addr, uint16_t data) "[0x%" PRIx32 "] <- 0x%" PRIx16 +imx2_wdt_interrupt(void) "" +imx2_wdt_expired(void) "" + # spapr_watchdog.c spapr_watchdog_start(uint64_t flags, uint64_t num, uint64_t timeout) "Flags 0x%" PRIx64 " num=%" PRId64 " %" PRIu64 "ms" spapr_watchdog_stop(uint64_t num, uint64_t ret) "num=%" PRIu64 " ret=%" PRId64 diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index e776a2fbd4..891d7beb2a 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -17,11 +17,14 @@ #include "hw/qdev-properties.h" #include "hw/watchdog/wdt_imx2.h" +#include "trace.h" static void imx2_wdt_interrupt(void *opaque) { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_interrupt(); + s->wicr |= IMX2_WDT_WICR_WTIS; qemu_set_irq(s->irq, 1); } @@ -30,6 +33,8 @@ static void imx2_wdt_expired(void *opaque) { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_expired(); + s->wrsr = IMX2_WDT_WRSR_TOUT; /* Perform watchdog action if watchdog is enabled */ @@ -67,20 +72,29 @@ static void imx2_wdt_reset(DeviceState *dev) static uint64_t imx2_wdt_read(void *opaque, hwaddr addr, unsigned int size) { IMX2WdtState *s = IMX2_WDT(opaque); + uint16_t value = 0; switch (addr) { case IMX2_WDT_WCR: - return s->wcr; + value = s->wcr; + break; case IMX2_WDT_WSR: - return s->wsr; + value = s->wsr; + break; case IMX2_WDT_WRSR: - return s->wrsr; + value = s->wrsr; + break; case IMX2_WDT_WICR: - return s->wicr; + value = s->wicr; + break; case IMX2_WDT_WMCR: - return s->wmcr; + value = s->wmcr; + break; } - return 0; + + trace_imx2_wdt_read(addr, value); + + return value; } static void imx_wdt2_update_itimer(IMX2WdtState *s, bool start) @@ -137,6 +151,8 @@ static void imx2_wdt_write(void *opaque, hwaddr addr, { IMX2WdtState *s = IMX2_WDT(opaque); + trace_imx2_wdt_write(addr, value); + switch (addr) { case IMX2_WDT_WCR: if (s->wcr_locked) { diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h index b710d71fb0..b24fa64557 100644 --- a/include/hw/arm/xlnx-versal.h +++ b/include/hw/arm/xlnx-versal.h @@ -30,6 +30,7 @@ #include "hw/dma/xlnx_csu_dma.h" #include "hw/misc/xlnx-versal-crl.h" #include "hw/misc/xlnx-versal-pmc-iou-slcr.h" +#include "hw/misc/xlnx-versal-trng.h" #include "hw/net/xlnx-versal-canfd.h" #include "hw/misc/xlnx-versal-cfu.h" #include "hw/misc/xlnx-versal-cframe-reg.h" @@ -115,6 +116,7 @@ struct Versal { } iou; XlnxZynqMPRTC rtc; + XlnxVersalTRng trng; XlnxBBRam bbram; XlnxEFuse efuse; XlnxVersalEFuseCtrl efuse_ctrl; @@ -159,6 +161,7 @@ struct Versal { #define VERSAL_OSPI_IRQ 124 #define VERSAL_SD0_IRQ_0 126 #define VERSAL_EFUSE_IRQ 139 +#define VERSAL_TRNG_IRQ 141 #define VERSAL_RTC_ALARM_IRQ 142 #define VERSAL_RTC_SECONDS_IRQ 143 @@ -328,4 +331,6 @@ struct Versal { #define MM_PMC_CRP_SIZE 0x10000 #define MM_PMC_RTC 0xf12a0000 #define MM_PMC_RTC_SIZE 0x10000 +#define MM_PMC_TRNG 0xf1230000 +#define MM_PMC_TRNG_SIZE 0x10000 #endif diff --git a/include/hw/char/stm32f2xx_usart.h b/include/hw/char/stm32f2xx_usart.h index 65bcc85470..fdfa7424a7 100644 --- a/include/hw/char/stm32f2xx_usart.h +++ b/include/hw/char/stm32f2xx_usart.h @@ -48,10 +48,12 @@ #define USART_SR_TC (1 << 6) #define USART_SR_RXNE (1 << 5) -#define USART_CR1_UE (1 << 13) -#define USART_CR1_RXNEIE (1 << 5) -#define USART_CR1_TE (1 << 3) -#define USART_CR1_RE (1 << 2) +#define USART_CR1_UE (1 << 13) +#define USART_CR1_TXEIE (1 << 7) +#define USART_CR1_TCEIE (1 << 6) +#define USART_CR1_RXNEIE (1 << 5) +#define USART_CR1_TE (1 << 3) +#define USART_CR1_RE (1 << 2) #define TYPE_STM32F2XX_USART "stm32f2xx-usart" OBJECT_DECLARE_SIMPLE_TYPE(STM32F2XXUsartState, STM32F2XX_USART) diff --git a/include/hw/input/gamepad.h b/include/hw/input/gamepad.h deleted file mode 100644 index 6f6aa2406a..0000000000 --- a/include/hw/input/gamepad.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Gamepad style buttons connected to IRQ/GPIO lines - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * 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 HW_INPUT_GAMEPAD_H -#define HW_INPUT_GAMEPAD_H - - -/* stellaris_input.c */ -void stellaris_gamepad_init(int n, qemu_irq *irq, const int *keycode); - -#endif diff --git a/include/hw/input/stellaris_gamepad.h b/include/hw/input/stellaris_gamepad.h new file mode 100644 index 0000000000..51085e166c --- /dev/null +++ b/include/hw/input/stellaris_gamepad.h @@ -0,0 +1,37 @@ +/* + * Gamepad style buttons connected to IRQ/GPIO lines + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * 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 HW_INPUT_STELLARIS_GAMEPAD_H +#define HW_INPUT_STELLARIS_GAMEPAD_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +/* + * QEMU interface: + * + QOM array property "keycodes": uint32_t QEMU keycodes to handle + * (these are QCodes, ie the Q_KEY_* values) + * + unnamed GPIO outputs: one per keycode, in the same order as the + * "keycodes" array property entries; asserted when key is down + */ + +#define TYPE_STELLARIS_GAMEPAD "stellaris-gamepad" +OBJECT_DECLARE_SIMPLE_TYPE(StellarisGamepad, STELLARIS_GAMEPAD) + +struct StellarisGamepad { + SysBusDevice parent_obj; + + uint32_t num_buttons; + qemu_irq *irqs; + uint32_t *keycodes; + uint8_t *pressed; +}; + +#endif diff --git a/include/hw/intc/m68k_irqc.h b/include/hw/intc/m68k_irqc.h index ef91f21812..693e33b0aa 100644 --- a/include/hw/intc/m68k_irqc.h +++ b/include/hw/intc/m68k_irqc.h @@ -33,6 +33,7 @@ typedef struct M68KIRQCState { SysBusDevice parent_obj; uint8_t ipr; + ArchCPU *cpu; /* statistics */ uint64_t stats_irq_count[M68K_IRQC_LEVEL_NUM]; diff --git a/include/hw/m68k/mcf.h b/include/hw/m68k/mcf.h index 8cbd587bbf..5d9f876ffe 100644 --- a/include/hw/m68k/mcf.h +++ b/include/hw/m68k/mcf.h @@ -10,8 +10,8 @@ uint64_t mcf_uart_read(void *opaque, hwaddr addr, unsigned size); void mcf_uart_write(void *opaque, hwaddr addr, uint64_t val, unsigned size); -void *mcf_uart_init(qemu_irq irq, Chardev *chr); -void mcf_uart_mm_init(hwaddr base, qemu_irq irq, Chardev *chr); +DeviceState *mcf_uart_create(qemu_irq irq, Chardev *chr); +DeviceState *mcf_uart_create_mmap(hwaddr base, qemu_irq irq, Chardev *chr); /* mcf_intc.c */ qemu_irq *mcf_intc_init(struct MemoryRegion *sysmem, diff --git a/include/hw/misc/xlnx-versal-trng.h b/include/hw/misc/xlnx-versal-trng.h new file mode 100644 index 0000000000..0bcef8a613 --- /dev/null +++ b/include/hw/misc/xlnx-versal-trng.h @@ -0,0 +1,58 @@ +/* + * Non-crypto strength model of the True Random Number Generator + * in the AMD/Xilinx Versal device family. + * + * Copyright (c) 2017-2020 Xilinx Inc. + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * 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. + */ +#ifndef XLNX_VERSAL_TRNG_H +#define XLNX_VERSAL_TRNG_H + +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "hw/register.h" + +#define TYPE_XLNX_VERSAL_TRNG "xlnx.versal-trng" +OBJECT_DECLARE_SIMPLE_TYPE(XlnxVersalTRng, XLNX_VERSAL_TRNG); + +#define RMAX_XLNX_VERSAL_TRNG ((0xf0 / 4) + 1) + +typedef struct XlnxVersalTRng { + SysBusDevice parent_obj; + qemu_irq irq; + GRand *prng; + + uint32_t hw_version; + uint32_t forced_faults; + + uint32_t rand_count; + uint64_t rand_reseed; + + uint64_t forced_prng_seed; + uint64_t forced_prng_count; + uint64_t tst_seed[2]; + + uint32_t regs[RMAX_XLNX_VERSAL_TRNG]; + RegisterInfo regs_info[RMAX_XLNX_VERSAL_TRNG]; +} XlnxVersalTRng; + +#undef RMAX_XLNX_VERSAL_TRNG +#endif diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index e4f8a13afc..91f7a2452d 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -7,6 +7,7 @@ extern const PropertyInfo qdev_prop_chr; extern const PropertyInfo qdev_prop_macaddr; extern const PropertyInfo qdev_prop_reserved_region; extern const PropertyInfo qdev_prop_multifd_compression; +extern const PropertyInfo qdev_prop_mig_mode; extern const PropertyInfo qdev_prop_losttickpolicy; extern const PropertyInfo qdev_prop_blockdev_on_error; extern const PropertyInfo qdev_prop_bios_chs_trans; @@ -42,6 +43,9 @@ extern const PropertyInfo qdev_prop_cpus390entitlement; #define DEFINE_PROP_MULTIFD_COMPRESSION(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_multifd_compression, \ MultiFDCompression) +#define DEFINE_PROP_MIG_MODE(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_mig_mode, \ + MigMode) #define DEFINE_PROP_LOSTTICKPOLICY(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_losttickpolicy, \ LostTickPolicy) diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index e1df08876c..7fa2fdb7c9 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -206,6 +206,9 @@ void qdev_prop_set_macaddr(DeviceState *dev, const char *name, const uint8_t *value); void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); +/* Takes ownership of @values */ +void qdev_prop_set_array(DeviceState *dev, const char *name, QList *values); + void *object_field_prop_ptr(Object *obj, Property *prop); void qdev_prop_register_global(GlobalProperty *prop); diff --git a/include/migration/blocker.h b/include/migration/blocker.h index b048f301b4..a687ac0efe 100644 --- a/include/migration/blocker.h +++ b/include/migration/blocker.h @@ -14,8 +14,12 @@ #ifndef MIGRATION_BLOCKER_H #define MIGRATION_BLOCKER_H +#include "qapi/qapi-types-migration.h" + +#define MIG_MODE_ALL MIG_MODE__MAX + /** - * @migrate_add_blocker - prevent migration from proceeding + * @migrate_add_blocker - prevent all modes of migration from proceeding * * @reasonp - address of an error to be returned whenever migration is attempted * @@ -30,8 +34,8 @@ int migrate_add_blocker(Error **reasonp, Error **errp); /** - * @migrate_add_blocker_internal - prevent migration from proceeding without - * only-migrate implications + * @migrate_add_blocker_internal - prevent all modes of migration from + * proceeding, but ignore -only-migratable * * @reasonp - address of an error to be returned whenever migration is attempted * @@ -50,7 +54,7 @@ int migrate_add_blocker(Error **reasonp, Error **errp); int migrate_add_blocker_internal(Error **reasonp, Error **errp); /** - * @migrate_del_blocker - remove a blocking error from migration and free it. + * @migrate_del_blocker - remove a migration blocker from all modes and free it. * * @reasonp - address of the error blocking migration * @@ -58,4 +62,36 @@ int migrate_add_blocker_internal(Error **reasonp, Error **errp); */ void migrate_del_blocker(Error **reasonp); +/** + * @migrate_add_blocker_normal - prevent normal migration mode from proceeding + * + * @reasonp - address of an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free @reasonp, except by + * calling migrate_del_blocker. + */ +int migrate_add_blocker_normal(Error **reasonp, Error **errp); + +/** + * @migrate_add_blocker_modes - prevent some modes of migration from proceeding + * + * @reasonp - address of an error to be returned whenever migration is attempted + * + * @errp - [out] The reason (if any) we cannot block migration right now. + * + * @mode - one or more migration modes to be blocked. The list is terminated + * by -1 or MIG_MODE_ALL. For the latter, all modes are blocked. + * + * @returns - 0 on success, -EBUSY/-EACCES on failure, with errp set. + * + * *@reasonp is freed and set to NULL if failure is returned. + * On success, the caller must not free *@reasonp before the blocker is removed. + */ +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...); + #endif diff --git a/include/migration/misc.h b/include/migration/misc.h index 673ac490fb..1bc8902e6d 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -15,6 +15,7 @@ #define MIGRATION_MISC_H #include "qemu/notify.h" +#include "qapi/qapi-types-migration.h" #include "qapi/qapi-types-net.h" /* migration/ram.c */ diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 1af181877c..9821918631 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -1230,6 +1230,34 @@ static inline int vmstate_register(VMStateIf *obj, int instance_id, opaque, -1, 0, NULL); } +/** + * vmstate_replace_hack_for_ppc() - ppc used to abuse vmstate_register + * + * Don't even think about using this function in new code. + * + * Returns: 0 on success, -1 on failure + */ +int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, + const VMStateDescription *vmsd, + void *opaque); + +/** + * vmstate_register_any() - legacy function to register state + * serialisation description and let the function choose the id + * + * New code shouldn't be using this function as QOM-ified devices have + * dc->vmsd to store the serialisation description. + * + * Returns: 0 on success, -1 on failure + */ +static inline int vmstate_register_any(VMStateIf *obj, + const VMStateDescription *vmsd, + void *opaque) +{ + return vmstate_register_with_alias_id(obj, VMSTATE_INSTANCE_ID_ANY, vmsd, + opaque, -1, 0, NULL); +} + void vmstate_unregister(VMStateIf *obj, const VMStateDescription *vmsd, void *opaque); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8761f9e26b..4cd6891d7b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -552,10 +552,10 @@ uint32_t get_elf_hwcap(void) return hwcaps; } -uint32_t get_elf_hwcap2(void) +uint64_t get_elf_hwcap2(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; + uint64_t hwcaps = 0; GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); @@ -800,12 +800,14 @@ uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); @@ -816,10 +818,10 @@ uint32_t get_elf_hwcap(void) return hwcaps; } -uint32_t get_elf_hwcap2(void) +uint64_t get_elf_hwcap2(void) { ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; + uint64_t hwcaps = 0; GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); @@ -839,6 +841,7 @@ uint32_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | ARM_HWCAP2_A64_SME_F32F32 | ARM_HWCAP2_A64_SME_B16F32 | diff --git a/linux-user/loader.h b/linux-user/loader.h index a0834290e7..e102e6f410 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -103,7 +103,7 @@ uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); #endif #if defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint32_t get_elf_hwcap2(void); +uint64_t get_elf_hwcap2(void); const char *elf_hwcap2_str(uint32_t bit); #endif diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index 894fdd111a..73d7b6796a 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -72,6 +72,19 @@ void cpu_loop(CPULoongArchState *env) case EXCCODE_BCE: force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); break; + + /* + * Begin with LSX and LASX disabled, then enable on the first trap. + * In this way we can tell if the unit is in use. This is used to + * choose the layout of any signal frame. + */ + case EXCCODE_SXD: + env->CSR_EUEN |= R_CSR_EUEN_SXE_MASK; + break; + case EXCCODE_ASXD: + env->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK; + break; + case EXCP_ATOMIC: cpu_exec_step_atomic(cs); break; diff --git a/linux-user/loongarch64/signal.c b/linux-user/loongarch64/signal.c index afcee641a6..39ea82c814 100644 --- a/linux-user/loongarch64/signal.c +++ b/linux-user/loongarch64/signal.c @@ -18,10 +18,10 @@ #define SC_USED_FP (1 << 0) struct target_sigcontext { - uint64_t sc_pc; - uint64_t sc_regs[32]; - uint32_t sc_flags; - uint64_t sc_extcontext[0] QEMU_ALIGNED(16); + abi_ulong sc_pc; + abi_ulong sc_regs[32]; + abi_uint sc_flags; + abi_ulong sc_extcontext[0] QEMU_ALIGNED(16); }; QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext); @@ -33,19 +33,35 @@ QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_regs) #define FPU_CTX_MAGIC 0x46505501 #define FPU_CTX_ALIGN 8 struct target_fpu_context { - uint64_t regs[32]; - uint64_t fcc; - uint32_t fcsr; + abi_ulong regs[32]; + abi_ulong fcc; + abi_uint fcsr; } QEMU_ALIGNED(FPU_CTX_ALIGN); QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs) != offsetof_fpucontext_fr); +#define LSX_CTX_MAGIC 0x53580001 +#define LSX_CTX_ALIGN 16 +struct target_lsx_context { + abi_ulong regs[2 * 32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(LSX_CTX_ALIGN); + +#define LASX_CTX_MAGIC 0x41535801 +#define LASX_CTX_ALIGN 32 +struct target_lasx_context { + abi_ulong regs[4 * 32]; + abi_ulong fcc; + abi_uint fcsr; +} QEMU_ALIGNED(LASX_CTX_ALIGN); + #define CONTEXT_INFO_ALIGN 16 struct target_sctx_info { - uint32_t magic; - uint32_t size; - uint64_t padding; + abi_uint magic; + abi_uint size; + abi_ulong padding; } QEMU_ALIGNED(CONTEXT_INFO_ALIGN); QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info); @@ -81,9 +97,11 @@ struct ctx_layout { }; struct extctx_layout { - unsigned int size; + unsigned long size; unsigned int flags; struct ctx_layout fpu; + struct ctx_layout lsx; + struct ctx_layout lasx; struct ctx_layout end; }; @@ -105,7 +123,8 @@ static abi_ptr extframe_alloc(struct extctx_layout *extctx, return sp; } -static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) +static abi_ptr setup_extcontext(CPULoongArchState *env, + struct extctx_layout *extctx, abi_ptr sp) { memset(extctx, 0, sizeof(struct extctx_layout)); @@ -114,8 +133,17 @@ static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp) /* For qemu, there is no lazy fp context switch, so fp always present. */ extctx->flags = SC_USED_FP; - sp = extframe_alloc(extctx, &extctx->fpu, - sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp); + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + sp = extframe_alloc(extctx, &extctx->lasx, + sizeof(struct target_lasx_context), LASX_CTX_ALIGN, sp); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + sp = extframe_alloc(extctx, &extctx->lsx, + sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp); + } else { + sp = extframe_alloc(extctx, &extctx->fpu, + sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp); + } return sp; } @@ -125,7 +153,6 @@ static void setup_sigframe(CPULoongArchState *env, struct extctx_layout *extctx) { struct target_sctx_info *info; - struct target_fpu_context *fpu_ctx; int i; __put_user(extctx->flags, &sc->sc_flags); @@ -136,25 +163,63 @@ static void setup_sigframe(CPULoongArchState *env, } /* - * Set fpu context + * Set extension context */ - info = extctx->fpu.haddr; - __put_user(FPU_CTX_MAGIC, &info->magic); - __put_user(extctx->fpu.size, &info->size); - fpu_ctx = (struct target_fpu_context *)(info + 1); - for (i = 0; i < 32; ++i) { - __put_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]); + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + struct target_lasx_context *lasx_ctx; + info = extctx->lasx.haddr; + + __put_user(LASX_CTX_MAGIC, &info->magic); + __put_user(extctx->lasx.size, &info->size); + + lasx_ctx = (struct target_lasx_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __put_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __put_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __put_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __put_user(read_fcc(env), &lasx_ctx->fcc); + __put_user(env->fcsr0, &lasx_ctx->fcsr); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + struct target_lsx_context *lsx_ctx; + info = extctx->lsx.haddr; + + __put_user(LSX_CTX_MAGIC, &info->magic); + __put_user(extctx->lsx.size, &info->size); + + lsx_ctx = (struct target_lsx_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __put_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __put_user(read_fcc(env), &lsx_ctx->fcc); + __put_user(env->fcsr0, &lsx_ctx->fcsr); + } else { + struct target_fpu_context *fpu_ctx; + info = extctx->fpu.haddr; + + __put_user(FPU_CTX_MAGIC, &info->magic); + __put_user(extctx->fpu.size, &info->size); + + fpu_ctx = (struct target_fpu_context *)(info + 1); + + for (i = 0; i < 32; ++i) { + __put_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]); + } + __put_user(read_fcc(env), &fpu_ctx->fcc); + __put_user(env->fcsr0, &fpu_ctx->fcsr); } - __put_user(read_fcc(env), &fpu_ctx->fcc); - __put_user(env->fcsr0, &fpu_ctx->fcsr); /* * Set end context */ info = extctx->end.haddr; __put_user(0, &info->magic); - __put_user(extctx->end.size, &info->size); + __put_user(0, &info->size); } static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) @@ -162,7 +227,7 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) memset(extctx, 0, sizeof(*extctx)); while (1) { - uint32_t magic, size; + abi_uint magic, size; if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) { return false; @@ -184,6 +249,24 @@ static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame) extctx->fpu.size = size; extctx->size += size; break; + case LSX_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_lsx_context))) { + return false; + } + extctx->lsx.gaddr = frame; + extctx->lsx.size = size; + extctx->size += size; + break; + case LASX_CTX_MAGIC: + if (size < (sizeof(struct target_sctx_info) + + sizeof(struct target_lasx_context))) { + return false; + } + extctx->lasx.gaddr = frame; + extctx->lasx.size = size; + extctx->size += size; + break; default: return false; } @@ -197,19 +280,45 @@ static void restore_sigframe(CPULoongArchState *env, struct extctx_layout *extctx) { int i; + abi_ulong fcc; __get_user(env->pc, &sc->sc_pc); for (i = 1; i < 32; ++i) { __get_user(env->gpr[i], &sc->sc_regs[i]); } - if (extctx->fpu.haddr) { + if (extctx->lasx.haddr) { + struct target_lasx_context *lasx_ctx = + extctx->lasx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]); + __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]); + __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]); + } + __get_user(fcc, &lasx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lasx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->lsx.haddr) { + struct target_lsx_context *lsx_ctx = + extctx->lsx.haddr + sizeof(struct target_sctx_info); + + for (i = 0; i < 32; ++i) { + __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]); + __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]); + } + __get_user(fcc, &lsx_ctx->fcc); + write_fcc(env, fcc); + __get_user(env->fcsr0, &lsx_ctx->fcsr); + restore_fp_status(env); + } else if (extctx->fpu.haddr) { struct target_fpu_context *fpu_ctx = extctx->fpu.haddr + sizeof(struct target_sctx_info); - uint64_t fcc; for (i = 0; i < 32; ++i) { - __get_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]); + __get_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]); } __get_user(fcc, &fpu_ctx->fcc); write_fcc(env, fcc); @@ -229,7 +338,7 @@ static abi_ptr get_sigframe(struct target_sigaction *ka, sp = target_sigsp(get_sp_from_cpustate(env), ka); sp = ROUND_DOWN(sp, 16); - sp = setup_extcontext(extctx, sp); + sp = setup_extcontext(env, extctx, sp); sp -= sizeof(struct target_rt_sigframe); assert(QEMU_IS_ALIGNED(sp, 16)); @@ -255,8 +364,17 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, force_sigsegv(sig); return; } - extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); - extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + + if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) { + extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) { + extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } else { + extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); + extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr); + } tswap_siginfo(&frame->rs_info, info); @@ -299,7 +417,12 @@ long do_rt_sigreturn(CPULoongArchState *env) if (!frame) { goto badframe; } - if (extctx.fpu.gaddr) { + + if (extctx.lasx.gaddr) { + extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr); + } else if (extctx.lsx.gaddr) { + extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr); + } else if (extctx.fpu.gaddr) { extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr); } diff --git a/migration/exec.c b/migration/exec.c index 2bf882bbe1..47d2f3b8fb 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -27,7 +27,6 @@ #include "qemu/cutils.h" #ifdef WIN32 -const char *exec_get_cmd_path(void); const char *exec_get_cmd_path(void) { g_autofree char *detected_path = g_new(char, MAX_PATH); @@ -40,20 +39,51 @@ const char *exec_get_cmd_path(void) } #endif -void exec_start_outgoing_migration(MigrationState *s, const char *command, Error **errp) +/* provides the length of strList */ +static int +str_list_length(strList *list) +{ + int len = 0; + strList *elem; + + for (elem = list; elem != NULL; elem = elem->next) { + len++; + } + + return len; +} + +static void +init_exec_array(strList *command, char **argv, Error **errp) +{ + int i = 0; + strList *lst; + + for (lst = command; lst; lst = lst->next) { + argv[i++] = lst->value; + } + + argv[i] = NULL; + return; +} + +void exec_start_outgoing_migration(MigrationState *s, strList *command, + Error **errp) { QIOChannel *ioc; -#ifdef WIN32 - const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL }; -#else - const char *argv[] = { "/bin/sh", "-c", command, NULL }; -#endif + int length = str_list_length(command); + g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1); - trace_migration_exec_outgoing(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); + init_exec_array(command, argv, errp); + g_autofree char *new_command = g_strjoinv(" ", (char **)argv); + + trace_migration_exec_outgoing(new_command); + ioc = QIO_CHANNEL( + qio_channel_command_new_spawn( + (const char * const *) g_steal_pointer(&argv), + O_RDWR, + errp)); if (!ioc) { return; } @@ -72,20 +102,22 @@ static gboolean exec_accept_incoming_migration(QIOChannel *ioc, return G_SOURCE_REMOVE; } -void exec_start_incoming_migration(const char *command, Error **errp) +void exec_start_incoming_migration(strList *command, Error **errp) { QIOChannel *ioc; -#ifdef WIN32 - const char *argv[] = { exec_get_cmd_path(), "/c", command, NULL }; -#else - const char *argv[] = { "/bin/sh", "-c", command, NULL }; -#endif + int length = str_list_length(command); + g_auto(GStrv) argv = (char **) g_new0(const char *, length + 1); + + init_exec_array(command, argv, errp); + g_autofree char *new_command = g_strjoinv(" ", (char **)argv); - trace_migration_exec_incoming(command); - ioc = QIO_CHANNEL(qio_channel_command_new_spawn(argv, - O_RDWR, - errp)); + trace_migration_exec_incoming(new_command); + ioc = QIO_CHANNEL( + qio_channel_command_new_spawn( + (const char * const *) g_steal_pointer(&argv), + O_RDWR, + errp)); if (!ioc) { return; } diff --git a/migration/exec.h b/migration/exec.h index b210ffde7a..3107f205e3 100644 --- a/migration/exec.h +++ b/migration/exec.h @@ -19,8 +19,12 @@ #ifndef QEMU_MIGRATION_EXEC_H #define QEMU_MIGRATION_EXEC_H -void exec_start_incoming_migration(const char *host_port, Error **errp); -void exec_start_outgoing_migration(MigrationState *s, const char *host_port, +#ifdef WIN32 +const char *exec_get_cmd_path(void); +#endif +void exec_start_incoming_migration(strList *host_port, Error **errp); + +void exec_start_outgoing_migration(MigrationState *s, strList *host_port, Error **errp); #endif diff --git a/migration/file.c b/migration/file.c index cf5b1bf365..5d4975f43e 100644 --- a/migration/file.c +++ b/migration/file.c @@ -19,7 +19,7 @@ /* Remove the offset option from @filespec and return it in @offsetp. */ -static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) +int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) { char *option = strstr(filespec, OFFSET_OPTION); int ret; @@ -36,20 +36,16 @@ static int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) return 0; } -void file_start_outgoing_migration(MigrationState *s, const char *filespec, - Error **errp) +void file_start_outgoing_migration(MigrationState *s, + FileMigrationArgs *file_args, Error **errp) { - g_autofree char *filename = g_strdup(filespec); g_autoptr(QIOChannelFile) fioc = NULL; - uint64_t offset = 0; + g_autofree char *filename = g_strdup(file_args->filename); + uint64_t offset = file_args->offset; QIOChannel *ioc; trace_migration_file_outgoing(filename); - if (file_parse_offset(filename, &offset, errp)) { - return; - } - fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 0600, errp); if (!fioc) { @@ -73,19 +69,15 @@ static gboolean file_accept_incoming_migration(QIOChannel *ioc, return G_SOURCE_REMOVE; } -void file_start_incoming_migration(const char *filespec, Error **errp) +void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) { - g_autofree char *filename = g_strdup(filespec); + g_autofree char *filename = g_strdup(file_args->filename); QIOChannelFile *fioc = NULL; - uint64_t offset = 0; + uint64_t offset = file_args->offset; QIOChannel *ioc; trace_migration_file_incoming(filename); - if (file_parse_offset(filename, &offset, errp)) { - return; - } - fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); if (!fioc) { return; diff --git a/migration/file.h b/migration/file.h index 90fa4849e0..37d6a08bfc 100644 --- a/migration/file.h +++ b/migration/file.h @@ -7,8 +7,12 @@ #ifndef QEMU_MIGRATION_FILE_H #define QEMU_MIGRATION_FILE_H -void file_start_incoming_migration(const char *filename, Error **errp); -void file_start_outgoing_migration(MigrationState *s, const char *filename, - Error **errp); +#include "qapi/qapi-types-migration.h" + +void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp); + +void file_start_outgoing_migration(MigrationState *s, + FileMigrationArgs *file_args, Error **errp); +int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); #endif diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index dfe98da355..86ae832176 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -387,6 +387,11 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %" PRIu64 " MB/s\n", MigrationParameter_str(MIGRATION_PARAMETER_VCPU_DIRTY_LIMIT), params->vcpu_dirty_limit); + + assert(params->has_mode); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_MODE), + qapi_enum_lookup(&MigMode_lookup, params->mode)); } qapi_free_MigrationParameters(params); @@ -446,9 +451,18 @@ void hmp_migrate_incoming(Monitor *mon, const QDict *qdict) { Error *err = NULL; const char *uri = qdict_get_str(qdict, "uri"); + MigrationChannelList *caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; + + if (!migrate_uri_parse(uri, &channel, &err)) { + goto end; + } + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); - qmp_migrate_incoming(uri, &err); + qmp_migrate_incoming(NULL, true, caps, &err); + qapi_free_MigrationChannelList(caps); +end: hmp_handle_error(mon, err); } @@ -661,6 +675,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_vcpu_dirty_limit = true; visit_type_size(v, param, &p->vcpu_dirty_limit, &err); break; + case MIGRATION_PARAMETER_MODE: + p->has_mode = true; + visit_type_MigMode(v, param, &p->mode, &err); + break; default: assert(0); } @@ -744,6 +762,8 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) bool resume = qdict_get_try_bool(qdict, "resume", false); const char *uri = qdict_get_str(qdict, "uri"); Error *err = NULL; + MigrationChannelList *caps = NULL; + g_autoptr(MigrationChannel) channel = NULL; if (inc) { warn_report("option '-i' is deprecated;" @@ -755,12 +775,20 @@ void hmp_migrate(Monitor *mon, const QDict *qdict) " use blockdev-mirror with NBD instead"); } - qmp_migrate(uri, !!blk, blk, !!inc, inc, - false, false, true, resume, &err); + if (!migrate_uri_parse(uri, &channel, &err)) { + hmp_handle_error(mon, err); + return; + } + QAPI_LIST_PREPEND(caps, g_steal_pointer(&channel)); + + qmp_migrate(NULL, true, caps, !!blk, blk, !!inc, inc, + false, false, true, resume, &err); if (hmp_handle_error(mon, err)) { return; } + qapi_free_MigrationChannelList(caps); + if (!detach) { HMPMigrationStatus *status; diff --git a/migration/migration.c b/migration/migration.c index 6abcbefd9c..28a34c9068 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -66,6 +66,7 @@ #include "sysemu/qtest.h" #include "options.h" #include "sysemu/dirtylimit.h" +#include "qemu/sockets.h" static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -92,31 +93,55 @@ enum mig_rp_message_type { static MigrationState *current_migration; static MigrationIncomingState *current_incoming; -static GSList *migration_blockers; +static GSList *migration_blockers[MIG_MODE__MAX]; static bool migration_object_check(MigrationState *ms, Error **errp); static int migration_maybe_pause(MigrationState *s, int *current_active_state, int new_state); static void migrate_fd_cancel(MigrationState *s); -static int close_return_path_on_source(MigrationState *s); +static bool close_return_path_on_source(MigrationState *s); + +static void migration_downtime_start(MigrationState *s) +{ + trace_vmstate_downtime_checkpoint("src-downtime-start"); + s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); +} + +static void migration_downtime_end(MigrationState *s) +{ + int64_t now = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + + /* + * If downtime already set, should mean that postcopy already set it, + * then that should be the real downtime already. + */ + if (!s->downtime) { + s->downtime = now - s->downtime_start; + } + + trace_vmstate_downtime_checkpoint("src-downtime-end"); +} static bool migration_needs_multiple_sockets(void) { return migrate_multifd() || migrate_postcopy_preempt(); } -static bool uri_supports_multi_channels(const char *uri) +static bool transport_supports_multi_channels(SocketAddress *saddr) { - return strstart(uri, "tcp:", NULL) || strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL); + return saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK; } static bool -migration_channels_and_uri_compatible(const char *uri, Error **errp) +migration_channels_and_transport_compatible(MigrationAddress *addr, + Error **errp) { if (migration_needs_multiple_sockets() && - !uri_supports_multi_channels(uri)) { + (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) && + !transport_supports_multi_channels(&addr->u.socket)) { error_setg(errp, "Migration requires multi-channel URIs (e.g. tcp)"); return false; } @@ -131,6 +156,15 @@ static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) return (a > b) - (a < b); } +int migration_stop_vm(RunState state) +{ + int ret = vm_stop_force_state(state); + + trace_vmstate_downtime_checkpoint("src-vm-stopped"); + + return ret; +} + void migration_object_init(void) { /* This can only be called once. */ @@ -423,25 +457,114 @@ void migrate_add_address(SocketAddress *address) QAPI_CLONE(SocketAddress, address)); } -static void qemu_start_incoming_migration(const char *uri, Error **errp) +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp) { - const char *p = NULL; + g_autoptr(MigrationChannel) val = g_new0(MigrationChannel, 1); + g_autoptr(MigrationAddress) addr = g_new0(MigrationAddress, 1); + SocketAddress *saddr = NULL; + InetSocketAddress *isock = &addr->u.rdma; + strList **tail = &addr->u.exec.args; + + if (strstart(uri, "exec:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_EXEC; +#ifdef WIN32 + QAPI_LIST_APPEND(tail, g_strdup(exec_get_cmd_path())); + QAPI_LIST_APPEND(tail, g_strdup("/c")); +#else + QAPI_LIST_APPEND(tail, g_strdup("/bin/sh")); + QAPI_LIST_APPEND(tail, g_strdup("-c")); +#endif + QAPI_LIST_APPEND(tail, g_strdup(uri + strlen("exec:"))); + } else if (strstart(uri, "rdma:", NULL)) { + if (inet_parse(isock, uri + strlen("rdma:"), errp)) { + qapi_free_InetSocketAddress(isock); + return false; + } + addr->transport = MIGRATION_ADDRESS_TYPE_RDMA; + } else if (strstart(uri, "tcp:", NULL) || + strstart(uri, "unix:", NULL) || + strstart(uri, "vsock:", NULL) || + strstart(uri, "fd:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_SOCKET; + saddr = socket_parse(uri, errp); + if (!saddr) { + return false; + } + addr->u.socket.type = saddr->type; + addr->u.socket.u = saddr->u; + } else if (strstart(uri, "file:", NULL)) { + addr->transport = MIGRATION_ADDRESS_TYPE_FILE; + addr->u.file.filename = g_strdup(uri + strlen("file:")); + if (file_parse_offset(addr->u.file.filename, &addr->u.file.offset, + errp)) { + return false; + } + } else { + error_setg(errp, "unknown migration protocol: %s", uri); + return false; + } + + val->channel_type = MIGRATION_CHANNEL_TYPE_MAIN; + val->addr = g_steal_pointer(&addr); + *channel = g_steal_pointer(&val); + return true; +} + +static void qemu_start_incoming_migration(const char *uri, bool has_channels, + MigrationChannelList *channels, + Error **errp) +{ + MigrationChannel *channel = NULL; + MigrationAddress *addr = NULL; MigrationIncomingState *mis = migration_incoming_get_current(); - /* URI is not suitable for migration? */ - if (!migration_channels_and_uri_compatible(uri, errp)) { + /* + * Having preliminary checks for uri and channel + */ + if (uri && has_channels) { + error_setg(errp, "'uri' and 'channels' arguments are mutually " + "exclusive; exactly one of the two should be present in " + "'migrate-incoming' qmp command "); + return; + } else if (channels) { + /* To verify that Migrate channel list has only item */ + if (channels->next) { + error_setg(errp, "Channel list has more than one entries"); + return; + } + channel = channels->value; + } else if (uri) { + /* caller uses the old URI syntax */ + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + } else { + error_setg(errp, "neither 'uri' or 'channels' argument are " + "specified in 'migrate-incoming' qmp command "); + return; + } + addr = channel->addr; + + /* transport mechanism not suitable for migration? */ + if (!migration_channels_and_transport_compatible(addr, errp)) { return; } migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, MIGRATION_STATUS_SETUP); - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - socket_start_incoming_migration(p ? p : uri, errp); + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { + socket_start_incoming_migration(saddr, errp); + } else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) { + fd_start_incoming_migration(saddr->u.fd.str, errp); + } #ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { if (migrate_compress()) { error_setg(errp, "RDMA and compression can't be used together"); return; @@ -454,14 +577,12 @@ static void qemu_start_incoming_migration(const char *uri, Error **errp) error_setg(errp, "RDMA and multifd can't be used together"); return; } - rdma_start_incoming_migration(p, errp); + rdma_start_incoming_migration(&addr->u.rdma, errp); #endif - } else if (strstart(uri, "exec:", &p)) { - exec_start_incoming_migration(p, errp); - } else if (strstart(uri, "fd:", &p)) { - fd_start_incoming_migration(p, errp); - } else if (strstart(uri, "file:", &p)) { - file_start_incoming_migration(p, errp); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_incoming_migration(addr->u.exec.args, errp); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_incoming_migration(&addr->u.file, errp); } else { error_setg(errp, "unknown migration protocol: %s", uri); } @@ -472,6 +593,8 @@ static void process_incoming_migration_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; + trace_vmstate_downtime_checkpoint("dst-precopy-bh-enter"); + /* If capability late_block_activate is set: * Only fire up the block code now if we're going to restart the * VM, else 'cont' will do it. @@ -497,6 +620,8 @@ static void process_incoming_migration_bh(void *opaque) */ qemu_announce_self(&mis->announce_timer, migrate_announce_params()); + trace_vmstate_downtime_checkpoint("dst-precopy-bh-announced"); + multifd_load_shutdown(); dirty_bitmap_mig_before_vm_start(); @@ -514,6 +639,7 @@ static void process_incoming_migration_bh(void *opaque) } else { runstate_set(global_state_get_runstate()); } + trace_vmstate_downtime_checkpoint("dst-precopy-bh-vm-started"); /* * This must happen after any state changes since as soon as an external * observer sees this event they might start to prod at the VM assuming @@ -548,6 +674,8 @@ process_incoming_migration_co(void *opaque) ret = qemu_loadvm_state(mis->from_src_file); mis->loadvm_co = NULL; + trace_vmstate_downtime_checkpoint("dst-precopy-loadvm-completed"); + ps = postcopy_state_get(); trace_process_incoming_migration_co_end(ret, ps); if (ps != POSTCOPY_INCOMING_NONE) { @@ -1006,7 +1134,7 @@ static void fill_source_migration_info(MigrationInfo *info) { MigrationState *s = migrate_get_current(); int state = qatomic_read(&s->state); - GSList *cur_blocker = migration_blockers; + GSList *cur_blocker = migration_blockers[migrate_mode()]; info->blocked_reasons = NULL; @@ -1356,6 +1484,17 @@ bool migration_in_postcopy(void) } } +bool migration_postcopy_is_alive(int state) +{ + switch (state) { + case MIGRATION_STATUS_POSTCOPY_ACTIVE: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + return true; + default: + return false; + } +} + bool migration_in_postcopy_after_devices(MigrationState *s) { return migration_in_postcopy() && s->postcopy_after_devices; @@ -1438,7 +1577,6 @@ int migrate_init(MigrationState *s, Error **errp) s->to_dst_file = NULL; s->state = MIGRATION_STATUS_NONE; s->rp_state.from_dst_file = NULL; - s->rp_state.error = false; s->mbps = 0.0; s->pages_per_second = 0.0; s->downtime = 0; @@ -1470,44 +1608,112 @@ int migrate_init(MigrationState *s, Error **errp) return 0; } -int migrate_add_blocker_internal(Error **reasonp, Error **errp) +static bool is_busy(Error **reasonp, Error **errp) { + ERRP_GUARD(); + /* Snapshots are similar to migrations, so check RUN_STATE_SAVE_VM too. */ if (runstate_check(RUN_STATE_SAVE_VM) || !migration_is_idle()) { error_propagate_prepend(errp, *reasonp, "disallowing migration blocker " "(migration/snapshot in progress) for: "); *reasonp = NULL; - return -EBUSY; + return true; } - - migration_blockers = g_slist_prepend(migration_blockers, *reasonp); - return 0; + return false; } -int migrate_add_blocker(Error **reasonp, Error **errp) +static bool is_only_migratable(Error **reasonp, Error **errp, int modes) { - if (only_migratable) { + ERRP_GUARD(); + + if (only_migratable && (modes & BIT(MIG_MODE_NORMAL))) { error_propagate_prepend(errp, *reasonp, "disallowing migration blocker " "(--only-migratable) for: "); *reasonp = NULL; + return true; + } + return false; +} + +static int get_modes(MigMode mode, va_list ap) +{ + int modes = 0; + + while (mode != -1 && mode != MIG_MODE_ALL) { + assert(mode >= MIG_MODE_NORMAL && mode < MIG_MODE__MAX); + modes |= BIT(mode); + mode = va_arg(ap, MigMode); + } + if (mode == MIG_MODE_ALL) { + modes = BIT(MIG_MODE__MAX) - 1; + } + return modes; +} + +static int add_blockers(Error **reasonp, Error **errp, int modes) +{ + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + if (modes & BIT(mode)) { + migration_blockers[mode] = g_slist_prepend(migration_blockers[mode], + *reasonp); + } + } + return 0; +} + +int migrate_add_blocker(Error **reasonp, Error **errp) +{ + return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_ALL); +} + +int migrate_add_blocker_normal(Error **reasonp, Error **errp) +{ + return migrate_add_blocker_modes(reasonp, errp, MIG_MODE_NORMAL, -1); +} + +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...) +{ + int modes; + va_list ap; + + va_start(ap, mode); + modes = get_modes(mode, ap); + va_end(ap); + + if (is_only_migratable(reasonp, errp, modes)) { return -EACCES; + } else if (is_busy(reasonp, errp)) { + return -EBUSY; } + return add_blockers(reasonp, errp, modes); +} - return migrate_add_blocker_internal(reasonp, errp); +int migrate_add_blocker_internal(Error **reasonp, Error **errp) +{ + int modes = BIT(MIG_MODE__MAX) - 1; + + if (is_busy(reasonp, errp)) { + return -EBUSY; + } + return add_blockers(reasonp, errp, modes); } void migrate_del_blocker(Error **reasonp) { if (*reasonp) { - migration_blockers = g_slist_remove(migration_blockers, *reasonp); + for (MigMode mode = 0; mode < MIG_MODE__MAX; mode++) { + migration_blockers[mode] = g_slist_remove(migration_blockers[mode], + *reasonp); + } error_free(*reasonp); *reasonp = NULL; } } -void qmp_migrate_incoming(const char *uri, Error **errp) +void qmp_migrate_incoming(const char *uri, bool has_channels, + MigrationChannelList *channels, Error **errp) { Error *local_err = NULL; static bool once = true; @@ -1525,7 +1731,7 @@ void qmp_migrate_incoming(const char *uri, Error **errp) return; } - qemu_start_incoming_migration(uri, &local_err); + qemu_start_incoming_migration(uri, has_channels, channels, &local_err); if (local_err) { yank_unregister_instance(MIGRATION_YANK_INSTANCE); @@ -1561,7 +1767,7 @@ void qmp_migrate_recover(const char *uri, Error **errp) * only re-setup the migration stream and poke existing migration * to continue using that newly established channel. */ - qemu_start_incoming_migration(uri, errp); + qemu_start_incoming_migration(uri, false, NULL, errp); } void qmp_migrate_pause(Error **errp) @@ -1570,8 +1776,15 @@ void qmp_migrate_pause(Error **errp) MigrationIncomingState *mis = migration_incoming_get_current(); int ret = 0; - if (ms->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + if (migration_postcopy_is_alive(ms->state)) { /* Source side, during postcopy */ + Error *error = NULL; + + /* Tell the core migration that we're pausing */ + error_setg(&error, "Postcopy migration is paused by the user"); + migrate_set_error(ms, error); + error_free(error); + qemu_mutex_lock(&ms->qemu_file_lock); if (ms->to_dst_file) { ret = qemu_file_shutdown(ms->to_dst_file); @@ -1580,10 +1793,17 @@ void qmp_migrate_pause(Error **errp) if (ret) { error_setg(errp, "Failed to pause source migration"); } + + /* + * Kick the migration thread out of any waiting windows (on behalf + * of the rp thread). + */ + migration_rp_kick(ms); + return; } - if (mis->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + if (migration_postcopy_is_alive(mis->state)) { ret = qemu_file_shutdown(mis->from_src_file); if (ret) { error_setg(errp, "Failed to pause destination migration"); @@ -1592,17 +1812,19 @@ void qmp_migrate_pause(Error **errp) } error_setg(errp, "migrate-pause is currently only supported " - "during postcopy-active state"); + "during postcopy-active or postcopy-recover state"); } bool migration_is_blocked(Error **errp) { + GSList *blockers = migration_blockers[migrate_mode()]; + if (qemu_savevm_state_blocked(errp)) { return true; } - if (migration_blockers) { - error_propagate(errp, error_copy(migration_blockers->data)); + if (blockers) { + error_propagate(errp, error_copy(blockers->data)); return true; } @@ -1702,17 +1924,46 @@ static bool migrate_prepare(MigrationState *s, bool blk, bool blk_inc, return true; } -void qmp_migrate(const char *uri, bool has_blk, bool blk, +void qmp_migrate(const char *uri, bool has_channels, + MigrationChannelList *channels, bool has_blk, bool blk, bool has_inc, bool inc, bool has_detach, bool detach, bool has_resume, bool resume, Error **errp) { bool resume_requested; Error *local_err = NULL; MigrationState *s = migrate_get_current(); - const char *p = NULL; + MigrationChannel *channel = NULL; + MigrationAddress *addr = NULL; - /* URI is not suitable for migration? */ - if (!migration_channels_and_uri_compatible(uri, errp)) { + /* + * Having preliminary checks for uri and channel + */ + if (uri && has_channels) { + error_setg(errp, "'uri' and 'channels' arguments are mutually " + "exclusive; exactly one of the two should be present in " + "'migrate' qmp command "); + return; + } else if (channels) { + /* To verify that Migrate channel list has only item */ + if (channels->next) { + error_setg(errp, "Channel list has more than one entries"); + return; + } + channel = channels->value; + } else if (uri) { + /* caller uses the old URI syntax */ + if (!migrate_uri_parse(uri, &channel, errp)) { + return; + } + } else { + error_setg(errp, "neither 'uri' or 'channels' argument are " + "specified in 'migrate' qmp command "); + return; + } + addr = channel->addr; + + /* transport mechanism not suitable for migration? */ + if (!migration_channels_and_transport_compatible(addr, errp)) { return; } @@ -1729,20 +1980,23 @@ void qmp_migrate(const char *uri, bool has_blk, bool blk, } } - if (strstart(uri, "tcp:", &p) || - strstart(uri, "unix:", NULL) || - strstart(uri, "vsock:", NULL)) { - socket_start_outgoing_migration(s, p ? p : uri, &local_err); + if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET) { + SocketAddress *saddr = &addr->u.socket; + if (saddr->type == SOCKET_ADDRESS_TYPE_INET || + saddr->type == SOCKET_ADDRESS_TYPE_UNIX || + saddr->type == SOCKET_ADDRESS_TYPE_VSOCK) { + socket_start_outgoing_migration(s, saddr, &local_err); + } else if (saddr->type == SOCKET_ADDRESS_TYPE_FD) { + fd_start_outgoing_migration(s, saddr->u.fd.str, &local_err); + } #ifdef CONFIG_RDMA - } else if (strstart(uri, "rdma:", &p)) { - rdma_start_outgoing_migration(s, p, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + rdma_start_outgoing_migration(s, &addr->u.rdma, &local_err); #endif - } else if (strstart(uri, "exec:", &p)) { - exec_start_outgoing_migration(s, p, &local_err); - } else if (strstart(uri, "fd:", &p)) { - fd_start_outgoing_migration(s, p, &local_err); - } else if (strstart(uri, "file:", &p)) { - file_start_outgoing_migration(s, p, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { + exec_start_outgoing_migration(s, addr->u.exec.args, &local_err); + } else if (addr->transport == MIGRATION_ADDRESS_TYPE_FILE) { + file_start_outgoing_migration(s, &addr->u.file, &local_err); } else { error_setg(&local_err, QERR_INVALID_PARAMETER_VALUE, "uri", "a valid migration protocol"); @@ -1777,19 +2031,21 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp) qemu_sem_post(&s->pause_sem); } -/* migration thread support */ -/* - * Something bad happened to the RP stream, mark an error - * The caller shall print or trace something to indicate why - */ -static void mark_source_rp_bad(MigrationState *s) +int migration_rp_wait(MigrationState *s) { - s->rp_state.error = true; -} + /* If migration has failure already, ignore the wait */ + if (migrate_has_error(s)) { + return -1; + } -void migration_rp_wait(MigrationState *s) -{ qemu_sem_wait(&s->rp_state.rp_sem); + + /* After wait, double check that there's no failure */ + if (migrate_has_error(s)) { + return -1; + } + + return 0; } void migration_rp_kick(MigrationState *s) @@ -1817,8 +2073,9 @@ static struct rp_cmd_args { * We're allowed to send more than requested (e.g. to round to our page size) * and we don't need to send pages that have already been sent. */ -static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, - ram_addr_t start, size_t len) +static void +migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, + ram_addr_t start, size_t len, Error **errp) { long our_host_ps = qemu_real_host_page_size(); @@ -1830,38 +2087,37 @@ static void migrate_handle_rp_req_pages(MigrationState *ms, const char* rbname, */ if (!QEMU_IS_ALIGNED(start, our_host_ps) || !QEMU_IS_ALIGNED(len, our_host_ps)) { - error_report("%s: Misaligned page request, start: " RAM_ADDR_FMT - " len: %zd", __func__, start, len); - mark_source_rp_bad(ms); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES: Misaligned page request, start:" + RAM_ADDR_FMT " len: %zd", start, len); return; } - if (ram_save_queue_pages(rbname, start, len)) { - mark_source_rp_bad(ms); - } + ram_save_queue_pages(rbname, start, len, errp); } -static int migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name) +static bool migrate_handle_rp_recv_bitmap(MigrationState *s, char *block_name, + Error **errp) { RAMBlock *block = qemu_ram_block_by_name(block_name); if (!block) { - error_report("%s: invalid block name '%s'", __func__, block_name); - return -EINVAL; + error_setg(errp, "MIG_RP_MSG_RECV_BITMAP has invalid block name '%s'", + block_name); + return false; } /* Fetch the received bitmap and refresh the dirty bitmap */ - return ram_dirty_bitmap_reload(s, block); + return ram_dirty_bitmap_reload(s, block, errp); } -static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) +static bool migrate_handle_rp_resume_ack(MigrationState *s, + uint32_t value, Error **errp) { trace_source_return_path_thread_resume_ack(value); if (value != MIGRATION_RESUME_ACK_VALUE) { - error_report("%s: illegal resume_ack value %"PRIu32, - __func__, value); - return -1; + error_setg(errp, "illegal resume_ack value %"PRIu32, value); + return false; } /* Now both sides are active. */ @@ -1871,7 +2127,7 @@ static int migrate_handle_rp_resume_ack(MigrationState *s, uint32_t value) /* Notify send thread that time to continue send pages */ migration_rp_kick(s); - return 0; + return true; } /* @@ -1919,48 +2175,46 @@ static void *source_return_path_thread(void *opaque) uint32_t tmp32, sibling_error; ram_addr_t start = 0; /* =0 to silence warning */ size_t len = 0, expected_len; + Error *err = NULL; int res; trace_source_return_path_thread_entry(); rcu_register_thread(); - while (!ms->rp_state.error && !qemu_file_get_error(rp) && - migration_is_setup_or_active(ms->state)) { + while (migration_is_setup_or_active(ms->state)) { trace_source_return_path_thread_loop_top(); + header_type = qemu_get_be16(rp); header_len = qemu_get_be16(rp); if (qemu_file_get_error(rp)) { - mark_source_rp_bad(ms); + qemu_file_get_error_obj(rp, &err); goto out; } if (header_type >= MIG_RP_MSG_MAX || header_type == MIG_RP_MSG_INVALID) { - error_report("RP: Received invalid message 0x%04x length 0x%04x", - header_type, header_len); - mark_source_rp_bad(ms); + error_setg(&err, "Received invalid message 0x%04x length 0x%04x", + header_type, header_len); goto out; } if ((rp_cmd_args[header_type].len != -1 && header_len != rp_cmd_args[header_type].len) || header_len > sizeof(buf)) { - error_report("RP: Received '%s' message (0x%04x) with" - "incorrect length %d expecting %zu", - rp_cmd_args[header_type].name, header_type, header_len, - (size_t)rp_cmd_args[header_type].len); - mark_source_rp_bad(ms); + error_setg(&err, "Received '%s' message (0x%04x) with" + "incorrect length %d expecting %zu", + rp_cmd_args[header_type].name, header_type, header_len, + (size_t)rp_cmd_args[header_type].len); goto out; } /* We know we've got a valid header by this point */ res = qemu_get_buffer(rp, buf, header_len); if (res != header_len) { - error_report("RP: Failed reading data for message 0x%04x" - " read %d expected %d", - header_type, res, header_len); - mark_source_rp_bad(ms); + error_setg(&err, "Failed reading data for message 0x%04x" + " read %d expected %d", + header_type, res, header_len); goto out; } @@ -1970,8 +2224,7 @@ static void *source_return_path_thread(void *opaque) sibling_error = ldl_be_p(buf); trace_source_return_path_thread_shut(sibling_error); if (sibling_error) { - error_report("RP: Sibling indicated error %d", sibling_error); - mark_source_rp_bad(ms); + error_setg(&err, "Sibling indicated error %d", sibling_error); } /* * We'll let the main thread deal with closing the RP @@ -1989,7 +2242,10 @@ static void *source_return_path_thread(void *opaque) case MIG_RP_MSG_REQ_PAGES: start = ldq_be_p(buf); len = ldl_be_p(buf + 8); - migrate_handle_rp_req_pages(ms, NULL, start, len); + migrate_handle_rp_req_pages(ms, NULL, start, len, &err); + if (err) { + goto out; + } break; case MIG_RP_MSG_REQ_PAGES_ID: @@ -2004,32 +2260,32 @@ static void *source_return_path_thread(void *opaque) expected_len += tmp32; } if (header_len != expected_len) { - error_report("RP: Req_Page_id with length %d expecting %zd", - header_len, expected_len); - mark_source_rp_bad(ms); + error_setg(&err, "Req_Page_id with length %d expecting %zd", + header_len, expected_len); + goto out; + } + migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len, + &err); + if (err) { goto out; } - migrate_handle_rp_req_pages(ms, (char *)&buf[13], start, len); break; case MIG_RP_MSG_RECV_BITMAP: if (header_len < 1) { - error_report("%s: missing block name", __func__); - mark_source_rp_bad(ms); + error_setg(&err, "MIG_RP_MSG_RECV_BITMAP missing block name"); goto out; } /* Format: len (1B) + idstr (<255B). This ends the idstr. */ buf[buf[0] + 1] = '\0'; - if (migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1))) { - mark_source_rp_bad(ms); + if (!migrate_handle_rp_recv_bitmap(ms, (char *)(buf + 1), &err)) { goto out; } break; case MIG_RP_MSG_RESUME_ACK: tmp32 = ldl_be_p(buf); - if (migrate_handle_rp_resume_ack(ms, tmp32)) { - mark_source_rp_bad(ms); + if (!migrate_handle_rp_resume_ack(ms, tmp32, &err)) { goto out; } break; @@ -2045,13 +2301,29 @@ static void *source_return_path_thread(void *opaque) } out: - if (qemu_file_get_error(rp)) { + if (err) { + migrate_set_error(ms, err); + error_free(err); trace_source_return_path_thread_bad_end(); - mark_source_rp_bad(ms); + } + + if (ms->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { + /* + * this will be extremely unlikely: that we got yet another network + * issue during recovering of the 1st network failure.. during this + * period the main migration thread can be waiting on rp_sem for + * this thread to sync with the other side. + * + * When this happens, explicitly kick the migration thread out of + * RECOVER stage and back to PAUSED, so the admin can try + * everything again. + */ + migration_rp_kick(ms); } trace_source_return_path_thread_end(); rcu_unregister_thread(); + return NULL; } @@ -2073,12 +2345,11 @@ static int open_return_path_on_source(MigrationState *ms) return 0; } -static int close_return_path_on_source(MigrationState *ms) +/* Return true if error detected, or false otherwise */ +static bool close_return_path_on_source(MigrationState *ms) { - int ret; - if (!ms->rp_state.rp_thread_created) { - return 0; + return false; } trace_migration_return_path_end_before(); @@ -2096,18 +2367,13 @@ static int close_return_path_on_source(MigrationState *ms) } } - trace_await_return_path_close_on_source_joining(); qemu_thread_join(&ms->rp_state.rp_thread); ms->rp_state.rp_thread_created = false; - trace_await_return_path_close_on_source_close(); - - ret = ms->rp_state.error; - ms->rp_state.error = false; - migration_release_dst_files(ms); + trace_migration_return_path_end_after(); - trace_migration_return_path_end_after(ret); - return ret; + /* Return path will persist the error in MigrationState when quit */ + return migrate_has_error(ms); } static inline void @@ -2126,7 +2392,6 @@ static int postcopy_start(MigrationState *ms, Error **errp) int ret; QIOChannelBuffer *bioc; QEMUFile *fb; - int64_t time_at_stop = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); uint64_t bandwidth = migrate_max_postcopy_bandwidth(); bool restart_block = false; int cur_state = MIGRATION_STATUS_ACTIVE; @@ -2148,9 +2413,11 @@ static int postcopy_start(MigrationState *ms, Error **errp) qemu_mutex_lock_iothread(); trace_postcopy_start_set_run(); + migration_downtime_start(ms); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); global_state_store(); - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE); if (ret < 0) { goto fail; } @@ -2250,7 +2517,7 @@ static int postcopy_start(MigrationState *ms, Error **errp) ms->postcopy_after_devices = true; migration_call_notifiers(ms); - ms->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - time_at_stop; + migration_downtime_end(ms); qemu_mutex_unlock_iothread(); @@ -2346,13 +2613,13 @@ static int migration_completion_precopy(MigrationState *s, int ret; qemu_mutex_lock_iothread(); - s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + migration_downtime_start(s); qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); s->vm_old_state = runstate_get(); global_state_store(); - ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); + ret = migration_stop_vm(RUN_STATE_FINISH_MIGRATE); trace_migration_completion_vm_stop(ret); if (ret < 0) { goto out_unlock; @@ -2519,7 +2786,9 @@ static int postcopy_resume_handshake(MigrationState *s) qemu_savevm_send_postcopy_resume(s->to_dst_file); while (s->state == MIGRATION_STATUS_POSTCOPY_RECOVER) { - migration_rp_wait(s); + if (migration_rp_wait(s)) { + return -1; + } } if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { @@ -2703,15 +2972,8 @@ static void migration_calculate_complete(MigrationState *s) int64_t end_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); int64_t transfer_time; + migration_downtime_end(s); s->total_time = end_time - s->start_time; - if (!s->downtime) { - /* - * It's still not set, so we are precopy migration. For - * postcopy, downtime is calculated during postcopy_start(). - */ - s->downtime = end_time - s->downtime_start; - } - transfer_time = s->total_time - s->setup_time; if (transfer_time) { s->mbps = ((double) bytes * 8.0) / transfer_time / 1000; @@ -3130,7 +3392,7 @@ static void bg_migration_vm_start_bh(void *opaque) s->vm_start_bh = NULL; vm_start(); - s->downtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - s->downtime_start; + migration_downtime_end(s); } /** @@ -3197,7 +3459,7 @@ static void *bg_migration_thread(void *opaque) s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; trace_migration_thread_setup_complete(); - s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + migration_downtime_start(s); qemu_mutex_lock_iothread(); @@ -3210,7 +3472,7 @@ static void *bg_migration_thread(void *opaque) global_state_store(); /* Forcibly stop VM before saving state of vCPUs and devices */ - if (vm_stop_force_state(RUN_STATE_PAUSED)) { + if (migration_stop_vm(RUN_STATE_PAUSED)) { goto fail; } /* diff --git a/migration/migration.h b/migration/migration.h index ae82004892..cf2c9c88e0 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -308,7 +308,6 @@ struct MigrationState { /* Protected by qemu_file_lock */ QEMUFile *from_dst_file; QemuThread rp_thread; - bool error; /* * We can also check non-zero of rp_thread, but there's no "official" * way to do this, so this bool makes it slightly more elegant. @@ -495,6 +494,7 @@ int migrate_init(MigrationState *s, Error **errp); bool migration_is_blocked(Error **errp); /* True if outgoing migration has entered postcopy phase */ bool migration_in_postcopy(void); +bool migration_postcopy_is_alive(int state); MigrationState *migrate_get_current(void); uint64_t ram_get_total_transferred_pages(void); @@ -520,7 +520,8 @@ bool check_dirty_bitmap_mig_alias_map(const BitmapMigrationNodeAliasList *bbm, Error **errp); void migrate_add_address(SocketAddress *address); - +bool migrate_uri_parse(const char *uri, MigrationChannel **channel, + Error **errp); int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); #define qemu_ram_foreach_block \ @@ -535,8 +536,11 @@ void migration_populate_vfio_info(MigrationInfo *info); void migration_reset_vfio_bytes_transferred(void); void postcopy_temp_page_reset(PostcopyTmpPage *tmp_page); -/* Migration thread waiting for return path thread. */ -void migration_rp_wait(MigrationState *s); +/* + * Migration thread waiting for return path thread. Return non-zero if an + * error is detected. + */ +int migration_rp_wait(MigrationState *s); /* * Kick the migration thread waiting for return path messages. NOTE: the * name can be slightly confusing (when read as "kick the rp thread"), just @@ -544,4 +548,6 @@ void migration_rp_wait(MigrationState *s); */ void migration_rp_kick(MigrationState *s); +int migration_stop_vm(RunState state); + #endif diff --git a/migration/options.c b/migration/options.c index 9a39826ca5..8d8ec73ad9 100644 --- a/migration/options.c +++ b/migration/options.c @@ -176,6 +176,9 @@ Property migration_properties[] = { DEFINE_PROP_UINT64("vcpu-dirty-limit", MigrationState, parameters.vcpu_dirty_limit, DEFAULT_MIGRATE_VCPU_DIRTY_LIMIT), + DEFINE_PROP_MIG_MODE("mode", MigrationState, + parameters.mode, + MIG_MODE_NORMAL), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -827,6 +830,13 @@ uint64_t migrate_max_postcopy_bandwidth(void) return s->parameters.max_postcopy_bandwidth; } +MigMode migrate_mode(void) +{ + MigrationState *s = migrate_get_current(); + + return s->parameters.mode; +} + int migrate_multifd_channels(void) { MigrationState *s = migrate_get_current(); @@ -999,6 +1009,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->x_vcpu_dirty_limit_period = s->parameters.x_vcpu_dirty_limit_period; params->has_vcpu_dirty_limit = true; params->vcpu_dirty_limit = s->parameters.vcpu_dirty_limit; + params->has_mode = true; + params->mode = s->parameters.mode; return params; } @@ -1034,6 +1046,7 @@ void migrate_params_init(MigrationParameters *params) params->has_announce_step = true; params->has_x_vcpu_dirty_limit_period = true; params->has_vcpu_dirty_limit = true; + params->has_mode = true; } /* @@ -1331,6 +1344,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_vcpu_dirty_limit) { dest->vcpu_dirty_limit = params->vcpu_dirty_limit; } + + if (params->has_mode) { + dest->mode = params->mode; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1471,6 +1488,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_vcpu_dirty_limit) { s->parameters.vcpu_dirty_limit = params->vcpu_dirty_limit; } + + if (params->has_mode) { + s->parameters.mode = params->mode; + } } void qmp_migrate_set_parameters(MigrateSetParameters *params, Error **errp) diff --git a/migration/options.h b/migration/options.h index 237f2d6b4a..246c160aee 100644 --- a/migration/options.h +++ b/migration/options.h @@ -83,6 +83,7 @@ uint8_t migrate_max_cpu_throttle(void); uint64_t migrate_max_bandwidth(void); uint64_t migrate_avail_switchover_bandwidth(void); uint64_t migrate_max_postcopy_bandwidth(void); +MigMode migrate_mode(void); int migrate_multifd_channels(void); MultiFDCompression migrate_multifd_compression(void); int migrate_multifd_zlib_level(void); diff --git a/migration/qemu-file.c b/migration/qemu-file.c index d64500310d..94231ff295 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -137,7 +137,7 @@ QEMUFile *qemu_file_new_input(QIOChannel *ioc) * * If errp is specified, a verbose error message will be copied over. */ -static int qemu_file_get_error_obj(QEMUFile *f, Error **errp) +int qemu_file_get_error_obj(QEMUFile *f, Error **errp) { if (!f->last_error) { return 0; diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 1774116f79..8aec9fabf7 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -68,6 +68,7 @@ int coroutine_mixed_fn qemu_peek_byte(QEMUFile *f, int offset); void qemu_file_skip(QEMUFile *f, int size); int qemu_file_get_error_obj_any(QEMUFile *f1, QEMUFile *f2, Error **errp); void qemu_file_set_error_obj(QEMUFile *f, int ret, Error *err); +int qemu_file_get_error_obj(QEMUFile *f, Error **errp); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); diff --git a/migration/ram.c b/migration/ram.c index 34724e8fe8..a0f3b86663 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1898,7 +1898,8 @@ static void migration_page_queue_free(RAMState *rs) * @start: starting address from the start of the RAMBlock * @len: length (in bytes) to send */ -int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, + Error **errp) { RAMBlock *ramblock; RAMState *rs = ram_state; @@ -1915,7 +1916,7 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) * Shouldn't happen, we can't reuse the last RAMBlock if * it's the 1st request. */ - error_report("ram_save_queue_pages no previous block"); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no previous block"); return -1; } } else { @@ -1923,16 +1924,17 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) if (!ramblock) { /* We shouldn't be asked for a non-existent RAMBlock */ - error_report("ram_save_queue_pages no block '%s'", rbname); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES has no block '%s'", rbname); return -1; } rs->last_req_rb = ramblock; } trace_ram_save_queue_pages(ramblock->idstr, start, len); if (!offset_in_ramblock(ramblock, start + len - 1)) { - error_report("%s request overrun start=" RAM_ADDR_FMT " len=" - RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, - __func__, start, len, ramblock->used_length); + error_setg(errp, "MIG_RP_MSG_REQ_PAGES request overrun, " + "start=" RAM_ADDR_FMT " len=" + RAM_ADDR_FMT " blocklen=" RAM_ADDR_FMT, + start, len, ramblock->used_length); return -1; } @@ -1964,9 +1966,9 @@ int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len) assert(len % page_size == 0); while (len) { if (ram_save_host_page_urgent(pss)) { - error_report("%s: ram_save_host_page_urgent() failed: " - "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, - __func__, ramblock->idstr, start); + error_setg(errp, "ram_save_host_page_urgent() failed: " + "ramblock=%s, start_addr=0x"RAM_ADDR_FMT, + ramblock->idstr, start); ret = -1; break; } @@ -4097,7 +4099,9 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) /* Wait until all the ramblocks' dirty bitmap synced */ while (qatomic_read(&rs->postcopy_bmap_sync_requested)) { - migration_rp_wait(s); + if (migration_rp_wait(s)) { + return -1; + } } trace_ram_dirty_bitmap_sync_complete(); @@ -4109,10 +4113,11 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) * Read the received bitmap, revert it as the initial dirty bitmap. * This is only used when the postcopy migration is paused but wants * to resume from a middle point. + * + * Returns true if succeeded, false for errors. */ -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) +bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block, Error **errp) { - int ret = -EINVAL; /* from_dst_file is always valid because we're within rp_thread */ QEMUFile *file = s->rp_state.from_dst_file; g_autofree unsigned long *le_bitmap = NULL; @@ -4124,9 +4129,9 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) trace_ram_dirty_bitmap_reload_begin(block->idstr); if (s->state != MIGRATION_STATUS_POSTCOPY_RECOVER) { - error_report("%s: incorrect state %s", __func__, - MigrationStatus_str(s->state)); - return -EINVAL; + error_setg(errp, "Reload bitmap in incorrect state %s", + MigrationStatus_str(s->state)); + return false; } /* @@ -4142,27 +4147,25 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) /* The size of the bitmap should match with our ramblock */ if (size != local_size) { - error_report("%s: ramblock '%s' bitmap size mismatch " - "(0x%"PRIx64" != 0x%"PRIx64")", __func__, - block->idstr, size, local_size); - return -EINVAL; + error_setg(errp, "ramblock '%s' bitmap size mismatch (0x%"PRIx64 + " != 0x%"PRIx64")", block->idstr, size, local_size); + return false; } size = qemu_get_buffer(file, (uint8_t *)le_bitmap, local_size); end_mark = qemu_get_be64(file); - ret = qemu_file_get_error(file); - if (ret || size != local_size) { - error_report("%s: read bitmap failed for ramblock '%s': %d" - " (size 0x%"PRIx64", got: 0x%"PRIx64")", - __func__, block->idstr, ret, local_size, size); - return -EIO; + if (qemu_file_get_error(file) || size != local_size) { + error_setg(errp, "read bitmap failed for ramblock '%s': " + "(size 0x%"PRIx64", got: 0x%"PRIx64")", + block->idstr, local_size, size); + return false; } if (end_mark != RAMBLOCK_RECV_BITMAP_ENDING) { - error_report("%s: ramblock '%s' end mark incorrect: 0x%"PRIx64, - __func__, block->idstr, end_mark); - return -EINVAL; + error_setg(errp, "ramblock '%s' end mark incorrect: 0x%"PRIx64, + block->idstr, end_mark); + return false; } /* @@ -4194,7 +4197,7 @@ int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *block) */ migration_rp_kick(s); - return 0; + return true; } static int ram_resume_prepare(MigrationState *s, void *opaque) diff --git a/migration/ram.h b/migration/ram.h index 9f3ad1ee81..9b937a446b 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -50,7 +50,8 @@ uint64_t ram_bytes_total(void); void mig_throttle_counter_reset(void); uint64_t ram_pagesize_summary(void); -int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); +int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len, + Error **errp); void ram_postcopy_migrated_memory_release(MigrationState *ms); /* For outgoing discard bitmap */ void ram_postcopy_send_discard_bitmap(MigrationState *ms); @@ -70,7 +71,7 @@ void ramblock_recv_bitmap_set(RAMBlock *rb, void *host_addr); void ramblock_recv_bitmap_set_range(RAMBlock *rb, void *host_addr, size_t nr); int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); -int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); +bool ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb, Error **errp); bool ramblock_page_is_discarded(RAMBlock *rb, ram_addr_t start); void postcopy_preempt_shutdown_file(MigrationState *s); void *postcopy_preempt_thread(void *opaque); diff --git a/migration/rdma.c b/migration/rdma.c index 2938db4f64..6a29e53daf 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -289,7 +289,6 @@ typedef struct RDMALocalBlocks { typedef struct RDMAContext { char *host; int port; - char *host_port; RDMAWorkRequestData wr_data[RDMA_WRID_MAX]; @@ -2431,9 +2430,7 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma->channel = NULL; } g_free(rdma->host); - g_free(rdma->host_port); rdma->host = NULL; - rdma->host_port = NULL; } @@ -2723,28 +2720,16 @@ static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path, rdma_return_path->is_return_path = true; } -static RDMAContext *qemu_rdma_data_init(const char *host_port, Error **errp) +static RDMAContext *qemu_rdma_data_init(InetSocketAddress *saddr, Error **errp) { RDMAContext *rdma = NULL; - InetSocketAddress *addr; rdma = g_new0(RDMAContext, 1); rdma->current_index = -1; rdma->current_chunk = -1; - addr = g_new(InetSocketAddress, 1); - if (!inet_parse(addr, host_port, NULL)) { - rdma->port = atoi(addr->port); - rdma->host = g_strdup(addr->host); - rdma->host_port = g_strdup(host_port); - } else { - error_setg(errp, "RDMA ERROR: bad RDMA migration address '%s'", - host_port); - g_free(rdma); - rdma = NULL; - } - - qapi_free_InetSocketAddress(addr); + rdma->host = g_strdup(saddr->host); + rdma->port = atoi(saddr->port); return rdma; } @@ -3353,6 +3338,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) .private_data_len = sizeof(cap), }; RDMAContext *rdma_return_path = NULL; + g_autoptr(InetSocketAddress) isock = g_new0(InetSocketAddress, 1); struct rdma_cm_event *cm_event; struct ibv_context *verbs; int ret; @@ -3367,13 +3353,16 @@ static int qemu_rdma_accept(RDMAContext *rdma) goto err_rdma_dest_wait; } + isock->host = rdma->host; + isock->port = g_strdup_printf("%d", rdma->port); + /* * initialize the RDMAContext for return path for postcopy after first * connection request reached. */ if ((migrate_postcopy() || migrate_return_path()) && !rdma->is_return_path) { - rdma_return_path = qemu_rdma_data_init(rdma->host_port, NULL); + rdma_return_path = qemu_rdma_data_init(isock, NULL); if (rdma_return_path == NULL) { rdma_ack_cm_event(cm_event); goto err_rdma_dest_wait; @@ -4074,7 +4063,8 @@ static void rdma_accept_incoming_migration(void *opaque) } } -void rdma_start_incoming_migration(const char *host_port, Error **errp) +void rdma_start_incoming_migration(InetSocketAddress *host_port, + Error **errp) { MigrationState *s = migrate_get_current(); int ret; @@ -4118,13 +4108,12 @@ cleanup_rdma: err: if (rdma) { g_free(rdma->host); - g_free(rdma->host_port); } g_free(rdma); } void rdma_start_outgoing_migration(void *opaque, - const char *host_port, Error **errp) + InetSocketAddress *host_port, Error **errp) { MigrationState *s = opaque; RDMAContext *rdma_return_path = NULL; diff --git a/migration/rdma.h b/migration/rdma.h index 30b15b4466..a8d27f33b8 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -14,15 +14,17 @@ * */ +#include "qemu/sockets.h" + #ifndef QEMU_MIGRATION_RDMA_H #define QEMU_MIGRATION_RDMA_H #include "exec/memory.h" -void rdma_start_outgoing_migration(void *opaque, const char *host_port, +void rdma_start_outgoing_migration(void *opaque, InetSocketAddress *host_port, Error **errp); -void rdma_start_incoming_migration(const char *host_port, Error **errp); +void rdma_start_incoming_migration(InetSocketAddress *host_port, Error **errp); /* * Constants used by rdma return codes diff --git a/migration/savevm.c b/migration/savevm.c index c7835e9c73..bc98c2ea6f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -237,6 +237,8 @@ static SaveState savevm_state = { .global_section_id = 0, }; +static SaveStateEntry *find_se(const char *idstr, uint32_t instance_id); + static bool should_validate_capability(int capability) { assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX); @@ -716,6 +718,18 @@ static void savevm_state_handler_insert(SaveStateEntry *nse) assert(priority <= MIG_PRI_MAX); + /* + * This should never happen otherwise migration will probably fail + * silently somewhere because we can be wrongly applying one + * object properties upon another one. Bail out ASAP. + */ + if (find_se(nse->idstr, nse->instance_id)) { + error_report("%s: Detected duplicate SaveStateEntry: " + "id=%s, instance_id=0x%"PRIx32, __func__, + nse->idstr, nse->instance_id); + exit(EXIT_FAILURE); + } + for (i = priority - 1; i >= 0; i--) { se = savevm_state.handler_pri_head[i]; if (se != NULL) { @@ -846,6 +860,24 @@ static void vmstate_check(const VMStateDescription *vmsd) } } +/* + * See comment in hw/intc/xics.c:icp_realize() + * + * This function can be removed when + * pre_2_10_vmstate_register_dummy_icp() is removed. + */ +int vmstate_replace_hack_for_ppc(VMStateIf *obj, int instance_id, + const VMStateDescription *vmsd, + void *opaque) +{ + SaveStateEntry *se = find_se(vmsd->name, instance_id); + + if (se) { + savevm_state_handler_remove(se); + } + return vmstate_register(obj, instance_id, vmsd, opaque); +} + int vmstate_register_with_alias_id(VMStateIf *obj, uint32_t instance_id, const VMStateDescription *vmsd, void *opaque, int alias_id, @@ -1459,6 +1491,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) static int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { + int64_t start_ts_each, end_ts_each; SaveStateEntry *se; int ret; @@ -1475,6 +1508,8 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) continue; } } + + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); trace_savevm_section_start(se->idstr, se->section_id); save_section_header(f, se, QEMU_VM_SECTION_END); @@ -1486,8 +1521,13 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) qemu_file_set_error(f, ret); return -1; } + end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id, + end_ts_each - start_ts_each); } + trace_vmstate_downtime_checkpoint("src-iterable-saved"); + return 0; } @@ -1496,6 +1536,7 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, bool inactivate_disks) { MigrationState *ms = migrate_get_current(); + int64_t start_ts_each, end_ts_each; JSONWriter *vmdesc = ms->vmdesc; int vmdesc_len; SaveStateEntry *se; @@ -1507,11 +1548,17 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, continue; } + start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + ret = vmstate_save(f, se, vmdesc); if (ret) { qemu_file_set_error(f, ret); return ret; } + + end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("non-iterable", se->idstr, se->instance_id, + end_ts_each - start_ts_each); } if (inactivate_disks) { @@ -1547,6 +1594,8 @@ int qemu_savevm_state_complete_precopy_non_iterable(QEMUFile *f, json_writer_free(vmdesc); ms->vmdesc = NULL; + trace_vmstate_downtime_checkpoint("src-non-iterable-saved"); + return 0; } @@ -2088,18 +2137,18 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) Error *local_err = NULL; MigrationIncomingState *mis = opaque; - trace_loadvm_postcopy_handle_run_bh("enter"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-enter"); /* TODO we should move all of this lot into postcopy_ram.c or a shared code * in migration.c */ cpu_synchronize_all_post_init(); - trace_loadvm_postcopy_handle_run_bh("after cpu sync"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cpu-synced"); qemu_announce_self(&mis->announce_timer, migrate_announce_params()); - trace_loadvm_postcopy_handle_run_bh("after announce"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-announced"); /* Make sure all file formats throw away their mutable metadata. * If we get an error here, just don't restart the VM yet. */ @@ -2110,7 +2159,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) autostart = false; } - trace_loadvm_postcopy_handle_run_bh("after invalidate cache"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-cache-invalidated"); dirty_bitmap_mig_before_vm_start(); @@ -2124,7 +2173,7 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) qemu_bh_delete(mis->bh); - trace_loadvm_postcopy_handle_run_bh("return"); + trace_vmstate_downtime_checkpoint("dst-postcopy-bh-vm-started"); } /* After all discards we can start running and asking for pages */ @@ -2505,9 +2554,12 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) } static int -qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) +qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis, + uint8_t type) { + bool trace_downtime = (type == QEMU_VM_SECTION_FULL); uint32_t instance_id, version_id, section_id; + int64_t start_ts, end_ts; SaveStateEntry *se; char idstr[256]; int ret; @@ -2556,12 +2608,23 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } + if (trace_downtime) { + start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + } + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state for instance 0x%"PRIx32" of" " device '%s'", instance_id, idstr); return ret; } + + if (trace_downtime) { + end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_load("non-iterable", se->idstr, + se->instance_id, end_ts - start_ts); + } + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2570,8 +2633,11 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) } static int -qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) +qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis, + uint8_t type) { + bool trace_downtime = (type == QEMU_VM_SECTION_END); + int64_t start_ts, end_ts; uint32_t section_id; SaveStateEntry *se; int ret; @@ -2596,12 +2662,23 @@ qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis) return -EINVAL; } + if (trace_downtime) { + start_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + } + ret = vmstate_load(f, se); if (ret < 0) { error_report("error while loading state section id %d(%s)", section_id, se->idstr); return ret; } + + if (trace_downtime) { + end_ts = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_load("iterable", se->idstr, + se->instance_id, end_ts - start_ts); + } + if (!check_section_footer(f, se)) { return -EINVAL; } @@ -2790,14 +2867,14 @@ retry: switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: - ret = qemu_loadvm_section_start_full(f, mis); + ret = qemu_loadvm_section_start_full(f, mis, section_type); if (ret < 0) { goto out; } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: - ret = qemu_loadvm_section_part_end(f, mis); + ret = qemu_loadvm_section_part_end(f, mis, section_type); if (ret < 0) { goto out; } diff --git a/migration/socket.c b/migration/socket.c index 1b6f5baefb..98e3ea1514 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -28,6 +28,8 @@ #include "trace.h" #include "postcopy-ram.h" #include "options.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-sockets.h" struct SocketOutgoingArgs { SocketAddress *saddr; @@ -108,19 +110,19 @@ out: object_unref(OBJECT(sioc)); } -static void -socket_start_outgoing_migration_internal(MigrationState *s, - SocketAddress *saddr, - Error **errp) +void socket_start_outgoing_migration(MigrationState *s, + SocketAddress *saddr, + Error **errp) { QIOChannelSocket *sioc = qio_channel_socket_new(); struct SocketConnectData *data = g_new0(struct SocketConnectData, 1); + SocketAddress *addr = QAPI_CLONE(SocketAddress, saddr); data->s = s; /* in case previous migration leaked it */ qapi_free_SocketAddress(outgoing_args.saddr); - outgoing_args.saddr = saddr; + outgoing_args.saddr = addr; if (saddr->type == SOCKET_ADDRESS_TYPE_INET) { data->hostname = g_strdup(saddr->u.inet.host); @@ -135,18 +137,6 @@ socket_start_outgoing_migration_internal(MigrationState *s, NULL); } -void socket_start_outgoing_migration(MigrationState *s, - const char *str, - Error **errp) -{ - Error *err = NULL; - SocketAddress *saddr = socket_parse(str, &err); - if (!err) { - socket_start_outgoing_migration_internal(s, saddr, &err); - } - error_propagate(errp, err); -} - static void socket_accept_incoming_migration(QIONetListener *listener, QIOChannelSocket *cioc, gpointer opaque) @@ -172,9 +162,8 @@ socket_incoming_migration_end(void *opaque) object_unref(OBJECT(listener)); } -static void -socket_start_incoming_migration_internal(SocketAddress *saddr, - Error **errp) +void socket_start_incoming_migration(SocketAddress *saddr, + Error **errp) { QIONetListener *listener = qio_net_listener_new(); MigrationIncomingState *mis = migration_incoming_get_current(); @@ -213,13 +202,3 @@ socket_start_incoming_migration_internal(SocketAddress *saddr, } } -void socket_start_incoming_migration(const char *str, Error **errp) -{ - Error *err = NULL; - SocketAddress *saddr = socket_parse(str, &err); - if (!err) { - socket_start_incoming_migration_internal(saddr, &err); - } - qapi_free_SocketAddress(saddr); - error_propagate(errp, err); -} diff --git a/migration/socket.h b/migration/socket.h index dc54df4e6c..5e4c33b8ea 100644 --- a/migration/socket.h +++ b/migration/socket.h @@ -19,13 +19,14 @@ #include "io/channel.h" #include "io/task.h" +#include "qemu/sockets.h" void socket_send_channel_create(QIOTaskFunc f, void *data); QIOChannel *socket_send_channel_create_sync(Error **errp); int socket_send_channel_destroy(QIOChannel *send); -void socket_start_incoming_migration(const char *str, Error **errp); +void socket_start_incoming_migration(SocketAddress *saddr, Error **errp); -void socket_start_outgoing_migration(MigrationState *s, const char *str, - Error **errp); +void socket_start_outgoing_migration(MigrationState *s, + SocketAddress *saddr, Error **errp); #endif diff --git a/migration/trace-events b/migration/trace-events index fa9486dffe..de4a743c8a 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -17,7 +17,6 @@ loadvm_handle_recv_bitmap(char *s) "%s" loadvm_postcopy_handle_advise(void) "" loadvm_postcopy_handle_listen(const char *str) "%s" loadvm_postcopy_handle_run(void) "" -loadvm_postcopy_handle_run_bh(const char *str) "%s" loadvm_postcopy_handle_resume(void) "" loadvm_postcopy_ram_handle_discard(void) "" loadvm_postcopy_ram_handle_discard_end(void) "" @@ -48,6 +47,9 @@ savevm_state_cleanup(void) "" savevm_state_complete_precopy(void) "" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" +vmstate_downtime_save(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 +vmstate_downtime_load(const char *type, const char *idstr, uint32_t instance_id, int64_t downtime) "type=%s idstr=%s instance_id=%d downtime=%"PRIi64 +vmstate_downtime_checkpoint(const char *checkpoint) "%s" postcopy_pause_incoming(void) "" postcopy_pause_incoming_continued(void) "" postcopy_page_req_sync(void *host_addr) "sync page req %p" @@ -148,8 +150,6 @@ multifd_tls_outgoing_handshake_complete(void *ioc) "ioc=%p" multifd_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname) "ioc=%p ioctype=%s hostname=%s" # migration.c -await_return_path_close_on_source_close(void) "" -await_return_path_close_on_source_joining(void) "" migrate_set_state(const char *new_state) "new state %s" migrate_fd_cleanup(void) "" migrate_fd_error(const char *error_desc) "error=%s" @@ -166,7 +166,7 @@ migration_completion_postcopy_end_after_complete(void) "" migration_rate_limit_pre(int ms) "%d ms" migration_rate_limit_post(int urgent) "urgent: %d" migration_return_path_end_before(void) "" -migration_return_path_end_after(int rp_error) "%d" +migration_return_path_end_after(void) "" migration_thread_after_loop(void) "" migration_thread_file_err(void) "" migration_thread_setup_complete(void) "" diff --git a/net/slirp.c b/net/slirp.c index c33b3e02e7..25b49c4526 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -46,6 +46,7 @@ #include "qapi/qmp/qdict.h" #include "util.h" #include "migration/register.h" +#include "migration/vmstate.h" #include "migration/qemu-file-types.h" static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) @@ -659,8 +660,8 @@ static int net_slirp_init(NetClientState *peer, const char *model, * specific version? */ g_assert(slirp_state_version() == 4); - register_savevm_live("slirp", 0, slirp_state_version(), - &savevm_slirp_state, s->slirp); + register_savevm_live("slirp", VMSTATE_INSTANCE_ID_ANY, + slirp_state_version(), &savevm_slirp_state, s->slirp); s->poll_notifier.notify = net_slirp_poll_notify; main_loop_poll_add_notifier(&s->poll_notifier); diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 4e55adbe00..c8d7d9868d 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -230,7 +230,8 @@ 'data': { 'model': 'CpuModelInfo' }, 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64' ] } } ## # @query-cpu-model-expansion: @@ -275,7 +276,8 @@ 'returns': 'CpuModelExpansionInfo', 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', - 'TARGET_ARM' ] } } + 'TARGET_ARM', + 'TARGET_LOONGARCH64' ] } } ## # @CpuDefinitionInfo: diff --git a/qapi/migration.json b/qapi/migration.json index e6610af428..975761eebd 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -632,6 +632,28 @@ { 'name': 'zstd', 'if': 'CONFIG_ZSTD' } ] } ## +# @MigMode: +# +# @normal: the original form of migration. (since 8.2) +# +# @cpr-reboot: The migrate command saves state to a file, allowing one to +# quit qemu, reboot to an updated kernel, and restart an updated +# version of qemu. The caller must specify a migration URI +# that writes to and reads from a file. Unlike normal mode, +# the use of certain local storage options does not block the +# migration, but the caller must not modify guest block devices +# between the quit and restart. To avoid saving guest RAM to the +# file, the memory backend must be shared, and the @x-ignore-shared +# migration capability must be set. Guest RAM must be non-volatile +# across reboot, such as by backing it with a dax device, but this +# is not enforced. The restarted qemu arguments must match those +# used to initially start qemu, plus the -incoming option. +# (since 8.2) +## +{ 'enum': 'MigMode', + 'data': [ 'normal', 'cpr-reboot' ] } + +## # @BitmapMigrationBitmapAliasTransform: # # @persistent: If present, the bitmap will be made persistent or @@ -849,6 +871,9 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # +# @mode: Migration mode. See description in @MigMode. Default is 'normal'. +# (Since 8.2) +# # Features: # # @deprecated: Member @block-incremental is deprecated. Use @@ -881,7 +906,8 @@ 'multifd-zlib-level', 'multifd-zstd-level', 'block-bitmap-mapping', { 'name': 'x-vcpu-dirty-limit-period', 'features': ['unstable'] }, - 'vcpu-dirty-limit'] } + 'vcpu-dirty-limit', + 'mode'] } ## # @MigrateSetParameters: @@ -1033,6 +1059,9 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # +# @mode: Migration mode. See description in @MigMode. Default is 'normal'. +# (Since 8.2) +# # Features: # # @deprecated: Member @block-incremental is deprecated. Use @@ -1085,7 +1114,8 @@ '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], '*x-vcpu-dirty-limit-period': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64'} } + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode'} } ## # @migrate-set-parameters: @@ -1257,6 +1287,9 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # +# @mode: Migration mode. See description in @MigMode. Default is 'normal'. +# (Since 8.2) +# # Features: # # @deprecated: Member @block-incremental is deprecated. Use @@ -1306,7 +1339,8 @@ '*block-bitmap-mapping': [ 'BitmapMigrationNodeAlias' ], '*x-vcpu-dirty-limit-period': { 'type': 'uint64', 'features': [ 'unstable' ] }, - '*vcpu-dirty-limit': 'uint64'} } + '*vcpu-dirty-limit': 'uint64', + '*mode': 'MigMode'} } ## # @query-migrate-parameters: @@ -1551,12 +1585,100 @@ { 'command': 'migrate-continue', 'data': {'state': 'MigrationStatus'} } ## +# @MigrationAddressType: +# +# The migration stream transport mechanisms. +# +# @socket: Migrate via socket. +# +# @exec: Direct the migration stream to another process. +# +# @rdma: Migrate via RDMA. +# +# @file: Direct the migration stream to a file. +# +# Since 8.2 +## +{ 'enum': 'MigrationAddressType', + 'data': [ 'socket', 'exec', 'rdma', 'file' ] } + +## +# @FileMigrationArgs: +# +# @filename: The file to receive the migration stream +# +# @offset: The file offset where the migration stream will start +# +# Since 8.2 +## +{ 'struct': 'FileMigrationArgs', + 'data': { 'filename': 'str', + 'offset': 'uint64' } } + +## +# @MigrationExecCommand: +# +# @args: command (list head) and arguments to execute. +# +# Since 8.2 +## +{ 'struct': 'MigrationExecCommand', + 'data': {'args': [ 'str' ] } } + +## +# @MigrationAddress: +# +# Migration endpoint configuration. +# +# Since 8.2 +## +{ 'union': 'MigrationAddress', + 'base': { 'transport' : 'MigrationAddressType'}, + 'discriminator': 'transport', + 'data': { + 'socket': 'SocketAddress', + 'exec': 'MigrationExecCommand', + 'rdma': 'InetSocketAddress', + 'file': 'FileMigrationArgs' } } + +## +# @MigrationChannelType: +# +# The migration channel-type request options. +# +# @main: Main outbound migration channel. +# +# Since 8.1 +## +{ 'enum': 'MigrationChannelType', + 'data': [ 'main' ] } + +## +# @MigrationChannel: +# +# Migration stream channel parameters. +# +# @channel-type: Channel type for transfering packet information. +# +# @addr: Migration endpoint configuration on destination interface. +# +# Since 8.1 +## +{ 'struct': 'MigrationChannel', + 'data': { + 'channel-type': 'MigrationChannelType', + 'addr': 'MigrationAddress' } } + +## # @migrate: # # Migrates the current running guest to another Virtual Machine. # # @uri: the Uniform Resource Identifier of the destination VM # +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. +# # @blk: do block migration (full disk copy) # # @inc: incremental disk copy migration @@ -1586,13 +1708,57 @@ # 3. The user Monitor's "detach" argument is invalid in QMP and should # not be used # +# 4. The uri argument should have the Uniform Resource Identifier of +# default destination VM. This connection will be bound to default +# network. +# +# 5. For now, number of migration streams is restricted to one, i.e +# number of items in 'channels' list is just 1. +# +# 6. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. +# # Example: # # -> { "execute": "migrate", "arguments": { "uri": "tcp:0:4446" } } # <- { "return": {} } +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "socket", +# "type": "inet", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "exec", +# "args": [ "/bin/nc", "-p", "6000", +# "/some/sock" ] } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "rdma", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "file", +# "filename": "/tmp/migfile", +# "offset": "0x1000" } } ] } } +# <- { "return": {} } +# ## { 'command': 'migrate', 'data': {'uri': 'str', + '*channels': [ 'MigrationChannel' ], '*blk': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*inc': { 'type': 'bool', 'features': [ 'deprecated' ] }, '*detach': 'bool', '*resume': 'bool' } } @@ -1606,6 +1772,9 @@ # @uri: The Uniform Resource Identifier identifying the source or # address to listen on # +# @channels: list of migration stream channels with each stream in the +# list connected to a destination interface endpoint. +# # Returns: nothing on success # # Since: 2.3 @@ -1621,13 +1790,46 @@ # # 3. The uri format is the same as for -incoming # +# 5. For now, number of migration streams is restricted to one, i.e +# number of items in 'channels' list is just 1. +# +# 4. The 'uri' and 'channels' arguments are mutually exclusive; +# exactly one of the two should be present. +# # Example: # # -> { "execute": "migrate-incoming", # "arguments": { "uri": "tcp::4446" } } # <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "socket", +# "type": "inet", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "exec", +# "args": [ "/bin/nc", "-p", "6000", +# "/some/sock" ] } } ] } } +# <- { "return": {} } +# +# -> { "execute": "migrate", +# "arguments": { +# "channels": [ { "channel-type": "main", +# "addr": { "transport": "rdma", +# "host": "10.12.34.9", +# "port": "1050" } } ] } } +# <- { "return": {} } ## -{ 'command': 'migrate-incoming', 'data': {'uri': 'str' } } +{ 'command': 'migrate-incoming', + 'data': {'*uri': 'str', + '*channels': [ 'MigrationChannel' ] } } ## # @xen-save-devices-state: diff --git a/stubs/migr-blocker.c b/stubs/migr-blocker.c index 17a5dbf87b..11cbff268f 100644 --- a/stubs/migr-blocker.c +++ b/stubs/migr-blocker.c @@ -6,6 +6,16 @@ int migrate_add_blocker(Error **reasonp, Error **errp) return 0; } +int migrate_add_blocker_normal(Error **reasonp, Error **errp) +{ + return 0; +} + +int migrate_add_blocker_modes(Error **reasonp, Error **errp, MigMode mode, ...) +{ + return 0; +} + void migrate_del_blocker(Error **reasonp) { } diff --git a/system/vl.c b/system/vl.c index 92d29bf521..3fb569254a 100644 --- a/system/vl.c +++ b/system/vl.c @@ -2702,7 +2702,7 @@ void qmp_x_exit_preconfig(Error **errp) if (incoming) { Error *local_err = NULL; if (strcmp(incoming, "defer") != 0) { - qmp_migrate_incoming(incoming, &local_err); + qmp_migrate_incoming(incoming, false, NULL, &local_err); if (local_err) { error_reportf_err(local_err, "-incoming %s: ", incoming); exit(1); diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 66212cd7ec..954d358268 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -669,6 +669,11 @@ static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; } +static inline bool isar_feature_aa64_mte3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 3; +} + static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 954328d72a..df6496b019 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -296,6 +296,8 @@ static void arm_cpu_reset_hold(Object *obj) env->cp15.sctlr_el[1] |= SCTLR_TSCXT; /* Disable access to Debug Communication Channel (DCC). */ env->cp15.mdscr_el1 |= 1 << 12; + /* Enable FEAT_MOPS */ + env->cp15.sctlr_el[1] |= SCTLR_MSCEN; #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { diff --git a/target/arm/internals.h b/target/arm/internals.h index f7224e6f4d..c837506e44 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1181,7 +1181,6 @@ typedef struct ARMCacheAttrs { unsigned int attrs:8; unsigned int shareability:2; /* as in the SH field of the VMSAv8-64 PTEs */ bool is_s2_format:1; - bool guarded:1; /* guarded bit of the v8-64 PTE */ } ARMCacheAttrs; /* Fields that are valid upon success. */ diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 53713e0300..1762b058ae 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3032,7 +3032,6 @@ static ARMCacheAttrs combine_cacheattrs(uint64_t hcr, assert(!s1.is_s2_format); ret.is_s2_format = false; - ret.guarded = s1.guarded; if (s1.attrs == 0xf0) { tagged = true; @@ -3175,7 +3174,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, hwaddr ipa; int s1_prot, s1_lgpgsz; ARMSecuritySpace in_space = ptw->in_space; - bool ret, ipa_secure; + bool ret, ipa_secure, s1_guarded; ARMCacheAttrs cacheattrs1; ARMSecuritySpace ipa_space; uint64_t hcr; @@ -3202,6 +3201,7 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, */ s1_prot = result->f.prot; s1_lgpgsz = result->f.lg_page_size; + s1_guarded = result->f.extra.arm.guarded; cacheattrs1 = result->cacheattrs; memset(result, 0, sizeof(*result)); @@ -3252,6 +3252,9 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, result->cacheattrs = combine_cacheattrs(hcr, cacheattrs1, result->cacheattrs); + /* No BTI GP information in stage 2, we just use the S1 value */ + result->f.extra.arm.guarded = s1_guarded; + /* * Check if IPA translates to secure or non-secure PA space. * Note that VSTCR overrides VTCR and {N}SW overrides {N}SA. diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 84f54750fc..ce4800b8d1 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1206,6 +1206,15 @@ static void check_setg_alignment(CPUARMState *env, uint64_t ptr, uint64_t size, } } +static uint64_t arm_reg_or_xzr(CPUARMState *env, int reg) +{ + /* + * Runtime equivalent of cpu_reg() -- return the CPU register value, + * for contexts when index 31 means XZR (not SP). + */ + return reg == 31 ? 0 : env->xregs[reg]; +} + /* * For the Memory Set operation, our implementation chooses * always to use "option A", where we update Xd to the final @@ -1226,7 +1235,7 @@ static void do_setp(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, int rd = mops_destreg(syndrome); int rs = mops_srcreg(syndrome); int rn = mops_sizereg(syndrome); - uint8_t data = env->xregs[rs]; + uint8_t data = arm_reg_or_xzr(env, rs); uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); uint64_t toaddr = env->xregs[rd]; uint64_t setsize = env->xregs[rn]; @@ -1286,7 +1295,7 @@ static void do_setm(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, int rd = mops_destreg(syndrome); int rs = mops_srcreg(syndrome); int rn = mops_sizereg(syndrome); - uint8_t data = env->xregs[rs]; + uint8_t data = arm_reg_or_xzr(env, rs); uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; uint64_t setsize = -env->xregs[rn]; uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); @@ -1349,7 +1358,7 @@ static void do_sete(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, int rd = mops_destreg(syndrome); int rs = mops_srcreg(syndrome); int rn = mops_sizereg(syndrome); - uint8_t data = env->xregs[rs]; + uint8_t data = arm_reg_or_xzr(env, rs); uint64_t toaddr = env->xregs[rd] + env->xregs[rn]; uint64_t setsize = -env->xregs[rn]; uint32_t memidx = FIELD_EX32(mtedesc, MTEDESC, MIDX); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7b39962f20..296e7d1ce2 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4294,7 +4294,7 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); t16 = tcg_temp_new_i128(); - for (i = 0; i < len_align; i += 8) { + for (i = 0; i < len_align; i += 16) { tcg_gen_ld_i64(t0, base, vofs + i); tcg_gen_ld_i64(t1, base, vofs + i + 8); tcg_gen_concat_i64_i128(t16, t0, t1); @@ -4320,7 +4320,8 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, t16 = tcg_temp_new_i128(); tcg_gen_concat_i64_i128(t16, t0, t1); - tcg_gen_qemu_st_i128(t16, clean_addr, midx, MO_LEUQ); + tcg_gen_qemu_st_i128(t16, clean_addr, midx, + MO_LE | MO_128 | MO_ATOM_NONE); tcg_gen_addi_i64(clean_addr, clean_addr, 16); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index 1265dc7cb5..cfe195db4e 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -12,6 +12,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 48 #define TARGET_VIRT_ADDR_SPACE_BITS 48 -#define TARGET_PAGE_BITS 14 +#define TARGET_PAGE_BITS 12 #endif diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ef1bf89dac..a60d07acd5 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -443,6 +443,7 @@ static void loongarch_la464_initfn(Object *obj) env->cpucfg[20] = data; env->CSR_ASID = FIELD_DP64(0, CSR_ASID, ASIDBITS, 0xa); + loongarch_cpu_post_init(obj); } static void loongarch_la132_initfn(Object *obj) @@ -474,6 +475,12 @@ static void loongarch_la132_initfn(Object *obj) env->cpucfg[1] = data; } +static void loongarch_max_initfn(Object *obj) +{ + /* '-cpu max' for TCG: we use cpu la464. */ + loongarch_la464_initfn(obj); +} + static void loongarch_cpu_list_entry(gpointer data, gpointer user_data) { const char *typename = object_class_get_name(OBJECT_CLASS(data)); @@ -616,6 +623,72 @@ static const MemoryRegionOps loongarch_qemu_ops = { }; #endif +static bool loongarch_get_lsx(Object *obj, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + bool ret; + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + ret = true; + } else { + ret = false; + } + return ret; +} + +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (value) { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); + } else { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 0); + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + } +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + bool ret; + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + ret = true; + } else { + ret = false; + } + return ret; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (value) { + if (!FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LSX, 1); + } + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 1); + } else { + cpu->env.cpucfg[2] = FIELD_DP32(cpu->env.cpucfg[2], CPUCFG2, LASX, 0); + } +} + +void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + } + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + } +} + static void loongarch_cpu_init(Object *obj) { #ifndef CONFIG_USER_ONLY @@ -829,6 +902,7 @@ static const TypeInfo loongarch_cpu_type_infos[] = { }, DEFINE_LOONGARCH_CPU_TYPE(64, "la464", loongarch_la464_initfn), DEFINE_LOONGARCH_CPU_TYPE(32, "la132", loongarch_la132_initfn), + DEFINE_LOONGARCH_CPU_TYPE(64, "max", loongarch_max_initfn), }; DEFINE_TYPES(loongarch_cpu_type_infos) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 8b54cf109c..9d0f79f814 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -486,4 +486,6 @@ void loongarch_cpu_list(void); #define LOONGARCH_CPU_TYPE_NAME(model) model LOONGARCH_CPU_TYPE_SUFFIX #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU +void loongarch_cpu_post_init(Object *obj); + #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/insn_trans/trans_vec.c.inc b/target/loongarch/insn_trans/trans_vec.c.inc index 98f856bb29..92b1d22e28 100644 --- a/target/loongarch/insn_trans/trans_vec.c.inc +++ b/target/loongarch/insn_trans/trans_vec.c.inc @@ -4,8 +4,6 @@ * Copyright (c) 2022-2023 Loongson Technology Corporation Limited */ -#ifndef CONFIG_USER_ONLY - static bool check_vec(DisasContext *ctx, uint32_t oprsz) { if ((oprsz == 16) && ((ctx->base.tb->flags & HW_FLAGS_EUEN_SXE) == 0)) { @@ -21,15 +19,6 @@ static bool check_vec(DisasContext *ctx, uint32_t oprsz) return true; } -#else - -static bool check_vec(DisasContext *ctx, uint32_t oprsz) -{ - return true; -} - -#endif - static bool gen_vvvv_ptr_vl(DisasContext *ctx, arg_vvvv *a, uint32_t oprsz, gen_helper_gvec_4_ptr *fn) { diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 6c25957881..645672ff59 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -7,8 +7,13 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qapi/qapi-commands-machine-target.h" #include "cpu.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" +#include "qom/qom-qobject.h" static void loongarch_cpu_add_definition(gpointer data, gpointer user_data) { @@ -35,3 +40,62 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) return cpu_list; } + +static const char *cpu_model_advertised_features[] = { + "lsx", "lasx", NULL +}; + +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + CpuModelExpansionInfo *expansion_info; + QDict *qdict_out; + ObjectClass *oc; + Object *obj; + const char *name; + int i; + + if (type != CPU_MODEL_EXPANSION_TYPE_STATIC) { + error_setg(errp, "The requested expansion type is not supported"); + return NULL; + } + + oc = cpu_class_by_name(TYPE_LOONGARCH_CPU, model->name); + if (!oc) { + error_setg(errp, "The CPU type '%s' is not a recognized LoongArch CPU type", + model->name); + return NULL; + } + + obj = object_new(object_class_get_name(oc)); + + expansion_info = g_new0(CpuModelExpansionInfo, 1); + expansion_info->model = g_malloc0(sizeof(*expansion_info->model)); + expansion_info->model->name = g_strdup(model->name); + + qdict_out = qdict_new(); + + i = 0; + while ((name = cpu_model_advertised_features[i++]) != NULL) { + ObjectProperty *prop = object_property_find(obj, name); + if (prop) { + QObject *value; + + assert(prop->get); + value = object_property_get_qobject(obj, name, &error_abort); + + qdict_put_obj(qdict_out, name, value); + } + } + + if (!qdict_size(qdict_out)) { + qobject_unref(qdict_out); + } else { + expansion_info->model->props = QOBJECT(qdict_out); + } + + object_unref(obj); + + return expansion_info; +} diff --git a/target/loongarch/tlb_helper.c b/target/loongarch/tlb_helper.c index c8b8b0497f..449043c68b 100644 --- a/target/loongarch/tlb_helper.c +++ b/target/loongarch/tlb_helper.c @@ -60,6 +60,9 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, tlb_rplv = 0; } + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); + /* Check access rights */ if (!tlb_v) { return TLBRET_INVALID; @@ -82,10 +85,6 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, return TLBRET_DIRTY; } - /* - * tlb_entry contains ppn[47:12] while 16KiB ppn is [47:15] - * need adjust. - */ *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | (address & MAKE_64BIT_MASK(0, tlb_ps)); *prot = PAGE_READ; @@ -774,7 +773,7 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, /* Move Global bit */ tmp0 = ((tmp0 & (1 << LOONGARCH_HGLOBAL_SHIFT)) >> LOONGARCH_HGLOBAL_SHIFT) << R_TLBENTRY_G_SHIFT | - (tmp0 & (~(1 << R_TLBENTRY_G_SHIFT))); + (tmp0 & (~(1 << LOONGARCH_HGLOBAL_SHIFT))); ps = ptbase + ptwidth - 1; if (odd) { tmp0 += MAKE_64BIT_MASK(ps, 1); diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/avocado/machine_m68k_nextcube.py index d6da2fbb01..f1205d7fc0 100644 --- a/tests/avocado/machine_m68k_nextcube.py +++ b/tests/avocado/machine_m68k_nextcube.py @@ -30,8 +30,8 @@ class NextCubeMachine(QemuSystemTest): timeout = 15 def check_bootrom_framebuffer(self, screenshot_path): - rom_url = ('http://www.nextcomputers.org/NeXTfiles/Software/ROM_Files/' - '68040_Non-Turbo_Chipset/Rev_2.5_v66.BIN') + rom_url = ('https://sourceforge.net/p/previous/code/1350/tree/' + 'trunk/src/Rev_2.5_v66.BIN?format=raw') rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24' rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index d6022ebd64..c9945e69b1 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -216,7 +216,7 @@ qtests_aarch64 = \ (config_all.has_key('CONFIG_TCG') and config_all_devices.has_key('CONFIG_TPM_TIS_SYSBUS') ? \ ['tpm-tis-device-test', 'tpm-tis-device-swtpm-test'] : []) + \ (config_all_devices.has_key('CONFIG_XLNX_ZYNQMP_ARM') ? ['xlnx-can-test', 'fuzz-xlnx-dp-test'] : []) + \ - (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test'] : []) + \ + (config_all_devices.has_key('CONFIG_XLNX_VERSAL') ? ['xlnx-canfd-test', 'xlnx-versal-trng-test'] : []) + \ (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test'] : []) + \ (config_all.has_key('CONFIG_TCG') and \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index bc70a14642..e803b46039 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -728,6 +728,7 @@ typedef struct { /* Postcopy specific fields */ void *postcopy_data; bool postcopy_preempt; + bool postcopy_recovery_test_fail; } MigrateCommon; static int test_migrate_start(QTestState **from, QTestState **to, @@ -1309,7 +1310,12 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, migrate_prepare_for_dirty_mem(from); qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { 'uri': 'tcp:127.0.0.1:0' }}"); + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ] } }"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -1404,6 +1410,80 @@ static void test_postcopy_preempt_tls_psk(void) } #endif +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { "failed", "active", + "completed", NULL }); +} + +#ifndef _WIN32 +static void postcopy_recover_fail(QTestState *from, QTestState *to) +{ + int ret, pair1[2], pair2[2]; + char c; + + /* Create two unrelated socketpairs */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); + g_assert_cmpint(ret, ==, 0); + + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); + g_assert_cmpint(ret, ==, 0); + + /* + * Give the guests unpaired ends of the sockets, so they'll all blocked + * at reading. This mimics a wrong channel established. + */ + qtest_qmp_fds_assert_success(from, &pair1[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qtest_qmp_fds_assert_success(to, &pair2[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + /* + * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to + * emulate the 1st byte of a real recovery, but stops from there to + * keep dest QEMU in RECOVER. This is needed so that we can kick off + * the recover process on dest QEMU (by triggering the G_IO_IN event). + * + * NOTE: this trick is not needed on src QEMUs, because src doesn't + * rely on an pre-existing G_IO_IN event, so it will always trigger the + * upcoming recovery anyway even if it can read nothing. + */ +#define QEMU_VM_COMMAND 0x08 + c = QEMU_VM_COMMAND; + ret = send(pair2[1], &c, 1, 0); + g_assert_cmpint(ret, ==, 1); + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, "fd:fd-mig", "{'resume': true}"); + + /* + * Make sure both QEMU instances will go into RECOVER stage, then test + * kicking them out using migrate-pause. + */ + wait_for_postcopy_status(from, "postcopy-recover"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * This would be issued by the admin upon noticing the hang, we should + * make sure we're able to kick this out. + */ + migrate_pause(from); + wait_for_postcopy_status(from, "postcopy-paused"); + + /* Do the same test on dest */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair1[1]); + close(pair2[0]); + close(pair2[1]); +} +#endif /* _WIN32 */ + static void test_postcopy_recovery_common(MigrateCommon *args) { QTestState *from, *to; @@ -1439,9 +1519,19 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * migrate-recover command can only succeed if destination machine * is in the paused state */ - wait_for_migration_status(to, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); + wait_for_postcopy_status(to, "postcopy-paused"); + wait_for_postcopy_status(from, "postcopy-paused"); + +#ifndef _WIN32 + if (args->postcopy_recovery_test_fail) { + /* + * Test when a wrong socket specified for recover, and then the + * ability to kick it out, and continue with a correct socket. + */ + postcopy_recover_fail(from, to); + /* continue with a good recovery */ + } +#endif /* _WIN32 */ /* * Create a new socket to emulate a new channel that is different @@ -1455,9 +1545,6 @@ static void test_postcopy_recovery_common(MigrateCommon *args) * Try to rebuild the migration channel using the resume flag and * the newly created channel */ - wait_for_migration_status(from, "postcopy-paused", - (const char * []) { "failed", "active", - "completed", NULL }); migrate_qmp(from, uri, "{'resume': true}"); /* Restore the postcopy bandwidth to unlimited */ @@ -1482,6 +1569,17 @@ static void test_postcopy_recovery_compress(void) test_postcopy_recovery_common(&args); } +#ifndef _WIN32 +static void test_postcopy_recovery_double_fail(void) +{ + MigrateCommon args = { + .postcopy_recovery_test_fail = true, + }; + + test_postcopy_recovery_common(&args); +} +#endif /* _WIN32 */ + #ifdef CONFIG_GNUTLS static void test_postcopy_recovery_tls_psk(void) { @@ -2026,6 +2124,31 @@ static void test_precopy_file_offset_bad(void) test_file_common(&args, false); } +static void *test_mode_reboot_start(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + return NULL; +} + +static void test_mode_reboot(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .start.use_shmem = true, + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = test_mode_reboot_start + }; + + test_file_common(&args, true); +} + static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -3068,6 +3191,11 @@ int main(int argc, char **argv) qtest_add_func("/migration/postcopy/recovery/compress/plain", test_postcopy_recovery_compress); } +#ifndef _WIN32 + qtest_add_func("/migration/postcopy/recovery/double-failures", + test_postcopy_recovery_double_fail); +#endif /* _WIN32 */ + } qtest_add_func("/migration/bad_dest", test_baddest); @@ -3096,6 +3224,14 @@ int main(int argc, char **argv) qtest_add_func("/migration/precopy/file/offset/bad", test_precopy_file_offset_bad); + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + qtest_add_func("/migration/mode/reboot", test_mode_reboot); + } + #ifdef CONFIG_GNUTLS qtest_add_func("/migration/precopy/unix/tls/psk", test_precopy_unix_tls_psk); diff --git a/tests/qtest/xlnx-versal-trng-test.c b/tests/qtest/xlnx-versal-trng-test.c new file mode 100644 index 0000000000..cef4e575bb --- /dev/null +++ b/tests/qtest/xlnx-versal-trng-test.c @@ -0,0 +1,485 @@ +/* + * QTests for the Xilinx Versal True Random Number Generator device + * + * Copyright (c) 2023 Advanced Micro Devices, Inc. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* Base Address */ +#define TRNG_BASEADDR (0xf1230000) + +/* TRNG_INT_CTRL */ +#define R_TRNG_INT_CTRL (0x0000) +#define TRNG_INT_CTRL_CERTF_RST_MASK (1 << 5) +#define TRNG_INT_CTRL_DTF_RST_MASK (1 << 4) +#define TRNG_INT_CTRL_DONE_RST_MASK (1 << 3) +#define TRNG_INT_CTRL_CERTF_EN_MASK (1 << 2) +#define TRNG_INT_CTRL_DTF_EN_MASK (1 << 1) +#define TRNG_INT_CTRL_DONE_EN_MASK (1) + +/* TRNG_STATUS */ +#define R_TRNG_STATUS (0x0004) +#define TRNG_STATUS_QCNT_SHIFT (9) +#define TRNG_STATUS_QCNT_MASK (7 << TRNG_STATUS_QCNT_SHIFT) +#define TRNG_STATUS_CERTF_MASK (1 << 3) +#define TRNG_STATUS_DTF_MASK (1 << 1) +#define TRNG_STATUS_DONE_MASK (1) + +/* TRNG_CTRL */ +#define R_TRNG_CTRL (0x0008) +#define TRNG_CTRL_PERSODISABLE_MASK (1 << 10) +#define TRNG_CTRL_SINGLEGENMODE_MASK (1 << 9) +#define TRNG_CTRL_PRNGMODE_MASK (1 << 7) +#define TRNG_CTRL_TSTMODE_MASK (1 << 6) +#define TRNG_CTRL_PRNGSTART_MASK (1 << 5) +#define TRNG_CTRL_PRNGXS_MASK (1 << 3) +#define TRNG_CTRL_TRSSEN_MASK (1 << 2) +#define TRNG_CTRL_QERTUEN_MASK (1 << 1) +#define TRNG_CTRL_PRNGSRST_MASK (1) + +/* TRNG_EXT_SEED_0 ... _11 */ +#define R_TRNG_EXT_SEED_0 (0x0040) +#define R_TRNG_EXT_SEED_11 (R_TRNG_EXT_SEED_0 + 4 * 11) + +/* TRNG_PER_STRNG_0 ... 11 */ +#define R_TRNG_PER_STRNG_0 (0x0080) +#define R_TRNG_PER_STRNG_11 (R_TRNG_PER_STRNG_0 + 4 * 11) + +/* TRNG_CORE_OUTPUT */ +#define R_TRNG_CORE_OUTPUT (0x00c0) + +/* TRNG_RESET */ +#define R_TRNG_RESET (0x00d0) +#define TRNG_RESET_VAL_MASK (1) + +/* TRNG_OSC_EN */ +#define R_TRNG_OSC_EN (0x00d4) +#define TRNG_OSC_EN_VAL_MASK (1) + +/* TRNG_TRNG_ISR, _IMR, _IER, _IDR */ +#define R_TRNG_ISR (0x00e0) +#define R_TRNG_IMR (0x00e4) +#define R_TRNG_IER (0x00e8) +#define R_TRNG_IDR (0x00ec) +#define TRNG_IRQ_SLVERR_MASK (1 << 1) +#define TRNG_IRQ_CORE_INT_MASK (1) + +/* + * End test with a formatted error message, by embedding the message + * in a GError. + */ +#define TRNG_FAILED(FMT, ...) \ + do { \ + g_autoptr(GError) err = g_error_new( \ + g_quark_from_static_string(trng_qname), 0, \ + FMT, ## __VA_ARGS__); \ + g_assert_no_error(err); \ + } while (0) + +static const gchar trng_qname[] = "xlnx-versal-trng-test"; + +static const uint32_t prng_seed[12] = { + 0x01234567, 0x12345678, 0x23456789, 0x3456789a, 0x456789ab, 0x56789abc, + 0x76543210, 0x87654321, 0x98765432, 0xa9876543, 0xba987654, 0xfedcba98, +}; + +static const uint32_t pers_str[12] = { + 0x76543210, 0x87654321, 0x98765432, 0xa9876543, 0xba987654, 0xfedcba98, + 0x01234567, 0x12345678, 0x23456789, 0x3456789a, 0x456789ab, 0x56789abc, +}; + +static void trng_test_start(void) +{ + qtest_start("-machine xlnx-versal-virt"); +} + +static void trng_test_stop(void) +{ + qtest_end(); +} + +static void trng_test_set_uint_prop(const char *name, uint64_t value) +{ + const char *path = "/machine/xlnx-versal/trng"; + QDict *response; + + response = qmp("{ 'execute': 'qom-set'," + " 'arguments': {" + " 'path': %s," + " 'property': %s," + " 'value': %llu" + "} }", path, + name, (unsigned long long)value); + g_assert(qdict_haskey(response, "return")); + qobject_unref(response); +} + +static void trng_write(unsigned ra, uint32_t val) +{ + writel(TRNG_BASEADDR + ra, val); +} + +static uint32_t trng_read(unsigned ra) +{ + return readl(TRNG_BASEADDR + ra); +} + +static void trng_bit_set(unsigned ra, uint32_t bits) +{ + trng_write(ra, (trng_read(ra) | bits)); +} + +static void trng_bit_clr(unsigned ra, uint32_t bits) +{ + trng_write(ra, (trng_read(ra) & ~bits)); +} + +static void trng_ctrl_set(uint32_t bits) +{ + trng_bit_set(R_TRNG_CTRL, bits); +} + +static void trng_ctrl_clr(uint32_t bits) +{ + trng_bit_clr(R_TRNG_CTRL, bits); +} + +static uint32_t trng_status(void) +{ + return trng_read(R_TRNG_STATUS); +} + +static unsigned trng_qcnt(void) +{ + uint32_t sta = trng_status(); + + return (sta & TRNG_STATUS_QCNT_MASK) >> TRNG_STATUS_QCNT_SHIFT; +} + +static const char *trng_info(void) +{ + uint32_t sta = trng_status(); + uint32_t ctl = trng_read(R_TRNG_CTRL); + + static char info[64]; + + snprintf(info, sizeof(info), "; status=0x%x, ctrl=0x%x", sta, ctl); + return info; +} + +static void trng_check_status(uint32_t status_mask, const char *act) +{ + uint32_t clear_mask = 0; + uint32_t status; + + /* + * Only selected bits are events in R_TRNG_STATUS, and + * clear them needs to go through R_INT_CTRL. + */ + if (status_mask & TRNG_STATUS_CERTF_MASK) { + clear_mask |= TRNG_INT_CTRL_CERTF_RST_MASK; + } + if (status_mask & TRNG_STATUS_DTF_MASK) { + clear_mask |= TRNG_INT_CTRL_DTF_RST_MASK; + } + if (status_mask & TRNG_STATUS_DONE_MASK) { + clear_mask |= TRNG_INT_CTRL_DONE_RST_MASK; + } + + status = trng_status(); + if ((status & status_mask) != status_mask) { + TRNG_FAILED("%s: Status bitmask 0x%x failed to be 1%s", + act, status_mask, trng_info()); + } + + /* Remove event */ + trng_bit_set(R_TRNG_INT_CTRL, clear_mask); + + if (!!(trng_read(R_TRNG_STATUS) & status_mask)) { + TRNG_FAILED("%s: Event 0x%0x stuck at 1 after clear: %s", + act, status_mask, trng_info()); + } +} + +static void trng_check_done_status(const char *act) +{ + trng_check_status(TRNG_STATUS_DONE_MASK, act); +} + +static void trng_check_dtf_status(void) +{ + trng_check_status(TRNG_STATUS_DTF_MASK, "DTF injection"); +} + +static void trng_check_certf_status(void) +{ + trng_check_status(TRNG_STATUS_CERTF_MASK, "CERTF injection"); +} + +static void trng_reset(void) +{ + trng_write(R_TRNG_RESET, TRNG_RESET_VAL_MASK); + trng_write(R_TRNG_RESET, 0); +} + +static void trng_load(unsigned r0, const uint32_t *b384) +{ + static const uint32_t zero[12] = { 0 }; + unsigned k; + + if (!b384) { + b384 = zero; + } + + for (k = 0; k < 12; k++) { + trng_write(r0 + 4 * k, b384[k]); + } +} + +static void trng_reseed(const uint32_t *seed) +{ + const char *act; + uint32_t ctl; + + ctl = TRNG_CTRL_PRNGSTART_MASK | + TRNG_CTRL_PRNGXS_MASK | + TRNG_CTRL_TRSSEN_MASK; + + trng_ctrl_clr(ctl | TRNG_CTRL_PRNGMODE_MASK); + + if (seed) { + trng_load(R_TRNG_EXT_SEED_0, seed); + act = "Reseed PRNG"; + ctl &= ~TRNG_CTRL_TRSSEN_MASK; + } else { + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); + act = "Reseed TRNG"; + ctl &= ~TRNG_CTRL_PRNGXS_MASK; + } + + trng_ctrl_set(ctl); + trng_check_done_status(act); + trng_ctrl_clr(TRNG_CTRL_PRNGSTART_MASK); +} + +static void trng_generate(bool auto_enb) +{ + uint32_t ctl; + + ctl = TRNG_CTRL_PRNGSTART_MASK | TRNG_CTRL_SINGLEGENMODE_MASK; + trng_ctrl_clr(ctl); + + if (auto_enb) { + ctl &= ~TRNG_CTRL_SINGLEGENMODE_MASK; + } + + trng_ctrl_set(ctl | TRNG_CTRL_PRNGMODE_MASK); + + trng_check_done_status("Generate"); + g_assert(trng_qcnt() != 7); +} + +static size_t trng_collect(uint32_t *rnd, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) { + if (trng_qcnt() == 0) { + return i; + } + + rnd[i] = trng_read(R_TRNG_CORE_OUTPUT); + } + + return i; +} + +static void trng_test_autogen(void) +{ + const size_t cnt = 512 / 32; + uint32_t rng[cnt], prng[cnt]; + size_t n; + + trng_reset(); + + /* PRNG run #1 */ + trng_reseed(prng_seed); + trng_generate(true); + + n = trng_collect(prng, cnt); + if (n != cnt) { + TRNG_FAILED("PRNG_1 Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + /* TRNG, should not match PRNG */ + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(rng, cnt); + if (n != cnt) { + TRNG_FAILED("TRNG Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + /* PRNG #2: should matches run #1 */ + trng_reseed(prng_seed); + trng_generate(true); + + n = trng_collect(rng, cnt); + if (n != cnt) { + TRNG_FAILED("PRNG_2 Auto-gen test failed: expected = %u, got = %u", + (unsigned)cnt, (unsigned)n); + } + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("PRNG_2 Auto-gen test failed: does not match PRNG_1"); + } +} + +static void trng_test_oneshot(void) +{ + const size_t cnt = 512 / 32; + uint32_t rng[cnt]; + size_t n; + + trng_reset(); + + /* PRNG run #1 */ + trng_reseed(prng_seed); + trng_generate(false); + + n = trng_collect(rng, cnt); + if (n == cnt) { + TRNG_FAILED("PRNG_1 One-shot gen test failed"); + } + + /* TRNG, should not match PRNG */ + trng_reseed(NULL); + trng_generate(false); + + n = trng_collect(rng, cnt); + if (n == cnt) { + TRNG_FAILED("TRNG One-shot test failed"); + } +} + +static void trng_test_per_str(void) +{ + const size_t cnt = 512 / 32; + uint32_t rng[cnt], prng[cnt]; + size_t n; + + trng_reset(); + + /* #1: disabled */ + trng_ctrl_set(TRNG_CTRL_PERSODISABLE_MASK); + trng_reseed(prng_seed); + trng_ctrl_clr(TRNG_CTRL_PERSODISABLE_MASK); + + trng_generate(true); + n = trng_collect(prng, cnt); + g_assert_cmpuint(n, ==, cnt); + + /* #2: zero string should match personalization disabled */ + trng_load(R_TRNG_PER_STRNG_0, NULL); + trng_reseed(prng_seed); + + trng_generate(true); + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Failed: PER_DISABLE != PER_STRNG_ALL_ZERO"); + } + + /* #3: non-zero string should not match personalization disabled */ + trng_load(R_TRNG_PER_STRNG_0, pers_str); + trng_reseed(prng_seed); + + trng_generate(true); + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (!memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Failed: PER_DISABLE == PER_STRNG_NON_ZERO"); + } +} + +static void trng_test_forced_prng(void) +{ + const char *prop = "forced-prng"; + const uint64_t seed = 0xdeadbeefbad1bad0ULL; + + const size_t cnt = 512 / 32; + uint32_t rng[cnt], prng[cnt]; + size_t n; + + trng_reset(); + trng_test_set_uint_prop(prop, seed); + + /* TRNG run #1 */ + trng_reset(); + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(prng, cnt); + g_assert_cmpuint(n, ==, cnt); + + /* TRNG run #2 should match run #1 */ + trng_reset(); + trng_reseed(NULL); + trng_generate(true); + + n = trng_collect(rng, cnt); + g_assert_cmpuint(n, ==, cnt); + + if (memcmp(rng, prng, sizeof(rng))) { + TRNG_FAILED("Forced-prng test failed: results do not match"); + } +} + +static void trng_test_fault_events(void) +{ + const char *prop = "fips-fault-events"; + + trng_reset(); + + /* Fault events only when TRSS is enabled */ + trng_write(R_TRNG_OSC_EN, TRNG_OSC_EN_VAL_MASK); + trng_ctrl_set(TRNG_CTRL_TRSSEN_MASK); + + trng_test_set_uint_prop(prop, TRNG_STATUS_CERTF_MASK); + trng_check_certf_status(); + + trng_test_set_uint_prop(prop, TRNG_STATUS_DTF_MASK); + trng_check_dtf_status(); + + trng_reset(); +} + +int main(int argc, char **argv) +{ + int rc; + + g_test_init(&argc, &argv, NULL); + + #define TRNG_TEST_ADD(n) \ + qtest_add_func("/hw/misc/xlnx-versal-trng/" #n, trng_test_ ## n); + TRNG_TEST_ADD(autogen); + TRNG_TEST_ADD(oneshot); + TRNG_TEST_ADD(per_str); + TRNG_TEST_ADD(forced_prng); + TRNG_TEST_ADD(fault_events); + #undef TRNG_TEST_ADD + + trng_test_start(); + rc = g_test_run(); + trng_test_stop(); + + return rc; +} diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 0c84b61ae0..cded1d01fc 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -103,7 +103,11 @@ sha512-sve: CFLAGS=-O3 -march=armv8.1-a+sve sha512-sve: sha512.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -TESTS += sha512-sve +sve-str: CFLAGS=-O1 -march=armv8.1-a+sve +sve-str: sve-str.c + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +TESTS += sha512-sve sve-str ifneq ($(GDB),) GDB_SCRIPT=$(SRC_PATH)/tests/guest-debug/run-test.py diff --git a/tests/tcg/aarch64/sve-str.c b/tests/tcg/aarch64/sve-str.c new file mode 100644 index 0000000000..ae271c9d87 --- /dev/null +++ b/tests/tcg/aarch64/sve-str.c @@ -0,0 +1,49 @@ +#include <stdio.h> +#include <sys/prctl.h> + +#define N (256 + 16) + +static int __attribute__((noinline)) test(int vl) +{ + unsigned char buf[N]; + int err = 0; + + for (int i = 0; i < N; ++i) { + buf[i] = (unsigned char)i; + } + + asm volatile ( + "mov z0.b, #255\n\t" + "str z0, %0" + : : "m" (buf) : "z0", "memory"); + + for (int i = 0; i < vl; ++i) { + if (buf[i] != 0xff) { + fprintf(stderr, "vl %d, index %d, expected 255, got %d\n", + vl, i, buf[i]); + err = 1; + } + } + + for (int i = vl; i < N; ++i) { + if (buf[i] != (unsigned char)i) { + fprintf(stderr, "vl %d, index %d, expected %d, got %d\n", + vl, i, (unsigned char)i, buf[i]); + err = 1; + } + } + + return err; +} + +int main() +{ + int err = 0; + + for (int i = 16; i <= 256; i += 16) { + if (prctl(PR_SVE_SET_VL, i, 0, 0, 0, 0) == i) { + err |= test(i); + } + } + return err; +} |