target-arm queue:

* linux-user/elfload: Add missing arm64 hwcap values
  * stellaris-gamepad: Convert to qdev
  * docs/specs: Convert various txt docs to rST
  * MAINTAINERS: Make sure that gicv3_internal.h is covered, too
  * hw/arm/pxa2xx_gpio: Pass CPU using QOM link property
  * hw/watchdog/wdt_imx2: Trace MMIO access and timer activity
  * hw/misc/imx7_snvs: Trace MMIO access
  * hw/misc/imx6_ccm: Convert DPRINTF to trace events
  * hw/i2c/pm_smbus: Convert DPRINTF to trace events
  * target/arm: Enable FEAT_MOPS insns in user-mode emulation
  * linux-user: Report AArch64 hwcap2 fields above bit 31
  * target/arm: Make FEAT_MOPS SET* insns handle Xs == XZR correctly
  * target/arm: Fix SVE STR increment
  * hw/char/stm32f2xx_usart: implement TX interrupts
  * target/arm: Correctly propagate stage 1 BTI guarded bit in a two-stage walk
  * xlnx-versal-virt: Add AMD/Xilinx TRNG device
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmVD3hEZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3kuRD/4mLL2DB+yvQJrzSvUlrjfi
 /orPDrY9xEQ7ln2YpNqc2BZ4wAgh947yk/ae5+lyACQcBhCPiwMyVK1bBscNxkgA
 8YPmuugNem/64+IHiKkz6aroqjvC83dUzJ9R5O9ctV70mgrX32YnhXNkkYVI81Ar
 bEwBznyYeCiy8ZafVxc2m70fiBOlurb6htYYdt7VHsgB0ozK/80UmuFI6exOKt1r
 oVyYouMaidNV/AoqZBGSKT2UFvFmI57PWN0YQD8CMECLsB/mBE9TEzSvLRdlOB4G
 qI5hgEJks61qDL6+YMJ+hskxW+D3g3I1WjuyqhKfiAzcKmmTAp1NsiiDtva8yBzX
 lDUXc6bPomalrKo1SPsooJv9r4uE3hCayDOlR+qM38DL4j2soSd3QIP7dCzERbZx
 snrD+ZTtgXtomUN8ojbnOK+kClEfURZ+wALbUEXwAh1sBwrKBxaD4ss4lA2esq10
 HJPjBJzAWoSmK2DY6GWt2xIa+GvQwdPnxMpHbp3yAddGP7i/lHM0x60q5YpjHV++
 DHaZmLBA7L9wcvT1VrwmieJaB+ADcSfkzBz2KznC4usdEY8BiJhjdRAzkqdGZWV5
 HKEg8QwMYHg4QRUoZxW/XdtVzdqcjO5pTSUr3HUE+85sum2e9Yee6rybg1W/EWYv
 7SnVkD5zG1BU268/p5k6UA==
 =OgfH
 -----END PGP SIGNATURE-----

Merge tag 'pull-target-arm-20231102' of https://git.linaro.org/people/pmaydell/qemu-arm into staging

target-arm queue:
 * linux-user/elfload: Add missing arm64 hwcap values
 * stellaris-gamepad: Convert to qdev
 * docs/specs: Convert various txt docs to rST
 * MAINTAINERS: Make sure that gicv3_internal.h is covered, too
 * hw/arm/pxa2xx_gpio: Pass CPU using QOM link property
 * hw/watchdog/wdt_imx2: Trace MMIO access and timer activity
 * hw/misc/imx7_snvs: Trace MMIO access
 * hw/misc/imx6_ccm: Convert DPRINTF to trace events
 * hw/i2c/pm_smbus: Convert DPRINTF to trace events
 * target/arm: Enable FEAT_MOPS insns in user-mode emulation
 * linux-user: Report AArch64 hwcap2 fields above bit 31
 * target/arm: Make FEAT_MOPS SET* insns handle Xs == XZR correctly
 * target/arm: Fix SVE STR increment
 * hw/char/stm32f2xx_usart: implement TX interrupts
 * target/arm: Correctly propagate stage 1 BTI guarded bit in a two-stage walk
 * xlnx-versal-virt: Add AMD/Xilinx TRNG device

* tag 'pull-target-arm-20231102' of https://git.linaro.org/people/pmaydell/qemu-arm: (33 commits)
  tests/qtest: Introduce tests for AMD/Xilinx Versal TRNG device
  hw/arm: xlnx-versal-virt: Add AMD/Xilinx TRNG device
  hw/misc: Introduce AMD/Xilix Versal TRNG device
  target/arm: Correctly propagate stage 1 BTI guarded bit in a two-stage walk
  hw/char/stm32f2xx_usart: Add more definitions for CR1 register
  hw/char/stm32f2xx_usart: Update IRQ when DR is written
  hw/char/stm32f2xx_usart: Extract common IRQ update code to update_irq()
  target/arm: Fix SVE STR increment
  target/arm: Make FEAT_MOPS SET* insns handle Xs == XZR correctly
  linux-user: Report AArch64 hwcap2 fields above bit 31
  target/arm: Enable FEAT_MOPS insns in user-mode emulation
  hw/i2c/pm_smbus: Convert DPRINTF to trace events
  hw/misc/imx6_ccm: Convert DPRINTF to trace events
  hw/misc/imx7_snvs: Trace MMIO access
  hw/watchdog/wdt_imx2: Trace timer activity
  hw/watchdog/wdt_imx2: Trace MMIO access
  hw/arm/pxa2xx_gpio: Pass CPU using QOM link property
  MAINTAINERS: Make sure that gicv3_internal.h is covered, too
  docs/specs/vmgenid: Convert to rST
  docs/specs/vmcoreinfo: Convert to rST
  ...

Conflicts:
  hw/input/stellaris_input.c
  The qdev conversion in this pull request ("stellaris-gamepad: Convert
  to qdev") eliminates the vmstate_register() call that was converted to
  vmstate_register_any() in the conflicting migration pull request.
  vmstate_register_any() is no longer necessary now that this device has
  been converted to qdev, so take this pull request's version of
  stellaris_gamepad.c over the previous pull request's
  stellaris_input.c (the file was renamed).

Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Stefan Hajnoczi 2023-11-03 10:04:12 +08:00
commit d762bf9793
56 changed files with 2302 additions and 776 deletions

View file

@ -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>

View file

@ -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)
;
PCI address ``addr``:
0x40000 -> DMA source address
addr+100 -> DMA destination address
100 -> DMA transfer count
3 -> DMA command register
while (DMA command register & 1)
;
::
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)
;

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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
~~~~
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
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.
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.

View file

@ -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.

View file

@ -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.

View file

@ -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).

54
docs/specs/vmcoreinfo.rst Normal file
View file

@ -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.

View file

@ -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.

246
docs/specs/vmgenid.rst Normal file
View file

@ -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.

View file

@ -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.

View file

@ -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

View file

@ -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.

View file

@ -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
~~~~~~~~~~~~~~~~~~~~~~

View file

@ -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

View file

@ -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(),
};

View file

@ -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 };
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_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 */
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);
stellaris_gamepad_init(5, gpad_irq, gpad_keycode);
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)) {

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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
*

View file

@ -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
*

View file

@ -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));

View file

@ -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

View file

@ -20,7 +20,7 @@ config PL050
config PS2
bool
config STELLARIS_INPUT
config STELLARIS_GAMEPAD
bool
config TSC2005

View file

@ -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'))

View file

@ -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);

View file

@ -1,92 +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_any(NULL, &vmstate_stellaris_gamepad, s);
}

View file

@ -197,4 +197,7 @@ config DJMEMC
config IOSB
bool
config XLNX_VERSAL_TRNG
bool
source macio/Kconfig

View file

@ -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:

View file

@ -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);
}

View file

@ -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'))

View file

@ -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"

717
hw/misc/xlnx-versal-trng.c Normal file
View file

@ -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, &reg_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)

View file

@ -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

View file

@ -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) {

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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 |

View file

@ -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

View file

@ -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;

View file

@ -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)) {

View file

@ -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. */

View file

@ -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.

View file

@ -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);

View file

@ -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);

View file

@ -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'] : []) + \

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}