target-arm queue:

* hw/arm/omap1: Remove unused omap_uwire_attach() method
  * stm32f405: Add RCC device to stm32f405 SoC
  * arm/gicv3: add missing casts
  * hw/misc: Create STM32L4x5 SYSCFG clock
  * hw/arm: Add SPI to Allwinner A10
  * hw/intc/omap_intc: Remove now-unnecessary abstract base class
  * hw/char/pl011: Use correct masks for IBRD and FBRD
  * docs/devel: Convert txt files to rST
  * Remove MAX111X, MAX7310, DSCM-1XXXX, pcmcia devices (used only
    by now-removed omap/pxa2xx boards)
  * vl.c: Remove pxa2xx-specific -portrait and -rotate options
  * dma: Fix function names in documentation
  * hw/arm/xilinx_zynq: Add various missing unimplemented devices
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmcOeWEZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3jCMD/482mpT1s+mrEJFWSJJXU4G
 8kr4Zj6+NafbayJ0vHTkpSbkEbPxuvDiUqmlnbI+3o11i+Z3IyiaGZbba7dyNnKl
 02MdQavL0dB+eMrcFNofRRvwvsposuj2ixgwTQe6L32HSFdHerVVwuhHM/wfwyCh
 DKt7gPRovD/7CtwDOSpyW7cK64WK1IUlE8VEsbFdQbCPkopm55LQ2sLT4TshadpG
 A6xcxyLN0x/lHgCmvijB1T09LSc1nQpUEQNIokC4f1Rmy6HNgGDYY1G7GAJf99mT
 nWhATuuhZThiYfRbN5KQoS9tGEUduxtkGhHiOgpdXpgc3cS7RusCHoqAnibpsVh3
 TgAkaRAX1d/jQ2KYR2h2jI3nh66ObhrFRT3dkzRZrIvmK9zeWUKmS9lzZ94aVfPH
 +MtBPwsO5OhzEABs8WpMY9V1nYaYDsFATMc1akUSaSLn1Er9Uz66NIk+J4Lob4P0
 78IPvTmwvAIITiqQvkISsc37n5a2/toeaffU2hPKtQLlhyilWynEZA5YItrXSTuk
 gYIBxyZSbzGj/ofZ9T9C0GDLbhJp9ksNIpIqRUiHOH3z9b85r7HVZORp+COw/ZXR
 UGak6rpJ+XVOxVL/cPRTvZB0RbUHIZh7WLNH2G7Tfv4E4llqL81iuImHXVh/2CXO
 9GWr9qbDLDYQ+BI7ipLAYg==
 =n2CA
 -----END PGP SIGNATURE-----

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

target-arm queue:
 * hw/arm/omap1: Remove unused omap_uwire_attach() method
 * stm32f405: Add RCC device to stm32f405 SoC
 * arm/gicv3: add missing casts
 * hw/misc: Create STM32L4x5 SYSCFG clock
 * hw/arm: Add SPI to Allwinner A10
 * hw/intc/omap_intc: Remove now-unnecessary abstract base class
 * hw/char/pl011: Use correct masks for IBRD and FBRD
 * docs/devel: Convert txt files to rST
 * Remove MAX111X, MAX7310, DSCM-1XXXX, pcmcia devices (used only
   by now-removed omap/pxa2xx boards)
 * vl.c: Remove pxa2xx-specific -portrait and -rotate options
 * dma: Fix function names in documentation
 * hw/arm/xilinx_zynq: Add various missing unimplemented devices

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmcOeWEZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3jCMD/482mpT1s+mrEJFWSJJXU4G
# 8kr4Zj6+NafbayJ0vHTkpSbkEbPxuvDiUqmlnbI+3o11i+Z3IyiaGZbba7dyNnKl
# 02MdQavL0dB+eMrcFNofRRvwvsposuj2ixgwTQe6L32HSFdHerVVwuhHM/wfwyCh
# DKt7gPRovD/7CtwDOSpyW7cK64WK1IUlE8VEsbFdQbCPkopm55LQ2sLT4TshadpG
# A6xcxyLN0x/lHgCmvijB1T09LSc1nQpUEQNIokC4f1Rmy6HNgGDYY1G7GAJf99mT
# nWhATuuhZThiYfRbN5KQoS9tGEUduxtkGhHiOgpdXpgc3cS7RusCHoqAnibpsVh3
# TgAkaRAX1d/jQ2KYR2h2jI3nh66ObhrFRT3dkzRZrIvmK9zeWUKmS9lzZ94aVfPH
# +MtBPwsO5OhzEABs8WpMY9V1nYaYDsFATMc1akUSaSLn1Er9Uz66NIk+J4Lob4P0
# 78IPvTmwvAIITiqQvkISsc37n5a2/toeaffU2hPKtQLlhyilWynEZA5YItrXSTuk
# gYIBxyZSbzGj/ofZ9T9C0GDLbhJp9ksNIpIqRUiHOH3z9b85r7HVZORp+COw/ZXR
# UGak6rpJ+XVOxVL/cPRTvZB0RbUHIZh7WLNH2G7Tfv4E4llqL81iuImHXVh/2CXO
# 9GWr9qbDLDYQ+BI7ipLAYg==
# =n2CA
# -----END PGP SIGNATURE-----
# gpg: Signature made Tue 15 Oct 2024 15:17:05 BST
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# gpg:                 aka "Peter Maydell <peter@archaic.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* tag 'pull-target-arm-20241015-1' of https://git.linaro.org/people/pmaydell/qemu-arm: (28 commits)
  hw/arm/xilinx_zynq: Add various missing unimplemented devices
  dma: Fix function names in documentation
  vl.c: Remove pxa2xx-specific -portrait and -rotate options
  hw/block: Remove ecc
  hw: Remove PCMCIA subsystem
  hw/ide: Remove DSCM-1XXXX microdrive device model
  hw/gpio: Remove MAX7310 device
  hw/adc: Remove MAX111X device
  docs/devel/lockcnt: Include kernel-doc API documentation
  include: Move QemuLockCnt APIs to their own header
  docs/devel/rcu: Convert to rST format
  docs/devel/multiple-iothreads: Convert to rST format
  docs/devel/lockcnt: Convert to rST format
  docs/devel/blkverify: Convert to rST format
  docs/devel/blkdebug: Convert to rST format
  hw/char/pl011: Use correct masks for IBRD and FBRD
  hw/intc/omap_intc: Remove now-unnecessary abstract base class
  hw/arm: Add SPI to Allwinner A10
  hw/ssi: Allwinner A10 SPI emulation
  tests/qtest: Check STM32L4x5 clock connections
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2024-10-15 15:18:22 +01:00
commit f774a67750
81 changed files with 1802 additions and 2048 deletions

View File

@ -1059,6 +1059,8 @@ S: Maintained
F: hw/arm/stm32f405_soc.c
F: hw/misc/stm32f4xx_syscfg.c
F: hw/misc/stm32f4xx_exti.c
F: hw/misc/stm32_rcc.c
F: include/hw/misc/stm32_rcc.h
Netduino 2
M: Alistair Francis <alistair@alistair23.me>
@ -3056,11 +3058,13 @@ F: qapi/run-state.json
Read, Copy, Update (RCU)
M: Paolo Bonzini <pbonzini@redhat.com>
S: Maintained
F: docs/devel/lockcnt.txt
F: docs/devel/rcu.txt
F: docs/devel/lockcnt.rst
F: docs/devel/rcu.rst
F: include/qemu/rcu*.h
F: include/qemu/lockcnt.h
F: tests/unit/rcutorture.c
F: tests/unit/test-rcu-*.c
F: util/lockcnt.c
F: util/rcu.c
Human Monitor (HMP)
@ -3888,6 +3892,7 @@ M: Stefan Hajnoczi <stefanha@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/blkverify.c
F: docs/devel/blkverify.rst
bochs
M: Stefan Hajnoczi <stefanha@redhat.com>
@ -3965,6 +3970,7 @@ M: Hanna Reitz <hreitz@redhat.com>
L: qemu-block@nongnu.org
S: Supported
F: block/blkdebug.c
F: docs/devel/blkdebug.rst
vpc
M: Kevin Wolf <kwolf@redhat.com>

View File

@ -25,6 +25,7 @@
*/
#include "qemu/osdep.h"
#include "qemu/lockcnt.h"
#include "qemu/thread.h"
#include "qemu/main-loop.h"
#include "hw/core/cpu.h"

View File

@ -532,6 +532,29 @@ security model option, or switch to ``virtiofs``. The virtiofs daemon
``virtiofsd`` uses vhost to eliminate the high latency costs of the 9p
``proxy`` backend.
``-portrait`` and ``-rotate`` (since 9.2)
'''''''''''''''''''''''''''''''''''''''''
The ``-portrait`` and ``-rotate`` options were documented as only
working with the PXA LCD device, and all the machine types using
that display device were removed in 9.2, so these options also
have been dropped.
These options were intended to simulate a mobile device being
rotated by the user, and had three effects:
* the display output was rotated by 90, 180 or 270 degrees
* the mouse/trackpad input was rotated the opposite way
* the machine model would signal to the guest about its
orientation
Of these three things, the input-rotation was coded without being
restricted to boards which supported the full set of device-rotation
handling, so in theory the options were usable on other machine models
to produce an odd effect (rotating input but not display output). But
this was never intended or documented behaviour, so we have dropped
the options along with the machine models they were intended for.
User-mode emulator command line arguments
-----------------------------------------

View File

@ -1,162 +0,0 @@
Block I/O error injection using blkdebug
----------------------------------------
Copyright (C) 2014-2015 Red Hat Inc
This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory.
The blkdebug block driver is a rule-based error injection engine. It can be
used to exercise error code paths in block drivers including ENOSPC (out of
space) and EIO.
This document gives an overview of the features available in blkdebug.
Background
----------
Block drivers have many error code paths that handle I/O errors. Image formats
are especially complex since metadata I/O errors during cluster allocation or
while updating tables happen halfway through request processing and require
discipline to keep image files consistent.
Error injection allows test cases to trigger I/O errors at specific points.
This way, all error paths can be tested to make sure they are correct.
Rules
-----
The blkdebug block driver takes a list of "rules" that tell the error injection
engine when to fail an I/O request.
Each I/O request is evaluated against the rules. If a rule matches the request
then its "action" is executed.
Rules can be placed in a configuration file; the configuration file
follows the same .ini-like format used by QEMU's -readconfig option, and
each section of the file represents a rule.
The following configuration file defines a single rule:
$ cat blkdebug.conf
[inject-error]
event = "read_aio"
errno = "28"
This rule fails all aio read requests with ENOSPC (28). Note that the errno
value depends on the host. On Linux, see
/usr/include/asm-generic/errno-base.h for errno values.
Invoke QEMU as follows:
$ qemu-system-x86_64
-drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \
-device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0
Rules support the following attributes:
event - which type of operation to match (e.g. read_aio, write_aio,
flush_to_os, flush_to_disk). See the "Events" section for
information on events.
state - (optional) the engine must be in this state number in order for this
rule to match. See the "State transitions" section for information
on states.
errno - the numeric errno value to return when a request matches this rule.
The errno values depend on the host since the numeric values are not
standardized in the POSIX specification.
sector - (optional) a sector number that the request must overlap in order to
match this rule
once - (optional, default "off") only execute this action on the first
matching request
immediately - (optional, default "off") return a NULL BlockAIOCB
pointer and fail without an errno instead. This
exercises the code path where BlockAIOCB fails and the
caller's BlockCompletionFunc is not invoked.
Events
------
Block drivers provide information about the type of I/O request they are about
to make so rules can match specific types of requests. For example, the qcow2
block driver tells blkdebug when it accesses the L1 table so rules can match
only L1 table accesses and not other metadata or guest data requests.
The core events are:
read_aio - guest data read
write_aio - guest data write
flush_to_os - write out unwritten block driver state (e.g. cached metadata)
flush_to_disk - flush the host block device's disk cache
See qapi/block-core.json:BlkdebugEvent for the full list of events.
You may need to grep block driver source code to understand the
meaning of specific events.
State transitions
-----------------
There are cases where more power is needed to match a particular I/O request in
a longer sequence of requests. For example:
write_aio
flush_to_disk
write_aio
How do we match the 2nd write_aio but not the first? This is where state
transitions come in.
The error injection engine has an integer called the "state" that always starts
initialized to 1. The state integer is internal to blkdebug and cannot be
observed from outside but rules can interact with it for powerful matching
behavior.
Rules can be conditional on the current state and they can transition to a new
state.
When a rule's "state" attribute is non-zero then the current state must equal
the attribute in order for the rule to match.
For example, to match the 2nd write_aio:
[set-state]
event = "write_aio"
state = "1"
new_state = "2"
[inject-error]
event = "write_aio"
state = "2"
errno = "5"
The first write_aio request matches the set-state rule and transitions from
state 1 to state 2. Once state 2 has been entered, the set-state rule no
longer matches since it requires state 1. But the inject-error rule now
matches the next write_aio request and injects EIO (5).
State transition rules support the following attributes:
event - which type of operation to match (e.g. read_aio, write_aio,
flush_to_os, flush_to_disk). See the "Events" section for
information on events.
state - (optional) the engine must be in this state number in order for this
rule to match
new_state - transition to this state number
Suspend and resume
------------------
Exercising code paths in block drivers may require specific ordering amongst
concurrent requests. The "breakpoint" feature allows requests to be halted on
a blkdebug event and resumed later. This makes it possible to achieve
deterministic ordering when multiple requests are in flight.
Breakpoints on blkdebug events are associated with a user-defined "tag" string.
This tag serves as an identifier by which the request can be resumed at a later
point.
See the qemu-io(1) break, resume, remove_break, and wait_break commands for
details.

View File

@ -358,6 +358,12 @@ humans (for instance in debugging), use ``clock_display_freq()``,
which returns a prettified string-representation, e.g. "33.3 MHz".
The caller must free the string with g_free() after use.
It's also possible to retrieve the clock period from a QTest by
accessing QOM property ``qtest-clock-period`` using a QMP command.
This property is only present when the device is being run under
the ``qtest`` accelerator; it is not available when QEMU is
being run normally.
Calculating expiry deadlines
----------------------------

View File

@ -9,6 +9,7 @@ generated from in-code annotations to function prototypes.
bitops
loads-stores
lockcnt
memory
modules
pci

View File

@ -8,6 +8,7 @@ Details about QEMU's various subsystems including how to add features to them.
qom
atomics
rcu
block-coroutine-wrapper
clocks
ebpf_rss
@ -21,3 +22,4 @@ Details about QEMU's various subsystems including how to add features to them.
writing-monitor-commands
virtio-backends
crypto
multiple-iothreads

View File

@ -1,9 +1,9 @@
DOCUMENTATION FOR LOCKED COUNTERS (aka QemuLockCnt)
===================================================
Locked Counters (aka ``QemuLockCnt``)
=====================================
QEMU often uses reference counts to track data structures that are being
accessed and should not be freed. For example, a loop that invoke
callbacks like this is not safe:
callbacks like this is not safe::
QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
if (ioh->revents & G_IO_OUT) {
@ -11,11 +11,11 @@ callbacks like this is not safe:
}
}
QLIST_FOREACH_SAFE protects against deletion of the current node (ioh)
by stashing away its "next" pointer. However, ioh->fd_write could
``QLIST_FOREACH_SAFE`` protects against deletion of the current node (``ioh``)
by stashing away its ``next`` pointer. However, ``ioh->fd_write`` could
actually delete the next node from the list. The simplest way to
avoid this is to mark the node as deleted, and remove it from the
list in the above loop:
list in the above loop::
QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
if (ioh->deleted) {
@ -29,7 +29,7 @@ list in the above loop:
}
If however this loop must also be reentrant, i.e. it is possible that
ioh->fd_write invokes the loop again, some kind of counting is needed:
``ioh->fd_write`` invokes the loop again, some kind of counting is needed::
walking_handlers++;
QLIST_FOREACH_SAFE(ioh, &io_handlers, next, pioh) {
@ -46,8 +46,8 @@ ioh->fd_write invokes the loop again, some kind of counting is needed:
}
walking_handlers--;
One may think of using the RCU primitives, rcu_read_lock() and
rcu_read_unlock(); effectively, the RCU nesting count would take
One may think of using the RCU primitives, ``rcu_read_lock()`` and
``rcu_read_unlock()``; effectively, the RCU nesting count would take
the place of the walking_handlers global variable. Indeed,
reference counting and RCU have similar purposes, but their usage in
general is complementary:
@ -70,14 +70,14 @@ general is complementary:
this can improve performance, but also delay reclamation undesirably.
With reference counting, reclamation is deterministic.
This file documents QemuLockCnt, an abstraction for using reference
This file documents ``QemuLockCnt``, an abstraction for using reference
counting in code that has to be both thread-safe and reentrant.
QemuLockCnt concepts
--------------------
``QemuLockCnt`` concepts
------------------------
A QemuLockCnt comprises both a counter and a mutex; it has primitives
A ``QemuLockCnt`` comprises both a counter and a mutex; it has primitives
to increment and decrement the counter, and to take and release the
mutex. The counter notes how many visits to the data structures are
taking place (the visits could be from different threads, or there could
@ -95,13 +95,14 @@ not just frees, though there could be cases where this is not necessary.
Reads, instead, can be done without taking the mutex, as long as the
readers and writers use the same macros that are used for RCU, for
example qatomic_rcu_read, qatomic_rcu_set, QLIST_FOREACH_RCU, etc. This is
because the reads are done outside a lock and a set or QLIST_INSERT_HEAD
example ``qatomic_rcu_read``, ``qatomic_rcu_set``, ``QLIST_FOREACH_RCU``,
etc. This is because the reads are done outside a lock and a set
or ``QLIST_INSERT_HEAD``
can happen concurrently with the read. The RCU API ensures that the
processor and the compiler see all required memory barriers.
This could be implemented simply by protecting the counter with the
mutex, for example:
mutex, for example::
// (1)
qemu_mutex_lock(&walking_handlers_mutex);
@ -125,33 +126,33 @@ mutex, for example:
Here, no frees can happen in the code represented by the ellipsis.
If another thread is executing critical section (2), that part of
the code cannot be entered, because the thread will not be able
to increment the walking_handlers variable. And of course
to increment the ``walking_handlers`` variable. And of course
during the visit any other thread will see a nonzero value for
walking_handlers, as in the single-threaded code.
``walking_handlers``, as in the single-threaded code.
Note that it is possible for multiple concurrent accesses to delay
the cleanup arbitrarily; in other words, for the walking_handlers
the cleanup arbitrarily; in other words, for the ``walking_handlers``
counter to never become zero. For this reason, this technique is
more easily applicable if concurrent access to the structure is rare.
However, critical sections are easy to forget since you have to do
them for each modification of the counter. QemuLockCnt ensures that
them for each modification of the counter. ``QemuLockCnt`` ensures that
all modifications of the counter take the lock appropriately, and it
can also be more efficient in two ways:
- it avoids taking the lock for many operations (for example
incrementing the counter while it is non-zero);
- on some platforms, one can implement QemuLockCnt to hold the lock
- on some platforms, one can implement ``QemuLockCnt`` to hold the lock
and the mutex in a single word, making the fast path no more expensive
than simply managing a counter using atomic operations (see
docs/devel/atomics.rst). This can be very helpful if concurrent access to
:doc:`atomics`). This can be very helpful if concurrent access to
the data structure is expected to be rare.
Using the same mutex for frees and writes can still incur some small
inefficiencies; for example, a visit can never start if the counter is
zero and the mutex is taken---even if the mutex is taken by a write,
zero and the mutex is taken -- even if the mutex is taken by a write,
which in principle need not block a visit of the data structure.
However, these are usually not a problem if any of the following
assumptions are valid:
@ -163,27 +164,27 @@ assumptions are valid:
- writes are frequent, but this kind of write (e.g. appending to a
list) has a very small critical section.
For example, QEMU uses QemuLockCnt to manage an AioContext's list of
For example, QEMU uses ``QemuLockCnt`` to manage an ``AioContext``'s list of
bottom halves and file descriptor handlers. Modifications to the list
of file descriptor handlers are rare. Creation of a new bottom half is
frequent and can happen on a fast path; however: 1) it is almost never
concurrent with a visit to the list of bottom halves; 2) it only has
three instructions in the critical path, two assignments and a smp_wmb().
three instructions in the critical path, two assignments and a ``smp_wmb()``.
QemuLockCnt API
---------------
``QemuLockCnt`` API
-------------------
The QemuLockCnt API is described in include/qemu/thread.h.
.. kernel-doc:: include/qemu/lockcnt.h
QemuLockCnt usage
-----------------
``QemuLockCnt`` usage
---------------------
This section explains the typical usage patterns for QemuLockCnt functions.
This section explains the typical usage patterns for ``QemuLockCnt`` functions.
Setting a variable to a non-NULL value can be done between
qemu_lockcnt_lock and qemu_lockcnt_unlock:
``qemu_lockcnt_lock`` and ``qemu_lockcnt_unlock``::
qemu_lockcnt_lock(&xyz_lockcnt);
if (!xyz) {
@ -193,8 +194,8 @@ qemu_lockcnt_lock and qemu_lockcnt_unlock:
}
qemu_lockcnt_unlock(&xyz_lockcnt);
Accessing the value can be done between qemu_lockcnt_inc and
qemu_lockcnt_dec:
Accessing the value can be done between ``qemu_lockcnt_inc`` and
``qemu_lockcnt_dec``::
qemu_lockcnt_inc(&xyz_lockcnt);
if (xyz) {
@ -204,11 +205,11 @@ qemu_lockcnt_dec:
}
qemu_lockcnt_dec(&xyz_lockcnt);
Freeing the object can similarly use qemu_lockcnt_lock and
qemu_lockcnt_unlock, but you also need to ensure that the count
is zero (i.e. there is no concurrent visit). Because qemu_lockcnt_inc
takes the QemuLockCnt's lock, the count cannot become non-zero while
the object is being freed. Freeing an object looks like this:
Freeing the object can similarly use ``qemu_lockcnt_lock`` and
``qemu_lockcnt_unlock``, but you also need to ensure that the count
is zero (i.e. there is no concurrent visit). Because ``qemu_lockcnt_inc``
takes the ``QemuLockCnt``'s lock, the count cannot become non-zero while
the object is being freed. Freeing an object looks like this::
qemu_lockcnt_lock(&xyz_lockcnt);
if (!qemu_lockcnt_count(&xyz_lockcnt)) {
@ -218,7 +219,7 @@ the object is being freed. Freeing an object looks like this:
qemu_lockcnt_unlock(&xyz_lockcnt);
If an object has to be freed right after a visit, you can combine
the decrement, the locking and the check on count as follows:
the decrement, the locking and the check on count as follows::
qemu_lockcnt_inc(&xyz_lockcnt);
if (xyz) {
@ -232,7 +233,7 @@ the decrement, the locking and the check on count as follows:
qemu_lockcnt_unlock(&xyz_lockcnt);
}
QemuLockCnt can also be used to access a list as follows:
``QemuLockCnt`` can also be used to access a list as follows::
qemu_lockcnt_inc(&io_handlers_lockcnt);
QLIST_FOREACH_RCU(ioh, &io_handlers, pioh) {
@ -252,10 +253,10 @@ QemuLockCnt can also be used to access a list as follows:
}
Again, the RCU primitives are used because new items can be added to the
list during the walk. QLIST_FOREACH_RCU ensures that the processor and
list during the walk. ``QLIST_FOREACH_RCU`` ensures that the processor and
the compiler see the appropriate memory barriers.
An alternative pattern uses qemu_lockcnt_dec_if_lock:
An alternative pattern uses ``qemu_lockcnt_dec_if_lock``::
qemu_lockcnt_inc(&io_handlers_lockcnt);
QLIST_FOREACH_SAFE_RCU(ioh, &io_handlers, next, pioh) {
@ -273,5 +274,5 @@ An alternative pattern uses qemu_lockcnt_dec_if_lock:
}
qemu_lockcnt_dec(&io_handlers_lockcnt);
Here you can use qemu_lockcnt_dec instead of qemu_lockcnt_dec_and_lock,
Here you can use ``qemu_lockcnt_dec`` instead of ``qemu_lockcnt_dec_and_lock``,
because there is no special task to do if the count goes from 1 to 0.

View File

@ -0,0 +1,139 @@
Using Multiple ``IOThread``\ s
==============================
..
Copyright (c) 2014-2017 Red Hat Inc.
This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory.
This document explains the ``IOThread`` feature and how to write code that runs
outside the BQL.
The main loop and ``IOThread``\ s
---------------------------------
QEMU is an event-driven program that can do several things at once using an
event loop. The VNC server and the QMP monitor are both processed from the
same event loop, which monitors their file descriptors until they become
readable and then invokes a callback.
The default event loop is called the main loop (see ``main-loop.c``). It is
possible to create additional event loop threads using
``-object iothread,id=my-iothread``.
Side note: The main loop and ``IOThread`` are both event loops but their code is
not shared completely. Sometimes it is useful to remember that although they
are conceptually similar they are currently not interchangeable.
Why ``IOThread``\ s are useful
------------------------------
``IOThread``\ s allow the user to control the placement of work. The main loop is a
scalability bottleneck on hosts with many CPUs. Work can be spread across
several ``IOThread``\ s instead of just one main loop. When set up correctly this
can improve I/O latency and reduce jitter seen by the guest.
The main loop is also deeply associated with the BQL, which is a
scalability bottleneck in itself. vCPU threads and the main loop use the BQL
to serialize execution of QEMU code. This mutex is necessary because a lot of
QEMU's code historically was not thread-safe.
The fact that all I/O processing is done in a single main loop and that the
BQL is contended by all vCPU threads and the main loop explain
why it is desirable to place work into ``IOThread``\ s.
The experimental ``virtio-blk`` data-plane implementation has been benchmarked and
shows these effects:
ftp://public.dhe.ibm.com/linux/pdfs/KVM_Virtualized_IO_Performance_Paper.pdf
.. _how-to-program:
How to program for ``IOThread``\ s
----------------------------------
The main difference between legacy code and new code that can run in an
``IOThread`` is dealing explicitly with the event loop object, ``AioContext``
(see ``include/block/aio.h``). Code that only works in the main loop
implicitly uses the main loop's ``AioContext``. Code that supports running
in ``IOThread``\ s must be aware of its ``AioContext``.
AioContext supports the following services:
* File descriptor monitoring (read/write/error on POSIX hosts)
* Event notifiers (inter-thread signalling)
* Timers
* Bottom Halves (BH) deferred callbacks
There are several old APIs that use the main loop AioContext:
* LEGACY ``qemu_aio_set_fd_handler()`` - monitor a file descriptor
* LEGACY ``qemu_aio_set_event_notifier()`` - monitor an event notifier
* LEGACY ``timer_new_ms()`` - create a timer
* LEGACY ``qemu_bh_new()`` - create a BH
* LEGACY ``qemu_bh_new_guarded()`` - create a BH with a device re-entrancy guard
* LEGACY ``qemu_aio_wait()`` - run an event loop iteration
Since they implicitly work on the main loop they cannot be used in code that
runs in an ``IOThread``. They might cause a crash or deadlock if called from an
``IOThread`` since the BQL is not held.
Instead, use the ``AioContext`` functions directly (see ``include/block/aio.h``):
* ``aio_set_fd_handler()`` - monitor a file descriptor
* ``aio_set_event_notifier()`` - monitor an event notifier
* ``aio_timer_new()`` - create a timer
* ``aio_bh_new()`` - create a BH
* ``aio_bh_new_guarded()`` - create a BH with a device re-entrancy guard
* ``aio_poll()`` - run an event loop iteration
The ``qemu_bh_new_guarded``/``aio_bh_new_guarded`` APIs accept a
``MemReentrancyGuard``
argument, which is used to check for and prevent re-entrancy problems. For
BHs associated with devices, the reentrancy-guard is contained in the
corresponding ``DeviceState`` and named ``mem_reentrancy_guard``.
The ``AioContext`` can be obtained from the ``IOThread`` using
``iothread_get_aio_context()`` or for the main loop using
``qemu_get_aio_context()``. Code that takes an ``AioContext`` argument
works both in ``IOThread``\ s or the main loop, depending on which ``AioContext``
instance the caller passes in.
How to synchronize with an ``IOThread``
---------------------------------------
Variables that can be accessed by multiple threads require some form of
synchronization such as ``qemu_mutex_lock()``, ``rcu_read_lock()``, etc.
``AioContext`` functions like ``aio_set_fd_handler()``,
``aio_set_event_notifier()``, ``aio_bh_new()``, and ``aio_timer_new()``
are thread-safe. They can be used to trigger activity in an ``IOThread``.
Side note: the best way to schedule a function call across threads is to call
``aio_bh_schedule_oneshot()``.
The main loop thread can wait synchronously for a condition using
``AIO_WAIT_WHILE()``.
``AioContext`` and the block layer
----------------------------------
The ``AioContext`` originates from the QEMU block layer, even though nowadays
``AioContext`` is a generic event loop that can be used by any QEMU subsystem.
The block layer has support for ``AioContext`` integrated. Each
``BlockDriverState`` is associated with an ``AioContext`` using
``bdrv_try_change_aio_context()`` and ``bdrv_get_aio_context()``.
This allows block layer code to process I/O inside the
right ``AioContext``. Other subsystems may wish to follow a similar approach.
Block layer code must therefore expect to run in an ``IOThread`` and avoid using
old APIs that implicitly use the main loop. See
`How to program for IOThreads`_ for information on how to do that.
Code running in the monitor typically needs to ensure that past
requests from the guest are completed. When a block device is running
in an ``IOThread``, the ``IOThread`` can also process requests from the guest
(via ioeventfd). To achieve both objects, wrap the code between
``bdrv_drained_begin()`` and ``bdrv_drained_end()``, thus creating a "drained
section".
Long-running jobs (usually in the form of coroutines) are often scheduled in
the ``BlockDriverState``'s ``AioContext``. The functions
``bdrv_add``/``remove_aio_context_notifier``, or alternatively
``blk_add``/``remove_aio_context_notifier`` if you use ``BlockBackends``,
can be used to get a notification whenever ``bdrv_try_change_aio_context()``
moves a ``BlockDriverState`` to a different ``AioContext``.

View File

@ -1,130 +0,0 @@
Copyright (c) 2014-2017 Red Hat Inc.
This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory.
This document explains the IOThread feature and how to write code that runs
outside the BQL.
The main loop and IOThreads
---------------------------
QEMU is an event-driven program that can do several things at once using an
event loop. The VNC server and the QMP monitor are both processed from the
same event loop, which monitors their file descriptors until they become
readable and then invokes a callback.
The default event loop is called the main loop (see main-loop.c). It is
possible to create additional event loop threads using -object
iothread,id=my-iothread.
Side note: The main loop and IOThread are both event loops but their code is
not shared completely. Sometimes it is useful to remember that although they
are conceptually similar they are currently not interchangeable.
Why IOThreads are useful
------------------------
IOThreads allow the user to control the placement of work. The main loop is a
scalability bottleneck on hosts with many CPUs. Work can be spread across
several IOThreads instead of just one main loop. When set up correctly this
can improve I/O latency and reduce jitter seen by the guest.
The main loop is also deeply associated with the BQL, which is a
scalability bottleneck in itself. vCPU threads and the main loop use the BQL
to serialize execution of QEMU code. This mutex is necessary because a lot of
QEMU's code historically was not thread-safe.
The fact that all I/O processing is done in a single main loop and that the
BQL is contended by all vCPU threads and the main loop explain
why it is desirable to place work into IOThreads.
The experimental virtio-blk data-plane implementation has been benchmarked and
shows these effects:
ftp://public.dhe.ibm.com/linux/pdfs/KVM_Virtualized_IO_Performance_Paper.pdf
How to program for IOThreads
----------------------------
The main difference between legacy code and new code that can run in an
IOThread is dealing explicitly with the event loop object, AioContext
(see include/block/aio.h). Code that only works in the main loop
implicitly uses the main loop's AioContext. Code that supports running
in IOThreads must be aware of its AioContext.
AioContext supports the following services:
* File descriptor monitoring (read/write/error on POSIX hosts)
* Event notifiers (inter-thread signalling)
* Timers
* Bottom Halves (BH) deferred callbacks
There are several old APIs that use the main loop AioContext:
* LEGACY qemu_aio_set_fd_handler() - monitor a file descriptor
* LEGACY qemu_aio_set_event_notifier() - monitor an event notifier
* LEGACY timer_new_ms() - create a timer
* LEGACY qemu_bh_new() - create a BH
* LEGACY qemu_bh_new_guarded() - create a BH with a device re-entrancy guard
* LEGACY qemu_aio_wait() - run an event loop iteration
Since they implicitly work on the main loop they cannot be used in code that
runs in an IOThread. They might cause a crash or deadlock if called from an
IOThread since the BQL is not held.
Instead, use the AioContext functions directly (see include/block/aio.h):
* aio_set_fd_handler() - monitor a file descriptor
* aio_set_event_notifier() - monitor an event notifier
* aio_timer_new() - create a timer
* aio_bh_new() - create a BH
* aio_bh_new_guarded() - create a BH with a device re-entrancy guard
* aio_poll() - run an event loop iteration
The qemu_bh_new_guarded/aio_bh_new_guarded APIs accept a "MemReentrancyGuard"
argument, which is used to check for and prevent re-entrancy problems. For
BHs associated with devices, the reentrancy-guard is contained in the
corresponding DeviceState and named "mem_reentrancy_guard".
The AioContext can be obtained from the IOThread using
iothread_get_aio_context() or for the main loop using qemu_get_aio_context().
Code that takes an AioContext argument works both in IOThreads or the main
loop, depending on which AioContext instance the caller passes in.
How to synchronize with an IOThread
-----------------------------------
Variables that can be accessed by multiple threads require some form of
synchronization such as qemu_mutex_lock(), rcu_read_lock(), etc.
AioContext functions like aio_set_fd_handler(), aio_set_event_notifier(),
aio_bh_new(), and aio_timer_new() are thread-safe. They can be used to trigger
activity in an IOThread.
Side note: the best way to schedule a function call across threads is to call
aio_bh_schedule_oneshot().
The main loop thread can wait synchronously for a condition using
AIO_WAIT_WHILE().
AioContext and the block layer
------------------------------
The AioContext originates from the QEMU block layer, even though nowadays
AioContext is a generic event loop that can be used by any QEMU subsystem.
The block layer has support for AioContext integrated. Each BlockDriverState
is associated with an AioContext using bdrv_try_change_aio_context() and
bdrv_get_aio_context(). This allows block layer code to process I/O inside the
right AioContext. Other subsystems may wish to follow a similar approach.
Block layer code must therefore expect to run in an IOThread and avoid using
old APIs that implicitly use the main loop. See the "How to program for
IOThreads" above for information on how to do that.
Code running in the monitor typically needs to ensure that past
requests from the guest are completed. When a block device is running
in an IOThread, the IOThread can also process requests from the guest
(via ioeventfd). To achieve both objects, wrap the code between
bdrv_drained_begin() and bdrv_drained_end(), thus creating a "drained
section".
Long-running jobs (usually in the form of coroutines) are often scheduled in
the BlockDriverState's AioContext. The functions
bdrv_add/remove_aio_context_notifier, or alternatively
blk_add/remove_aio_context_notifier if you use BlockBackends, can be used to
get a notification whenever bdrv_try_change_aio_context() moves a
BlockDriverState to a different AioContext.

View File

@ -20,7 +20,7 @@ for the execution of all *currently running* critical sections before
proceeding, or before asynchronously executing a callback.
The key point here is that only the currently running critical sections
are waited for; critical sections that are started _after_ the beginning
are waited for; critical sections that are started **after** the beginning
of the wait do not extend the wait, despite running concurrently with
the updater. This is the reason why RCU is more scalable than,
for example, reader-writer locks. It is so much more scalable that
@ -37,7 +37,7 @@ do not matter; as soon as all previous critical sections have finished,
there cannot be any readers who hold references to the data structure,
and these can now be safely reclaimed (e.g., freed or unref'ed).
Here is a picture:
Here is a picture::
thread 1 thread 2 thread 3
------------------- ------------------------ -------------------
@ -58,43 +58,38 @@ that critical section.
RCU API
=======
-------
The core RCU API is small:
void rcu_read_lock(void);
``void rcu_read_lock(void);``
Used by a reader to inform the reclaimer that the reader is
entering an RCU read-side critical section.
void rcu_read_unlock(void);
``void rcu_read_unlock(void);``
Used by a reader to inform the reclaimer that the reader is
exiting an RCU read-side critical section. Note that RCU
read-side critical sections may be nested and/or overlapping.
void synchronize_rcu(void);
``void synchronize_rcu(void);``
Blocks until all pre-existing RCU read-side critical sections
on all threads have completed. This marks the end of the removal
phase and the beginning of reclamation phase.
Note that it would be valid for another update to come while
synchronize_rcu is running. Because of this, it is better that
``synchronize_rcu`` is running. Because of this, it is better that
the updater releases any locks it may hold before calling
synchronize_rcu. If this is not possible (for example, because
the updater is protected by the BQL), you can use call_rcu.
``synchronize_rcu``. If this is not possible (for example, because
the updater is protected by the BQL), you can use ``call_rcu``.
void call_rcu1(struct rcu_head * head,
void (*func)(struct rcu_head *head));
This function invokes func(head) after all pre-existing RCU
``void call_rcu1(struct rcu_head * head, void (*func)(struct rcu_head *head));``
This function invokes ``func(head)`` after all pre-existing RCU
read-side critical sections on all threads have completed. This
marks the end of the removal phase, with func taking care
asynchronously of the reclamation phase.
The foo struct needs to have an rcu_head structure added,
perhaps as follows:
The ``foo`` struct needs to have an ``rcu_head`` structure added,
perhaps as follows::
struct foo {
struct rcu_head rcu;
@ -103,8 +98,8 @@ The core RCU API is small:
long c;
};
so that the reclaimer function can fetch the struct foo address
and free it:
so that the reclaimer function can fetch the ``struct foo`` address
and free it::
call_rcu1(&foo.rcu, foo_reclaim);
@ -114,29 +109,27 @@ The core RCU API is small:
g_free(fp);
}
For the common case where the rcu_head member is the first of the
struct, you can use the following macro.
``call_rcu1`` is typically used via either the ``call_rcu`` or
``g_free_rcu`` macros, which handle the common case where the
``rcu_head`` member is the first of the struct.
void call_rcu(T *p,
void (*func)(T *p),
field-name);
void g_free_rcu(T *p,
field-name);
``void call_rcu(T *p, void (*func)(T *p), field-name);``
If the ``struct rcu_head`` is the first field in the struct, you can
use this macro instead of ``call_rcu1``.
call_rcu1 is typically used through these macro, in the common case
where the "struct rcu_head" is the first field in the struct. If
the callback function is g_free, in particular, g_free_rcu can be
used. In the above case, one could have written simply:
``void g_free_rcu(T *p, field-name);``
This is a special-case version of ``call_rcu`` where the callback
function is ``g_free``.
In the example given in ``call_rcu1``, one could have written simply::
g_free_rcu(&foo, rcu);
typeof(*p) qatomic_rcu_read(p);
``typeof(*p) qatomic_rcu_read(p);``
``qatomic_rcu_read()`` is similar to ``qatomic_load_acquire()``, but
it makes some assumptions on the code that calls it. This allows a
more optimized implementation.
qatomic_rcu_read() is similar to qatomic_load_acquire(), but it makes
some assumptions on the code that calls it. This allows a more
optimized implementation.
qatomic_rcu_read assumes that whenever a single RCU critical
``qatomic_rcu_read`` assumes that whenever a single RCU critical
section reads multiple shared data, these reads are either
data-dependent or need no ordering. This is almost always the
case when using RCU, because read-side critical sections typically
@ -144,7 +137,7 @@ The core RCU API is small:
every update) until reaching a data structure of interest,
and then read from there.
RCU read-side critical sections must use qatomic_rcu_read() to
RCU read-side critical sections must use ``qatomic_rcu_read()`` to
read data, unless concurrent writes are prevented by another
synchronization mechanism.
@ -152,18 +145,17 @@ The core RCU API is small:
data structure in a single direction, opposite to the direction
in which the updater initializes it.
void qatomic_rcu_set(p, typeof(*p) v);
``void qatomic_rcu_set(p, typeof(*p) v);``
``qatomic_rcu_set()`` is similar to ``qatomic_store_release()``,
though it also makes assumptions on the code that calls it in
order to allow a more optimized implementation.
qatomic_rcu_set() is similar to qatomic_store_release(), though it also
makes assumptions on the code that calls it in order to allow a more
optimized implementation.
In particular, qatomic_rcu_set() suffices for synchronization
In particular, ``qatomic_rcu_set()`` suffices for synchronization
with readers, if the updater never mutates a field within a
data item that is already accessible to readers. This is the
case when initializing a new copy of the RCU-protected data
structure; just ensure that initialization of *p is carried out
before qatomic_rcu_set() makes the data item visible to readers.
structure; just ensure that initialization of ``*p`` is carried out
before ``qatomic_rcu_set()`` makes the data item visible to readers.
If this rule is observed, writes will happen in the opposite
order as reads in the RCU read-side critical sections (or if
there is just one update), and there will be no need for other
@ -171,58 +163,54 @@ The core RCU API is small:
The following APIs must be used before RCU is used in a thread:
void rcu_register_thread(void);
``void rcu_register_thread(void);``
Mark a thread as taking part in the RCU mechanism. Such a thread
will have to report quiescent points regularly, either manually
or through the QemuCond/QemuSemaphore/QemuEvent APIs.
void rcu_unregister_thread(void);
or through the ``QemuCond``/``QemuSemaphore``/``QemuEvent`` APIs.
``void rcu_unregister_thread(void);``
Mark a thread as not taking part anymore in the RCU mechanism.
It is not a problem if such a thread reports quiescent points,
either manually or by using the QemuCond/QemuSemaphore/QemuEvent
APIs.
either manually or by using the
``QemuCond``/``QemuSemaphore``/``QemuEvent`` APIs.
Note that these APIs are relatively heavyweight, and should _not_ be
Note that these APIs are relatively heavyweight, and should **not** be
nested.
Convenience macros
==================
------------------
Two macros are provided that automatically release the read lock at the
end of the scope.
RCU_READ_LOCK_GUARD()
``RCU_READ_LOCK_GUARD()``
Takes the lock and will release it at the end of the block it's
used in.
WITH_RCU_READ_LOCK_GUARD() { code }
``WITH_RCU_READ_LOCK_GUARD() { code }``
Is used at the head of a block to protect the code within the block.
Note that 'goto'ing out of the guarded block will also drop the lock.
Note that a ``goto`` out of the guarded block will also drop the lock.
DIFFERENCES WITH LINUX
======================
Differences with Linux
----------------------
- Waiting on a mutex is possible, though discouraged, within an RCU critical
section. This is because spinlocks are rarely (if ever) used in userspace
programming; not allowing this would prevent upgrading an RCU read-side
critical section to become an updater.
- qatomic_rcu_read and qatomic_rcu_set replace rcu_dereference and
rcu_assign_pointer. They take a _pointer_ to the variable being accessed.
- ``qatomic_rcu_read`` and ``qatomic_rcu_set`` replace ``rcu_dereference`` and
``rcu_assign_pointer``. They take a **pointer** to the variable being accessed.
- call_rcu is a macro that has an extra argument (the name of the first
field in the struct, which must be a struct rcu_head), and expects the
- ``call_rcu`` is a macro that has an extra argument (the name of the first
field in the struct, which must be a struct ``rcu_head``), and expects the
type of the callback's argument to be the type of the first argument.
call_rcu1 is the same as Linux's call_rcu.
``call_rcu1`` is the same as Linux's ``call_rcu``.
RCU PATTERNS
============
RCU Patterns
------------
Many patterns using read-writer locks translate directly to RCU, with
the advantages of higher scalability and deadlock immunity.
@ -243,28 +231,28 @@ Here are some frequently-used RCU idioms that are worth noting.
RCU list processing
-------------------
^^^^^^^^^^^^^^^^^^^
TBD (not yet used in QEMU)
RCU reference counting
----------------------
^^^^^^^^^^^^^^^^^^^^^^
Because grace periods are not allowed to complete while there is an RCU
read-side critical section in progress, the RCU read-side primitives
may be used as a restricted reference-counting mechanism. For example,
consider the following code fragment:
consider the following code fragment::
rcu_read_lock();
p = qatomic_rcu_read(&foo);
/* do something with p. */
rcu_read_unlock();
The RCU read-side critical section ensures that the value of "p" remains
valid until after the rcu_read_unlock(). In some sense, it is acquiring
a reference to p that is later released when the critical section ends.
The write side looks simply like this (with appropriate locking):
The RCU read-side critical section ensures that the value of ``p`` remains
valid until after the ``rcu_read_unlock()``. In some sense, it is acquiring
a reference to ``p`` that is later released when the critical section ends.
The write side looks simply like this (with appropriate locking)::
qemu_mutex_lock(&foo_mutex);
old = foo;
@ -274,7 +262,7 @@ The write side looks simply like this (with appropriate locking):
free(old);
If the processing cannot be done purely within the critical section, it
is possible to combine this idiom with a "real" reference count:
is possible to combine this idiom with a "real" reference count::
rcu_read_lock();
p = qatomic_rcu_read(&foo);
@ -283,7 +271,7 @@ is possible to combine this idiom with a "real" reference count:
/* do something with p. */
foo_unref(p);
The write side can be like this:
The write side can be like this::
qemu_mutex_lock(&foo_mutex);
old = foo;
@ -292,7 +280,7 @@ The write side can be like this:
synchronize_rcu();
foo_unref(old);
or with call_rcu:
or with ``call_rcu``::
qemu_mutex_lock(&foo_mutex);
old = foo;
@ -301,10 +289,10 @@ or with call_rcu:
call_rcu(foo_unref, old, rcu);
In both cases, the write side only performs removal. Reclamation
happens when the last reference to a "foo" object is dropped.
Using synchronize_rcu() is undesirably expensive, because the
happens when the last reference to a ``foo`` object is dropped.
Using ``synchronize_rcu()`` is undesirably expensive, because the
last reference may be dropped on the read side. Hence you can
use call_rcu() instead:
use ``call_rcu()`` instead::
foo_unref(struct foo *p) {
if (qatomic_fetch_dec(&p->refcount) == 1) {
@ -314,7 +302,7 @@ use call_rcu() instead:
Note that the same idioms would be possible with reader/writer
locks:
locks::
read_lock(&foo_rwlock); write_mutex_lock(&foo_rwlock);
p = foo; p = foo;
@ -334,15 +322,15 @@ locks:
foo_unref(p);
read_unlock(&foo_rwlock);
foo_unref could use a mechanism such as bottom halves to move deallocation
``foo_unref`` could use a mechanism such as bottom halves to move deallocation
out of the write-side critical section.
RCU resizable arrays
--------------------
^^^^^^^^^^^^^^^^^^^^
Resizable arrays can be used with RCU. The expensive RCU synchronization
(or call_rcu) only needs to take place when the array is resized.
(or ``call_rcu``) only needs to take place when the array is resized.
The two items to take care of are:
- ensuring that the old version of the array is available between removal
@ -351,10 +339,10 @@ The two items to take care of are:
- avoiding mismatches in the read side between the array data and the
array size.
The first problem is avoided simply by not using realloc. Instead,
The first problem is avoided simply by not using ``realloc``. Instead,
each resize will allocate a new array and copy the old data into it.
The second problem would arise if the size and the data pointers were
two members of a larger struct:
two members of a larger struct::
struct mystuff {
...
@ -364,7 +352,7 @@ two members of a larger struct:
...
};
Instead, we store the size of the array with the array itself:
Instead, we store the size of the array with the array itself::
struct arr {
int size;
@ -400,7 +388,7 @@ Instead, we store the size of the array with the array itself:
}
SOURCES
=======
References
----------
* Documentation/RCU/ from the Linux kernel
* The `Linux kernel RCU documentation <https://docs.kernel.org/RCU/>`__

View File

@ -0,0 +1,177 @@
Block I/O error injection using ``blkdebug``
============================================
..
Copyright (C) 2014-2015 Red Hat Inc
This work is licensed under the terms of the GNU GPL, version 2 or later. See
the COPYING file in the top-level directory.
The ``blkdebug`` block driver is a rule-based error injection engine. It can be
used to exercise error code paths in block drivers including ``ENOSPC`` (out of
space) and ``EIO``.
This document gives an overview of the features available in ``blkdebug``.
Background
----------
Block drivers have many error code paths that handle I/O errors. Image formats
are especially complex since metadata I/O errors during cluster allocation or
while updating tables happen halfway through request processing and require
discipline to keep image files consistent.
Error injection allows test cases to trigger I/O errors at specific points.
This way, all error paths can be tested to make sure they are correct.
Rules
-----
The ``blkdebug`` block driver takes a list of "rules" that tell the error injection
engine when to fail an I/O request.
Each I/O request is evaluated against the rules. If a rule matches the request
then its "action" is executed.
Rules can be placed in a configuration file; the configuration file
follows the same .ini-like format used by QEMU's ``-readconfig`` option, and
each section of the file represents a rule.
The following configuration file defines a single rule::
$ cat blkdebug.conf
[inject-error]
event = "read_aio"
errno = "28"
This rule fails all aio read requests with ``ENOSPC`` (28). Note that the errno
value depends on the host. On Linux, see
``/usr/include/asm-generic/errno-base.h`` for errno values.
Invoke QEMU as follows::
$ qemu-system-x86_64
-drive if=none,cache=none,file=blkdebug:blkdebug.conf:test.img,id=drive0 \
-device virtio-blk-pci,drive=drive0,id=virtio-blk-pci0
Rules support the following attributes:
``event``
which type of operation to match (e.g. ``read_aio``, ``write_aio``,
``flush_to_os``, ``flush_to_disk``). See `Events`_ for
information on events.
``state``
(optional) the engine must be in this state number in order for this
rule to match. See `State transitions`_ for information
on states.
``errno``
the numeric errno value to return when a request matches this rule.
The errno values depend on the host since the numeric values are not
standardized in the POSIX specification.
``sector``
(optional) a sector number that the request must overlap in order to
match this rule
``once``
(optional, default ``off``) only execute this action on the first
matching request
``immediately``
(optional, default ``off``) return a NULL ``BlockAIOCB``
pointer and fail without an errno instead. This
exercises the code path where ``BlockAIOCB`` fails and the
caller's ``BlockCompletionFunc`` is not invoked.
Events
------
Block drivers provide information about the type of I/O request they are about
to make so rules can match specific types of requests. For example, the ``qcow2``
block driver tells ``blkdebug`` when it accesses the L1 table so rules can match
only L1 table accesses and not other metadata or guest data requests.
The core events are:
``read_aio``
guest data read
``write_aio``
guest data write
``flush_to_os``
write out unwritten block driver state (e.g. cached metadata)
``flush_to_disk``
flush the host block device's disk cache
See ``qapi/block-core.json:BlkdebugEvent`` for the full list of events.
You may need to grep block driver source code to understand the
meaning of specific events.
State transitions
-----------------
There are cases where more power is needed to match a particular I/O request in
a longer sequence of requests. For example::
write_aio
flush_to_disk
write_aio
How do we match the 2nd ``write_aio`` but not the first? This is where state
transitions come in.
The error injection engine has an integer called the "state" that always starts
initialized to 1. The state integer is internal to ``blkdebug`` and cannot be
observed from outside but rules can interact with it for powerful matching
behavior.
Rules can be conditional on the current state and they can transition to a new
state.
When a rule's "state" attribute is non-zero then the current state must equal
the attribute in order for the rule to match.
For example, to match the 2nd write_aio::
[set-state]
event = "write_aio"
state = "1"
new_state = "2"
[inject-error]
event = "write_aio"
state = "2"
errno = "5"
The first ``write_aio`` request matches the ``set-state`` rule and transitions from
state 1 to state 2. Once state 2 has been entered, the ``set-state`` rule no
longer matches since it requires state 1. But the ``inject-error`` rule now
matches the next ``write_aio`` request and injects ``EIO`` (5).
State transition rules support the following attributes:
``event``
which type of operation to match (e.g. ``read_aio``, ``write_aio``,
``flush_to_os`, ``flush_to_disk``). See `Events`_ for
information on events.
``state``
(optional) the engine must be in this state number in order for this
rule to match
``new_state``
transition to this state number
Suspend and resume
------------------
Exercising code paths in block drivers may require specific ordering amongst
concurrent requests. The "breakpoint" feature allows requests to be halted on
a ``blkdebug`` event and resumed later. This makes it possible to achieve
deterministic ordering when multiple requests are in flight.
Breakpoints on ``blkdebug`` events are associated with a user-defined ``tag`` string.
This tag serves as an identifier by which the request can be resumed at a later
point.
See the ``qemu-io(1)`` ``break``, ``resume``, ``remove_break``, and ``wait_break``
commands for details.

View File

@ -1,8 +1,10 @@
= Block driver correctness testing with blkverify =
Block driver correctness testing with ``blkverify``
===================================================
== Introduction ==
Introduction
------------
This document describes how to use the blkverify protocol to test that a block
This document describes how to use the ``blkverify`` protocol to test that a block
driver is operating correctly.
It is difficult to test and debug block drivers against real guests. Often
@ -11,12 +13,13 @@ of the executable. Other times obscure errors are raised by a program inside
the guest. These issues are extremely hard to trace back to bugs in the block
driver.
Blkverify solves this problem by catching data corruption inside QEMU the first
``blkverify`` solves this problem by catching data corruption inside QEMU the first
time bad data is read and reporting the disk sector that is corrupted.
== How it works ==
How it works
------------
The blkverify protocol has two child block devices, the "test" device and the
The ``blkverify`` protocol has two child block devices, the "test" device and the
"raw" device. Read/write operations are mirrored to both devices so their
state should always be in sync.
@ -25,13 +28,14 @@ contents to the "test" image. The idea is that the "raw" device will handle
read/write operations correctly and not corrupt data. It can be used as a
reference for comparison against the "test" device.
After a mirrored read operation completes, blkverify will compare the data and
After a mirrored read operation completes, ``blkverify`` will compare the data and
raise an error if it is not identical. This makes it possible to catch the
first instance where corrupt data is read.
== Example ==
Example
-------
Imagine raw.img has 0xcd repeated throughout its first sector:
Imagine raw.img has 0xcd repeated throughout its first sector::
$ ./qemu-io -c 'read -v 0 512' raw.img
00000000: cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd cd ................
@ -42,7 +46,7 @@ Imagine raw.img has 0xcd repeated throughout its first sector:
read 512/512 bytes at offset 0
512.000000 bytes, 1 ops; 0.0000 sec (97.656 MiB/sec and 200000.0000 ops/sec)
And test.img is corrupt, its first sector is zeroed when it shouldn't be:
And test.img is corrupt, its first sector is zeroed when it shouldn't be::
$ ./qemu-io -c 'read -v 0 512' test.img
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
@ -53,17 +57,17 @@ And test.img is corrupt, its first sector is zeroed when it shouldn't be:
read 512/512 bytes at offset 0
512.000000 bytes, 1 ops; 0.0000 sec (81.380 MiB/sec and 166666.6667 ops/sec)
This error is caught by blkverify:
This error is caught by ``blkverify``::
$ ./qemu-io -c 'read 0 512' blkverify:a.img:b.img
blkverify: read sector_num=0 nb_sectors=4 contents mismatch in sector 0
A more realistic scenario is verifying the installation of a guest OS:
A more realistic scenario is verifying the installation of a guest OS::
$ ./qemu-img create raw.img 16G
$ ./qemu-img create -f qcow2 test.qcow2 16G
$ ./qemu-system-x86_64 -cdrom debian.iso \
-drive file=blkverify:raw.img:test.qcow2
If the installation is aborted when blkverify detects corruption, use qemu-io
If the installation is aborted when ``blkverify`` detects corruption, use ``qemu-io``
to explore the contents of the disk image at the sector in question.

View File

@ -14,3 +14,5 @@ testing infrastructure.
acpi-bits
ci
fuzzing
blkdebug
blkverify

View File

@ -15,4 +15,5 @@ Emulated devices:
- USB controller
- SATA controller
- TWI (I2C) controller
- SPI controller
- Watchdog timer

View File

@ -36,6 +36,7 @@ Supported devices
* SPI controller
* System configuration (SYSCFG)
* Timer controller (TIMER)
* Reset and Clock Controller (RCC) (STM32F4 only, reset and enable only)
Missing devices
---------------
@ -53,7 +54,7 @@ Missing devices
* Power supply configuration (PWR)
* Random Number Generator (RNG)
* Real-Time Clock (RTC) controller
* Reset and Clock Controller (RCC)
* Reset and Clock Controller (RCC) (other features than reset and enable)
* Secure Digital Input/Output (SDIO) interface
* USB OTG
* Watchdog controller (IWDG, WWDG)

View File

@ -27,7 +27,6 @@ source nvme/Kconfig
source nvram/Kconfig
source pci-bridge/Kconfig
source pci-host/Kconfig
source pcmcia/Kconfig
source pci/Kconfig
source remote/Kconfig
source rtc/Kconfig

View File

@ -1,5 +1,2 @@
config STM32F2XX_ADC
bool
config MAX111X
bool

View File

@ -1,236 +0,0 @@
/*
* Maxim MAX1110/1111 ADC chip emulation.
*
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
* This code is licensed under the GNU GPLv2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "hw/adc/max111x.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
#include "qemu/module.h"
#include "hw/qdev-properties.h"
/* Control-byte bitfields */
#define CB_PD0 (1 << 0)
#define CB_PD1 (1 << 1)
#define CB_SGL (1 << 2)
#define CB_UNI (1 << 3)
#define CB_SEL0 (1 << 4)
#define CB_SEL1 (1 << 5)
#define CB_SEL2 (1 << 6)
#define CB_START (1 << 7)
#define CHANNEL_NUM(v, b0, b1, b2) \
((((v) >> (2 + (b0))) & 4) | \
(((v) >> (3 + (b1))) & 2) | \
(((v) >> (4 + (b2))) & 1))
static uint32_t max111x_read(MAX111xState *s)
{
if (!s->tb1)
return 0;
switch (s->cycle ++) {
case 1:
return s->rb2;
case 2:
return s->rb3;
}
return 0;
}
/* Interpret a control-byte */
static void max111x_write(MAX111xState *s, uint32_t value)
{
int measure, chan;
/* Ignore the value if START bit is zero */
if (!(value & CB_START))
return;
s->cycle = 0;
if (!(value & CB_PD1)) {
s->tb1 = 0;
return;
}
s->tb1 = value;
if (s->inputs == 8)
chan = CHANNEL_NUM(value, 1, 0, 2);
else
chan = CHANNEL_NUM(value & ~CB_SEL0, 0, 1, 2);
if (value & CB_SGL)
measure = s->input[chan] - s->com;
else
measure = s->input[chan] - s->input[chan ^ 1];
if (!(value & CB_UNI))
measure ^= 0x80;
s->rb2 = (measure >> 2) & 0x3f;
s->rb3 = (measure << 6) & 0xc0;
/* FIXME: When should the IRQ be lowered? */
qemu_irq_raise(s->interrupt);
}
static uint32_t max111x_transfer(SSIPeripheral *dev, uint32_t value)
{
MAX111xState *s = MAX_111X(dev);
max111x_write(s, value);
return max111x_read(s);
}
static const VMStateDescription vmstate_max111x = {
.name = "max111x",
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_SSI_PERIPHERAL(parent_obj, MAX111xState),
VMSTATE_UINT8(tb1, MAX111xState),
VMSTATE_UINT8(rb2, MAX111xState),
VMSTATE_UINT8(rb3, MAX111xState),
VMSTATE_INT32_EQUAL(inputs, MAX111xState, NULL),
VMSTATE_INT32(com, MAX111xState),
VMSTATE_ARRAY_INT32_UNSAFE(input, MAX111xState, inputs,
vmstate_info_uint8, uint8_t),
VMSTATE_END_OF_LIST()
}
};
static void max111x_input_set(void *opaque, int line, int value)
{
MAX111xState *s = MAX_111X(opaque);
assert(line >= 0 && line < s->inputs);
s->input[line] = value;
}
static int max111x_init(SSIPeripheral *d, int inputs)
{
DeviceState *dev = DEVICE(d);
MAX111xState *s = MAX_111X(dev);
qdev_init_gpio_out(dev, &s->interrupt, 1);
qdev_init_gpio_in(dev, max111x_input_set, inputs);
s->inputs = inputs;
return 0;
}
static void max1110_realize(SSIPeripheral *dev, Error **errp)
{
max111x_init(dev, 8);
}
static void max1111_realize(SSIPeripheral *dev, Error **errp)
{
max111x_init(dev, 4);
}
static void max111x_reset(DeviceState *dev)
{
MAX111xState *s = MAX_111X(dev);
int i;
for (i = 0; i < s->inputs; i++) {
s->input[i] = s->reset_input[i];
}
s->com = 0;
s->tb1 = 0;
s->rb2 = 0;
s->rb3 = 0;
s->cycle = 0;
}
static Property max1110_properties[] = {
/* Reset values for ADC inputs */
DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0),
DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0),
DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0),
DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0),
DEFINE_PROP_END_OF_LIST(),
};
static Property max1111_properties[] = {
/* Reset values for ADC inputs */
DEFINE_PROP_UINT8("input0", MAX111xState, reset_input[0], 0xf0),
DEFINE_PROP_UINT8("input1", MAX111xState, reset_input[1], 0xe0),
DEFINE_PROP_UINT8("input2", MAX111xState, reset_input[2], 0xd0),
DEFINE_PROP_UINT8("input3", MAX111xState, reset_input[3], 0xc0),
DEFINE_PROP_UINT8("input4", MAX111xState, reset_input[4], 0xb0),
DEFINE_PROP_UINT8("input5", MAX111xState, reset_input[5], 0xa0),
DEFINE_PROP_UINT8("input6", MAX111xState, reset_input[6], 0x90),
DEFINE_PROP_UINT8("input7", MAX111xState, reset_input[7], 0x80),
DEFINE_PROP_END_OF_LIST(),
};
static void max111x_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->transfer = max111x_transfer;
device_class_set_legacy_reset(dc, max111x_reset);
dc->vmsd = &vmstate_max111x;
set_bit(DEVICE_CATEGORY_MISC, dc->categories);
}
static const TypeInfo max111x_info = {
.name = TYPE_MAX_111X,
.parent = TYPE_SSI_PERIPHERAL,
.instance_size = sizeof(MAX111xState),
.class_init = max111x_class_init,
.abstract = true,
};
static void max1110_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->realize = max1110_realize;
device_class_set_props(dc, max1110_properties);
}
static const TypeInfo max1110_info = {
.name = TYPE_MAX_1110,
.parent = TYPE_MAX_111X,
.class_init = max1110_class_init,
};
static void max1111_class_init(ObjectClass *klass, void *data)
{
SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
k->realize = max1111_realize;
device_class_set_props(dc, max1111_properties);
}
static const TypeInfo max1111_info = {
.name = TYPE_MAX_1111,
.parent = TYPE_MAX_111X,
.class_init = max1111_class_init,
};
static void max111x_register_types(void)
{
type_register_static(&max111x_info);
type_register_static(&max1110_info);
type_register_static(&max1111_info);
}
type_init(max111x_register_types)

View File

@ -2,4 +2,3 @@ system_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_adc.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
system_ss.add(when: 'CONFIG_ZYNQ', if_true: files('zynq-xadc.c'))
system_ss.add(when: 'CONFIG_MAX111X', if_true: files('max111x.c'))

View File

@ -150,7 +150,6 @@ config OMAP
bool
select FRAMEBUFFER
select I2C
select ECC
select NAND
select PFLASH_CFI01
select SD
@ -328,6 +327,7 @@ config ALLWINNER_A10
select ALLWINNER_WDT
select ALLWINNER_EMAC
select ALLWINNER_I2C
select ALLWINNER_A10_SPI
select AXP2XX_PMU
select SERIAL_MM
select UNIMP
@ -397,6 +397,7 @@ config STM32F405_SOC
bool
select ARM_V7M
select OR_IRQ
select STM32_RCC
select STM32F4XX_SYSCFG
select STM32F4XX_EXTI

View File

@ -35,6 +35,7 @@
#define AW_A10_PIC_REG_BASE 0x01c20400
#define AW_A10_PIT_REG_BASE 0x01c20c00
#define AW_A10_UART0_REG_BASE 0x01c28000
#define AW_A10_SPI0_BASE 0x01c05000
#define AW_A10_EMAC_BASE 0x01c0b000
#define AW_A10_EHCI_BASE 0x01c14000
#define AW_A10_OHCI_BASE 0x01c14400
@ -80,6 +81,8 @@ static void aw_a10_init(Object *obj)
object_initialize_child(obj, "i2c0", &s->i2c0, TYPE_AW_I2C);
object_initialize_child(obj, "spi0", &s->spi0, TYPE_AW_A10_SPI);
for (size_t i = 0; i < AW_A10_NUM_USB; i++) {
object_initialize_child(obj, "ehci[*]", &s->ehci[i],
TYPE_PLATFORM_EHCI);
@ -195,6 +198,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp)
sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c0), 0, AW_A10_I2C0_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c0), 0, qdev_get_gpio_in(dev, 7));
/* SPI */
sysbus_realize(SYS_BUS_DEVICE(&s->spi0), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0, AW_A10_SPI0_BASE);
sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0, qdev_get_gpio_in(dev, 10));
/* WDT */
sysbus_realize(SYS_BUS_DEVICE(&s->wdt), &error_fatal);
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->wdt), 0, AW_A10_WDT_BASE, 1);

View File

@ -2170,29 +2170,27 @@ struct omap_uwire_s {
uint16_t rxbuf;
uint16_t control;
uint16_t setup[5];
uWireSlave *chip[4];
};
static void omap_uwire_transfer_start(struct omap_uwire_s *s)
{
int chipselect = (s->control >> 10) & 3; /* INDEX */
uWireSlave *slave = s->chip[chipselect];
if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */
if (s->control & (1 << 12)) /* CS_CMD */
if (slave && slave->send)
slave->send(slave->opaque,
s->txbuf >> (16 - ((s->control >> 5) & 0x1f)));
if (s->control & (1 << 12)) { /* CS_CMD */
qemu_log_mask(LOG_UNIMP, "uWireSlave TX CS:%d data:0x%04x\n",
chipselect,
s->txbuf >> (16 - ((s->control >> 5) & 0x1f)));
}
s->control &= ~(1 << 14); /* CSRB */
/* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
* a DRQ. When is the level IRQ supposed to be reset? */
}
if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */
if (s->control & (1 << 12)) /* CS_CMD */
if (slave && slave->receive)
s->rxbuf = slave->receive(slave->opaque);
if (s->control & (1 << 12)) { /* CS_CMD */
qemu_log_mask(LOG_UNIMP, "uWireSlave RX CS:%d\n", chipselect);
}
s->control |= 1 << 15; /* RDRB */
/* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or
* a DRQ. When is the level IRQ supposed to be reset? */
@ -2321,17 +2319,6 @@ static struct omap_uwire_s *omap_uwire_init(MemoryRegion *system_memory,
return s;
}
void omap_uwire_attach(struct omap_uwire_s *s,
uWireSlave *slave, int chipselect)
{
if (chipselect < 0 || chipselect > 3) {
error_report("%s: Bad chipselect %i", __func__, chipselect);
exit(-1);
}
s->chip[chipselect] = slave;
}
/* Pseudonoise Pulse-Width Light Modulator */
struct omap_pwl_s {
MemoryRegion iomem;

View File

@ -30,6 +30,7 @@
#include "hw/qdev-clock.h"
#include "hw/misc/unimp.h"
#define RCC_ADDR 0x40023800
#define SYSCFG_ADD 0x40013800
static const uint32_t usart_addr[] = { 0x40011000, 0x40004400, 0x40004800,
0x40004C00, 0x40005000, 0x40011400,
@ -59,6 +60,8 @@ static void stm32f405_soc_initfn(Object *obj)
object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M);
object_initialize_child(obj, "rcc", &s->rcc, TYPE_STM32_RCC);
object_initialize_child(obj, "syscfg", &s->syscfg, TYPE_STM32F4XX_SYSCFG);
for (i = 0; i < STM_NUM_USARTS; i++) {
@ -160,6 +163,14 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
return;
}
/* Reset and clock controller */
dev = DEVICE(&s->rcc);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->rcc), errp)) {
return;
}
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, RCC_ADDR);
/* System configuration controller */
dev = DEVICE(&s->syscfg);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->syscfg), errp)) {
@ -276,7 +287,6 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp)
create_unimplemented_device("GPIOH", 0x40021C00, 0x400);
create_unimplemented_device("GPIOI", 0x40022000, 0x400);
create_unimplemented_device("CRC", 0x40023000, 0x400);
create_unimplemented_device("RCC", 0x40023800, 0x400);
create_unimplemented_device("Flash Int", 0x40023C00, 0x400);
create_unimplemented_device("BKPSRAM", 0x40024000, 0x400);
create_unimplemented_device("DMA1", 0x40026000, 0x400);

View File

@ -236,6 +236,8 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp)
/* System configuration controller */
busdev = SYS_BUS_DEVICE(&s->syscfg);
qdev_connect_clock_in(DEVICE(&s->syscfg), "clk",
qdev_get_clock_out(DEVICE(&(s->rcc)), "syscfg-out"));
if (!sysbus_realize(busdev, errp)) {
return;
}

View File

@ -34,6 +34,7 @@
#include "hw/net/cadence_gem.h"
#include "hw/cpu/a9mpcore.h"
#include "hw/qdev-clock.h"
#include "hw/misc/unimp.h"
#include "sysemu/reset.h"
#include "qom/object.h"
#include "exec/tswap.h"
@ -373,6 +374,75 @@ static void zynq_init(MachineState *machine)
sysbus_connect_irq(busdev, 0, pic[40 - IRQ_OFFSET]);
sysbus_mmio_map(busdev, 0, 0xF8007000);
/*
* Refer to the ug585-Zynq-7000-TRM manual B.3 (Module Summary) and
* the zynq-7000.dtsi. Add placeholders for unimplemented devices.
*/
create_unimplemented_device("zynq.i2c0", 0xE0004000, 4 * KiB);
create_unimplemented_device("zynq.i2c1", 0xE0005000, 4 * KiB);
create_unimplemented_device("zynq.can0", 0xE0008000, 4 * KiB);
create_unimplemented_device("zynq.can1", 0xE0009000, 4 * KiB);
create_unimplemented_device("zynq.gpio", 0xE000A000, 4 * KiB);
create_unimplemented_device("zynq.smcc", 0xE000E000, 4 * KiB);
/* Direct Memory Access Controller, PL330, Non-Secure Mode */
create_unimplemented_device("zynq.dma_ns", 0xF8004000, 4 * KiB);
/* System Watchdog Timer Registers */
create_unimplemented_device("zynq.swdt", 0xF8005000, 4 * KiB);
/* DDR memory controller */
create_unimplemented_device("zynq.ddrc", 0xF8006000, 4 * KiB);
/* AXI_HP Interface (AFI) */
create_unimplemented_device("zynq.axi_hp0", 0xF8008000, 0x28);
create_unimplemented_device("zynq.axi_hp1", 0xF8009000, 0x28);
create_unimplemented_device("zynq.axi_hp2", 0xF800A000, 0x28);
create_unimplemented_device("zynq.axi_hp3", 0xF800B000, 0x28);
create_unimplemented_device("zynq.efuse", 0xF800d000, 0x20);
/* Embedded Trace Buffer */
create_unimplemented_device("zynq.etb", 0xF8801000, 4 * KiB);
/* Cross Trigger Interface, ETB and TPIU */
create_unimplemented_device("zynq.cti_etb_tpiu", 0xF8802000, 4 * KiB);
/* Trace Port Interface Unit */
create_unimplemented_device("zynq.tpiu", 0xF8803000, 4 * KiB);
/* CoreSight Trace Funnel */
create_unimplemented_device("zynq.funnel", 0xF8804000, 4 * KiB);
/* Instrumentation Trace Macrocell */
create_unimplemented_device("zynq.itm", 0xF8805000, 4 * KiB);
/* Cross Trigger Interface, FTM */
create_unimplemented_device("zynq.cti_ftm", 0xF8809000, 4 * KiB);
/* Fabric Trace Macrocell */
create_unimplemented_device("zynq.ftm", 0xF880B000, 4 * KiB);
/* Cortex A9 Performance Monitoring Unit, CPU */
create_unimplemented_device("cortex-a9.pmu0", 0xF8891000, 4 * KiB);
create_unimplemented_device("cortex-a9.pmu1", 0xF8893000, 4 * KiB);
/* Cross Trigger Interface, CPU */
create_unimplemented_device("zynq.cpu_cti0", 0xF8898000, 4 * KiB);
create_unimplemented_device("zynq.cpu_cti1", 0xF8899000, 4 * KiB);
/* CoreSight PTM-A9, CPU */
create_unimplemented_device("cortex-a9.ptm0", 0xF889c000, 4 * KiB);
create_unimplemented_device("cortex-a9.ptm1", 0xF889d000, 4 * KiB);
/* AMBA NIC301 TrustZone */
create_unimplemented_device("zynq.trustZone", 0xF8900000, 0x20);
/* AMBA Network Interconnect Advanced Quality of Service (QoS-301) */
create_unimplemented_device("zynq.qos301_cpu", 0xF8946000, 0x130);
create_unimplemented_device("zynq.qos301_dmac", 0xF8947000, 0x130);
create_unimplemented_device("zynq.qos301_iou", 0xF8948000, 0x130);
zynq_binfo.ram_size = machine->ram_size;
zynq_binfo.board_id = 0xd32;
zynq_binfo.loader_start = 0;

View File

@ -22,9 +22,6 @@ config PFLASH_CFI01
config PFLASH_CFI02
bool
config ECC
bool
config VIRTIO_BLK
bool
default y

View File

@ -1,91 +0,0 @@
/*
* Calculate Error-correcting Codes. Used by NAND Flash controllers
* (not by NAND chips).
*
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
* This code is licensed under the GNU GPL v2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include "qemu/osdep.h"
#include "migration/vmstate.h"
#include "hw/block/flash.h"
/*
* Pre-calculated 256-way 1 byte column parity. Table borrowed from Linux.
*/
static const uint8_t nand_ecc_precalc_table[] = {
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30,
0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55,
0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56,
0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33,
0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59,
0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c,
0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f,
0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a,
0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
};
/* Update ECC parity count. */
uint8_t ecc_digest(ECCState *s, uint8_t sample)
{
uint8_t idx = nand_ecc_precalc_table[sample];
s->cp ^= idx & 0x3f;
if (idx & 0x40) {
s->lp[0] ^= ~s->count;
s->lp[1] ^= s->count;
}
s->count ++;
return sample;
}
/* Reinitialise the counters. */
void ecc_reset(ECCState *s)
{
s->lp[0] = 0x0000;
s->lp[1] = 0x0000;
s->cp = 0x00;
s->count = 0;
}
/* Save/restore */
const VMStateDescription vmstate_ecc_state = {
.name = "ecc-state",
.version_id = 0,
.minimum_version_id = 0,
.fields = (const VMStateField[]) {
VMSTATE_UINT8(cp, ECCState),
VMSTATE_UINT16_ARRAY(lp, ECCState, 2),
VMSTATE_UINT16(count, ECCState),
VMSTATE_END_OF_LIST(),
},
};

View File

@ -3,7 +3,6 @@ system_ss.add(files(
'cdrom.c',
'hd-geometry.c'
))
system_ss.add(when: 'CONFIG_ECC', if_true: files('ecc.c'))
system_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c'))
system_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c'))
system_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c'))

View File

@ -90,10 +90,10 @@ DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr)
#define CR_UARTEN (1 << 0)
/* Integer Baud Rate Divider, UARTIBRD */
#define IBRD_MASK 0x3f
#define IBRD_MASK 0xffff
/* Fractional Baud Rate Divider, UARTFBRD */
#define FBRD_MASK 0xffff
#define FBRD_MASK 0x3f
static const unsigned char pl011_id_arm[8] =
{ 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 };

View File

@ -13,6 +13,8 @@
#include "qemu/osdep.h"
#include "qemu/cutils.h"
#include "qapi/visitor.h"
#include "sysemu/qtest.h"
#include "hw/clock.h"
#include "trace.h"
@ -158,6 +160,15 @@ bool clock_set_mul_div(Clock *clk, uint32_t multiplier, uint32_t divider)
return true;
}
static void clock_period_prop_get(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
Clock *clk = CLOCK(obj);
uint64_t period = clock_get(clk);
visit_type_uint64(v, name, &period, errp);
}
static void clock_initfn(Object *obj)
{
Clock *clk = CLOCK(obj);
@ -166,6 +177,11 @@ static void clock_initfn(Object *obj)
clk->divider = 1;
QLIST_INIT(&clk->children);
if (qtest_enabled()) {
object_property_add(obj, "qtest-clock-period", "uint64",
clock_period_prop_get, NULL, NULL, NULL);
}
}
static void clock_finalizefn(Object *obj)

View File

@ -24,6 +24,7 @@
#include "sysemu/hw_accel.h"
#include "qemu/log.h"
#include "qemu/main-loop.h"
#include "qemu/lockcnt.h"
#include "exec/log.h"
#include "exec/gdbstub.h"
#include "sysemu/tcg.h"

View File

@ -1,7 +1,3 @@
config MAX7310
bool
depends on I2C
config PL061
bool

View File

@ -1,217 +0,0 @@
/*
* MAX7310 8-port GPIO expansion chip.
*
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
* This file is licensed under GNU GPL.
*/
#include "qemu/osdep.h"
#include "hw/i2c/i2c.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qom/object.h"
#define TYPE_MAX7310 "max7310"
OBJECT_DECLARE_SIMPLE_TYPE(MAX7310State, MAX7310)
struct MAX7310State {
I2CSlave parent_obj;
int i2c_command_byte;
int len;
uint8_t level;
uint8_t direction;
uint8_t polarity;
uint8_t status;
uint8_t command;
qemu_irq handler[8];
qemu_irq *gpio_in;
};
static void max7310_reset(DeviceState *dev)
{
MAX7310State *s = MAX7310(dev);
s->level &= s->direction;
s->direction = 0xff;
s->polarity = 0xf0;
s->status = 0x01;
s->command = 0x00;
}
static uint8_t max7310_rx(I2CSlave *i2c)
{
MAX7310State *s = MAX7310(i2c);
switch (s->command) {
case 0x00: /* Input port */
return s->level ^ s->polarity;
case 0x01: /* Output port */
return s->level & ~s->direction;
case 0x02: /* Polarity inversion */
return s->polarity;
case 0x03: /* Configuration */
return s->direction;
case 0x04: /* Timeout */
return s->status;
case 0xff: /* Reserved */
return 0xff;
default:
qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n",
__func__, s->command);
break;
}
return 0xff;
}
static int max7310_tx(I2CSlave *i2c, uint8_t data)
{
MAX7310State *s = MAX7310(i2c);
uint8_t diff;
int line;
if (s->len ++ > 1) {
#ifdef VERBOSE
printf("%s: message too long (%i bytes)\n", __func__, s->len);
#endif
return 1;
}
if (s->i2c_command_byte) {
s->command = data;
s->i2c_command_byte = 0;
return 0;
}
switch (s->command) {
case 0x01: /* Output port */
for (diff = (data ^ s->level) & ~s->direction; diff;
diff &= ~(1 << line)) {
line = ctz32(diff);
if (s->handler[line])
qemu_set_irq(s->handler[line], (data >> line) & 1);
}
s->level = (s->level & s->direction) | (data & ~s->direction);
break;
case 0x02: /* Polarity inversion */
s->polarity = data;
break;
case 0x03: /* Configuration */
s->level &= ~(s->direction ^ data);
s->direction = data;
break;
case 0x04: /* Timeout */
s->status = data;
break;
case 0x00: /* Input port - ignore writes */
break;
default:
qemu_log_mask(LOG_UNIMP, "%s: Unsupported register 0x02%" PRIx8 "\n",
__func__, s->command);
return 1;
}
return 0;
}
static int max7310_event(I2CSlave *i2c, enum i2c_event event)
{
MAX7310State *s = MAX7310(i2c);
s->len = 0;
switch (event) {
case I2C_START_SEND:
s->i2c_command_byte = 1;
break;
case I2C_FINISH:
#ifdef VERBOSE
if (s->len == 1)
printf("%s: message too short (%i bytes)\n", __func__, s->len);
#endif
break;
default:
break;
}
return 0;
}
static const VMStateDescription vmstate_max7310 = {
.name = "max7310",
.version_id = 0,
.minimum_version_id = 0,
.fields = (const VMStateField[]) {
VMSTATE_INT32(i2c_command_byte, MAX7310State),
VMSTATE_INT32(len, MAX7310State),
VMSTATE_UINT8(level, MAX7310State),
VMSTATE_UINT8(direction, MAX7310State),
VMSTATE_UINT8(polarity, MAX7310State),
VMSTATE_UINT8(status, MAX7310State),
VMSTATE_UINT8(command, MAX7310State),
VMSTATE_I2C_SLAVE(parent_obj, MAX7310State),
VMSTATE_END_OF_LIST()
}
};
static void max7310_gpio_set(void *opaque, int line, int level)
{
MAX7310State *s = (MAX7310State *) opaque;
assert(line >= 0 && line < ARRAY_SIZE(s->handler));
if (level)
s->level |= s->direction & (1 << line);
else
s->level &= ~(s->direction & (1 << line));
}
/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
* but also accepts sequences that are not SMBus so return an I2C device. */
static void max7310_realize(DeviceState *dev, Error **errp)
{
MAX7310State *s = MAX7310(dev);
qdev_init_gpio_in(dev, max7310_gpio_set, ARRAY_SIZE(s->handler));
qdev_init_gpio_out(dev, s->handler, ARRAY_SIZE(s->handler));
}
static void max7310_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
dc->realize = max7310_realize;
k->event = max7310_event;
k->recv = max7310_rx;
k->send = max7310_tx;
device_class_set_legacy_reset(dc, max7310_reset);
dc->vmsd = &vmstate_max7310;
}
static const TypeInfo max7310_info = {
.name = TYPE_MAX7310,
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(MAX7310State),
.class_init = max7310_class_init,
};
static void max7310_register_types(void)
{
type_register_static(&max7310_info);
}
type_init(max7310_register_types)

View File

@ -1,7 +1,6 @@
system_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c'))
system_ss.add(when: 'CONFIG_GPIO_MPC8XXX', if_true: files('mpc8xxx.c'))
system_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c'))
system_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c'))
system_ss.add(when: 'CONFIG_PCA9552', if_true: files('pca9552.c'))
system_ss.add(when: 'CONFIG_PCA9554', if_true: files('pca9554.c'))
system_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c'))

View File

@ -43,12 +43,6 @@ config IDE_VIA
bool
select IDE_PCI
config MICRODRIVE
bool
select IDE_BUS
select IDE_DEV
depends on PCMCIA
config AHCI
bool
select IDE_BUS

View File

@ -13,4 +13,3 @@ system_ss.add(when: 'CONFIG_IDE_PCI', if_true: files('pci.c'))
system_ss.add(when: 'CONFIG_IDE_PIIX', if_true: files('piix.c', 'ioport.c'))
system_ss.add(when: 'CONFIG_IDE_SII3112', if_true: files('sii3112.c'))
system_ss.add(when: 'CONFIG_IDE_VIA', if_true: files('via.c'))
system_ss.add(when: 'CONFIG_MICRODRIVE', if_true: files('microdrive.c'))

View File

@ -1,644 +0,0 @@
/*
* QEMU IDE Emulation: microdrive (CF / PCMCIA)
*
* Copyright (c) 2003 Fabrice Bellard
* Copyright (c) 2006 Openedhand Ltd.
*
* 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/pcmcia.h"
#include "migration/vmstate.h"
#include "qapi/error.h"
#include "qemu/module.h"
#include "sysemu/dma.h"
#include "hw/irq.h"
#include "qom/object.h"
#include "ide-internal.h"
#define TYPE_MICRODRIVE "microdrive"
OBJECT_DECLARE_SIMPLE_TYPE(MicroDriveState, MICRODRIVE)
/***********************************************************/
/* CF-ATA Microdrive */
#define METADATA_SIZE 0x20
/* DSCM-1XXXX Microdrive hard disk with CF+ II / PCMCIA interface. */
struct MicroDriveState {
/*< private >*/
PCMCIACardState parent_obj;
/*< public >*/
IDEBus bus;
uint32_t attr_base;
uint32_t io_base;
/* Card state */
uint8_t opt;
uint8_t stat;
uint8_t pins;
uint8_t ctrl;
uint16_t io;
uint8_t cycle;
};
/* Register bitfields */
enum md_opt {
OPT_MODE_MMAP = 0,
OPT_MODE_IOMAP16 = 1,
OPT_MODE_IOMAP1 = 2,
OPT_MODE_IOMAP2 = 3,
OPT_MODE = 0x3f,
OPT_LEVIREQ = 0x40,
OPT_SRESET = 0x80,
};
enum md_cstat {
STAT_INT = 0x02,
STAT_PWRDWN = 0x04,
STAT_XE = 0x10,
STAT_IOIS8 = 0x20,
STAT_SIGCHG = 0x40,
STAT_CHANGED = 0x80,
};
enum md_pins {
PINS_MRDY = 0x02,
PINS_CRDY = 0x20,
};
enum md_ctrl {
CTRL_IEN = 0x02,
CTRL_SRST = 0x04,
};
static inline void md_interrupt_update(MicroDriveState *s)
{
PCMCIACardState *card = PCMCIA_CARD(s);
if (card->slot == NULL) {
return;
}
qemu_set_irq(card->slot->irq,
!(s->stat & STAT_INT) && /* Inverted */
!(s->ctrl & (CTRL_IEN | CTRL_SRST)) &&
!(s->opt & OPT_SRESET));
}
static void md_set_irq(void *opaque, int irq, int level)
{
MicroDriveState *s = opaque;
if (level) {
s->stat |= STAT_INT;
} else {
s->stat &= ~STAT_INT;
}
md_interrupt_update(s);
}
static void md_reset(DeviceState *dev)
{
MicroDriveState *s = MICRODRIVE(dev);
s->opt = OPT_MODE_MMAP;
s->stat = 0;
s->pins = 0;
s->cycle = 0;
s->ctrl = 0;
ide_bus_reset(&s->bus);
}
static uint8_t md_attr_read(PCMCIACardState *card, uint32_t at)
{
MicroDriveState *s = MICRODRIVE(card);
PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card);
if (at < s->attr_base) {
if (at < pcc->cis_len) {
return pcc->cis[at];
} else {
return 0x00;
}
}
at -= s->attr_base;
switch (at) {
case 0x00: /* Configuration Option Register */
return s->opt;
case 0x02: /* Card Configuration Status Register */
if (s->ctrl & CTRL_IEN) {
return s->stat & ~STAT_INT;
} else {
return s->stat;
}
case 0x04: /* Pin Replacement Register */
return (s->pins & PINS_CRDY) | 0x0c;
case 0x06: /* Socket and Copy Register */
return 0x00;
#ifdef VERBOSE
default:
printf("%s: Bad attribute space register %02x\n", __func__, at);
#endif
}
return 0;
}
static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value)
{
MicroDriveState *s = MICRODRIVE(card);
at -= s->attr_base;
switch (at) {
case 0x00: /* Configuration Option Register */
s->opt = value & 0xcf;
if (value & OPT_SRESET) {
device_cold_reset(DEVICE(s));
}
md_interrupt_update(s);
break;
case 0x02: /* Card Configuration Status Register */
if ((s->stat ^ value) & STAT_PWRDWN) {
s->pins |= PINS_CRDY;
}
s->stat &= 0x82;
s->stat |= value & 0x74;
md_interrupt_update(s);
/* Word 170 in Identify Device must be equal to STAT_XE */
break;
case 0x04: /* Pin Replacement Register */
s->pins &= PINS_CRDY;
s->pins |= value & PINS_MRDY;
break;
case 0x06: /* Socket and Copy Register */
break;
default:
printf("%s: Bad attribute space register %02x\n", __func__, at);
}
}
static uint16_t md_common_read(PCMCIACardState *card, uint32_t at)
{
MicroDriveState *s = MICRODRIVE(card);
IDEState *ifs;
uint16_t ret;
at -= s->io_base;
switch (s->opt & OPT_MODE) {
case OPT_MODE_MMAP:
if ((at & ~0x3ff) == 0x400) {
at = 0;
}
break;
case OPT_MODE_IOMAP16:
at &= 0xf;
break;
case OPT_MODE_IOMAP1:
if ((at & ~0xf) == 0x3f0) {
at -= 0x3e8;
} else if ((at & ~0xf) == 0x1f0) {
at -= 0x1f0;
}
break;
case OPT_MODE_IOMAP2:
if ((at & ~0xf) == 0x370) {
at -= 0x368;
} else if ((at & ~0xf) == 0x170) {
at -= 0x170;
}
}
switch (at) {
case 0x0: /* Even RD Data */
case 0x8:
return ide_data_readw(&s->bus, 0);
/* TODO: 8-bit accesses */
if (s->cycle) {
ret = s->io >> 8;
} else {
s->io = ide_data_readw(&s->bus, 0);
ret = s->io & 0xff;
}
s->cycle = !s->cycle;
return ret;
case 0x9: /* Odd RD Data */
return s->io >> 8;
case 0xd: /* Error */
return ide_ioport_read(&s->bus, 0x1);
case 0xe: /* Alternate Status */
ifs = ide_bus_active_if(&s->bus);
if (ifs->blk) {
return ifs->status;
} else {
return 0;
}
case 0xf: /* Device Address */
ifs = ide_bus_active_if(&s->bus);
return 0xc2 | ((~ifs->select << 2) & 0x3c);
default:
return ide_ioport_read(&s->bus, at);
}
return 0;
}
static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value)
{
MicroDriveState *s = MICRODRIVE(card);
at -= s->io_base;
switch (s->opt & OPT_MODE) {
case OPT_MODE_MMAP:
if ((at & ~0x3ff) == 0x400) {
at = 0;
}
break;
case OPT_MODE_IOMAP16:
at &= 0xf;
break;
case OPT_MODE_IOMAP1:
if ((at & ~0xf) == 0x3f0) {
at -= 0x3e8;
} else if ((at & ~0xf) == 0x1f0) {
at -= 0x1f0;
}
break;
case OPT_MODE_IOMAP2:
if ((at & ~0xf) == 0x370) {
at -= 0x368;
} else if ((at & ~0xf) == 0x170) {
at -= 0x170;
}
}
switch (at) {
case 0x0: /* Even WR Data */
case 0x8:
ide_data_writew(&s->bus, 0, value);
break;
/* TODO: 8-bit accesses */
if (s->cycle) {
ide_data_writew(&s->bus, 0, s->io | (value << 8));
} else {
s->io = value & 0xff;
}
s->cycle = !s->cycle;
break;
case 0x9:
s->io = value & 0xff;
s->cycle = !s->cycle;
break;
case 0xd: /* Features */
ide_ioport_write(&s->bus, 0x1, value);
break;
case 0xe: /* Device Control */
s->ctrl = value;
if (value & CTRL_SRST) {
device_cold_reset(DEVICE(s));
}
md_interrupt_update(s);
break;
default:
if (s->stat & STAT_PWRDWN) {
s->pins |= PINS_CRDY;
s->stat &= ~STAT_PWRDWN;
}
ide_ioport_write(&s->bus, at, value);
}
}
static const VMStateDescription vmstate_microdrive = {
.name = "microdrive",
.version_id = 3,
.minimum_version_id = 0,
.fields = (const VMStateField[]) {
VMSTATE_UINT8(opt, MicroDriveState),
VMSTATE_UINT8(stat, MicroDriveState),
VMSTATE_UINT8(pins, MicroDriveState),
VMSTATE_UINT8(ctrl, MicroDriveState),
VMSTATE_UINT16(io, MicroDriveState),
VMSTATE_UINT8(cycle, MicroDriveState),
VMSTATE_IDE_BUS(bus, MicroDriveState),
VMSTATE_IDE_DRIVES(bus.ifs, MicroDriveState),
VMSTATE_END_OF_LIST()
}
};
static const uint8_t dscm1xxxx_cis[0x14a] = {
[0x000] = CISTPL_DEVICE, /* 5V Device Information */
[0x002] = 0x03, /* Tuple length = 4 bytes */
[0x004] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */
[0x006] = 0x01, /* Size = 2K bytes */
[0x008] = CISTPL_ENDMARK,
[0x00a] = CISTPL_DEVICE_OC, /* Additional Device Information */
[0x00c] = 0x04, /* Tuple length = 4 byest */
[0x00e] = 0x03, /* Conditions: Ext = 0, Vcc 3.3V, MWAIT = 1 */
[0x010] = 0xdb, /* ID: DTYPE_FUNCSPEC, non WP, DSPEED_150NS */
[0x012] = 0x01, /* Size = 2K bytes */
[0x014] = CISTPL_ENDMARK,
[0x016] = CISTPL_JEDEC_C, /* JEDEC ID */
[0x018] = 0x02, /* Tuple length = 2 bytes */
[0x01a] = 0xdf, /* PC Card ATA with no Vpp required */
[0x01c] = 0x01,
[0x01e] = CISTPL_MANFID, /* Manufacture ID */
[0x020] = 0x04, /* Tuple length = 4 bytes */
[0x022] = 0xa4, /* TPLMID_MANF = 00a4 (IBM) */
[0x024] = 0x00,
[0x026] = 0x00, /* PLMID_CARD = 0000 */
[0x028] = 0x00,
[0x02a] = CISTPL_VERS_1, /* Level 1 Version */
[0x02c] = 0x12, /* Tuple length = 23 bytes */
[0x02e] = 0x04, /* Major Version = JEIDA 4.2 / PCMCIA 2.1 */
[0x030] = 0x01, /* Minor Version = 1 */
[0x032] = 'I',
[0x034] = 'B',
[0x036] = 'M',
[0x038] = 0x00,
[0x03a] = 'm',
[0x03c] = 'i',
[0x03e] = 'c',
[0x040] = 'r',
[0x042] = 'o',
[0x044] = 'd',
[0x046] = 'r',
[0x048] = 'i',
[0x04a] = 'v',
[0x04c] = 'e',
[0x04e] = 0x00,
[0x050] = CISTPL_ENDMARK,
[0x052] = CISTPL_FUNCID, /* Function ID */
[0x054] = 0x02, /* Tuple length = 2 bytes */
[0x056] = 0x04, /* TPLFID_FUNCTION = Fixed Disk */
[0x058] = 0x01, /* TPLFID_SYSINIT: POST = 1, ROM = 0 */
[0x05a] = CISTPL_FUNCE, /* Function Extension */
[0x05c] = 0x02, /* Tuple length = 2 bytes */
[0x05e] = 0x01, /* TPLFE_TYPE = Disk Device Interface */
[0x060] = 0x01, /* TPLFE_DATA = PC Card ATA Interface */
[0x062] = CISTPL_FUNCE, /* Function Extension */
[0x064] = 0x03, /* Tuple length = 3 bytes */
[0x066] = 0x02, /* TPLFE_TYPE = Basic PC Card ATA Interface */
[0x068] = 0x08, /* TPLFE_DATA: Rotating, Unique, Single */
[0x06a] = 0x0f, /* TPLFE_DATA: Sleep, Standby, Idle, Auto */
[0x06c] = CISTPL_CONFIG, /* Configuration */
[0x06e] = 0x05, /* Tuple length = 5 bytes */
[0x070] = 0x01, /* TPCC_RASZ = 2 bytes, TPCC_RMSZ = 1 byte */
[0x072] = 0x07, /* TPCC_LAST = 7 */
[0x074] = 0x00, /* TPCC_RADR = 0200 */
[0x076] = 0x02,
[0x078] = 0x0f, /* TPCC_RMSK = 200, 202, 204, 206 */
[0x07a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x07c] = 0x0b, /* Tuple length = 11 bytes */
[0x07e] = 0xc0, /* TPCE_INDX = Memory Mode, Default, Iface */
[0x080] = 0xc0, /* TPCE_IF = Memory, no BVDs, no WP, READY */
[0x082] = 0xa1, /* TPCE_FS = Vcc only, no I/O, Memory, Misc */
[0x084] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
[0x086] = 0x55, /* NomV: 5.0 V */
[0x088] = 0x4d, /* MinV: 4.5 V */
[0x08a] = 0x5d, /* MaxV: 5.5 V */
[0x08c] = 0x4e, /* Peakl: 450 mA */
[0x08e] = 0x08, /* TPCE_MS = 1 window, 1 byte, Host address */
[0x090] = 0x00, /* Window descriptor: Window length = 0 */
[0x092] = 0x20, /* TPCE_MI: support power down mode, RW */
[0x094] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x096] = 0x06, /* Tuple length = 6 bytes */
[0x098] = 0x00, /* TPCE_INDX = Memory Mode, no Default */
[0x09a] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
[0x09c] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
[0x09e] = 0xb5, /* NomV: 3.3 V */
[0x0a0] = 0x1e,
[0x0a2] = 0x3e, /* Peakl: 350 mA */
[0x0a4] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x0a6] = 0x0d, /* Tuple length = 13 bytes */
[0x0a8] = 0xc1, /* TPCE_INDX = I/O and Memory Mode, Default */
[0x0aa] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
[0x0ac] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
[0x0ae] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
[0x0b0] = 0x55, /* NomV: 5.0 V */
[0x0b2] = 0x4d, /* MinV: 4.5 V */
[0x0b4] = 0x5d, /* MaxV: 5.5 V */
[0x0b6] = 0x4e, /* Peakl: 450 mA */
[0x0b8] = 0x64, /* TPCE_IO = 16-byte boundary, 16/8 accesses */
[0x0ba] = 0xf0, /* TPCE_IR = MASK, Level, Pulse, Share */
[0x0bc] = 0xff, /* IRQ0..IRQ7 supported */
[0x0be] = 0xff, /* IRQ8..IRQ15 supported */
[0x0c0] = 0x20, /* TPCE_MI = support power down mode */
[0x0c2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x0c4] = 0x06, /* Tuple length = 6 bytes */
[0x0c6] = 0x01, /* TPCE_INDX = I/O and Memory Mode */
[0x0c8] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
[0x0ca] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
[0x0cc] = 0xb5, /* NomV: 3.3 V */
[0x0ce] = 0x1e,
[0x0d0] = 0x3e, /* Peakl: 350 mA */
[0x0d2] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x0d4] = 0x12, /* Tuple length = 18 bytes */
[0x0d6] = 0xc2, /* TPCE_INDX = I/O Primary Mode */
[0x0d8] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
[0x0da] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
[0x0dc] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
[0x0de] = 0x55, /* NomV: 5.0 V */
[0x0e0] = 0x4d, /* MinV: 4.5 V */
[0x0e2] = 0x5d, /* MaxV: 5.5 V */
[0x0e4] = 0x4e, /* Peakl: 450 mA */
[0x0e6] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */
[0x0e8] = 0x61, /* Range: 2 fields, 2 bytes addr, 1 byte len */
[0x0ea] = 0xf0, /* Field 1 address = 0x01f0 */
[0x0ec] = 0x01,
[0x0ee] = 0x07, /* Address block length = 8 */
[0x0f0] = 0xf6, /* Field 2 address = 0x03f6 */
[0x0f2] = 0x03,
[0x0f4] = 0x01, /* Address block length = 2 */
[0x0f6] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */
[0x0f8] = 0x20, /* TPCE_MI = support power down mode */
[0x0fa] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x0fc] = 0x06, /* Tuple length = 6 bytes */
[0x0fe] = 0x02, /* TPCE_INDX = I/O Primary Mode, no Default */
[0x100] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
[0x102] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
[0x104] = 0xb5, /* NomV: 3.3 V */
[0x106] = 0x1e,
[0x108] = 0x3e, /* Peakl: 350 mA */
[0x10a] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x10c] = 0x12, /* Tuple length = 18 bytes */
[0x10e] = 0xc3, /* TPCE_INDX = I/O Secondary Mode, Default */
[0x110] = 0x41, /* TPCE_IF = I/O and Memory, no BVD, no WP */
[0x112] = 0x99, /* TPCE_FS = Vcc only, I/O, Interrupt, Misc */
[0x114] = 0x27, /* NomV = 1, MinV = 1, MaxV = 1, Peakl = 1 */
[0x116] = 0x55, /* NomV: 5.0 V */
[0x118] = 0x4d, /* MinV: 4.5 V */
[0x11a] = 0x5d, /* MaxV: 5.5 V */
[0x11c] = 0x4e, /* Peakl: 450 mA */
[0x11e] = 0xea, /* TPCE_IO = 1K boundary, 16/8 access, Range */
[0x120] = 0x61, /* Range: 2 fields, 2 byte addr, 1 byte len */
[0x122] = 0x70, /* Field 1 address = 0x0170 */
[0x124] = 0x01,
[0x126] = 0x07, /* Address block length = 8 */
[0x128] = 0x76, /* Field 2 address = 0x0376 */
[0x12a] = 0x03,
[0x12c] = 0x01, /* Address block length = 2 */
[0x12e] = 0xee, /* TPCE_IR = IRQ E, Level, Pulse, Share */
[0x130] = 0x20, /* TPCE_MI = support power down mode */
[0x132] = CISTPL_CFTABLE_ENTRY, /* 16-bit PC Card Configuration */
[0x134] = 0x06, /* Tuple length = 6 bytes */
[0x136] = 0x03, /* TPCE_INDX = I/O Secondary Mode */
[0x138] = 0x01, /* TPCE_FS = Vcc only, no I/O, no Memory */
[0x13a] = 0x21, /* NomV = 1, MinV = 0, MaxV = 0, Peakl = 1 */
[0x13c] = 0xb5, /* NomV: 3.3 V */
[0x13e] = 0x1e,
[0x140] = 0x3e, /* Peakl: 350 mA */
[0x142] = CISTPL_NO_LINK, /* No Link */
[0x144] = 0x00, /* Tuple length = 0 bytes */
[0x146] = CISTPL_END, /* Tuple End */
};
#define TYPE_DSCM1XXXX "dscm1xxxx"
static int dscm1xxxx_attach(PCMCIACardState *card)
{
MicroDriveState *md = MICRODRIVE(card);
PCMCIACardClass *pcc = PCMCIA_CARD_GET_CLASS(card);
md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8);
md->io_base = 0x0;
device_cold_reset(DEVICE(md));
md_interrupt_update(md);
return 0;
}
static int dscm1xxxx_detach(PCMCIACardState *card)
{
MicroDriveState *md = MICRODRIVE(card);
device_cold_reset(DEVICE(md));
return 0;
}
PCMCIACardState *dscm1xxxx_init(DriveInfo *dinfo)
{
MicroDriveState *md;
md = MICRODRIVE(object_new(TYPE_DSCM1XXXX));
qdev_realize(DEVICE(md), NULL, &error_fatal);
if (dinfo != NULL) {
ide_bus_create_drive(&md->bus, 0, dinfo);
}
md->bus.ifs[0].drive_kind = IDE_CFATA;
md->bus.ifs[0].mdata_size = METADATA_SIZE;
md->bus.ifs[0].mdata_storage = g_malloc0(METADATA_SIZE);
return PCMCIA_CARD(md);
}
static void dscm1xxxx_class_init(ObjectClass *oc, void *data)
{
PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
pcc->cis = dscm1xxxx_cis;
pcc->cis_len = sizeof(dscm1xxxx_cis);
pcc->attach = dscm1xxxx_attach;
pcc->detach = dscm1xxxx_detach;
/* Reason: Needs to be wired-up in code, see dscm1xxxx_init() */
dc->user_creatable = false;
}
static const TypeInfo dscm1xxxx_type_info = {
.name = TYPE_DSCM1XXXX,
.parent = TYPE_MICRODRIVE,
.class_init = dscm1xxxx_class_init,
};
static void microdrive_realize(DeviceState *dev, Error **errp)
{
MicroDriveState *md = MICRODRIVE(dev);
ide_bus_init_output_irq(&md->bus, qemu_allocate_irq(md_set_irq, md, 0));
}
static void microdrive_init(Object *obj)
{
MicroDriveState *md = MICRODRIVE(obj);
ide_bus_init(&md->bus, sizeof(md->bus), DEVICE(obj), 0, 1);
}
static void microdrive_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
PCMCIACardClass *pcc = PCMCIA_CARD_CLASS(oc);
pcc->attr_read = md_attr_read;
pcc->attr_write = md_attr_write;
pcc->common_read = md_common_read;
pcc->common_write = md_common_write;
pcc->io_read = md_common_read;
pcc->io_write = md_common_write;
dc->realize = microdrive_realize;
device_class_set_legacy_reset(dc, md_reset);
dc->vmsd = &vmstate_microdrive;
}
static const TypeInfo microdrive_type_info = {
.name = TYPE_MICRODRIVE,
.parent = TYPE_PCMCIA_CARD,
.instance_size = sizeof(MicroDriveState),
.instance_init = microdrive_init,
.abstract = true,
.class_init = microdrive_class_init,
};
static void microdrive_register_types(void)
{
type_register_static(&microdrive_type_info);
type_register_static(&dscm1xxxx_type_info);
}
type_init(microdrive_register_types)

View File

@ -781,7 +781,7 @@ static void icv_activate_irq(GICv3CPUState *cs, int idx, int grp)
if (nmi) {
cs->ich_apr[grp][regno] |= ICV_AP1R_EL1_NMI;
} else {
cs->ich_apr[grp][regno] |= (1 << regbit);
cs->ich_apr[grp][regno] |= (1U << regbit);
}
}
@ -793,7 +793,7 @@ static void icv_activate_vlpi(GICv3CPUState *cs)
int regno = aprbit / 32;
int regbit = aprbit % 32;
cs->ich_apr[cs->hppvlpi.grp][regno] |= (1 << regbit);
cs->ich_apr[cs->hppvlpi.grp][regno] |= (1U << regbit);
gicv3_redist_vlpi_pending(cs, cs->hppvlpi.irq, 0);
}
@ -1170,7 +1170,7 @@ static void icc_activate_irq(GICv3CPUState *cs, int irq)
if (nmi) {
cs->icc_apr[cs->hppi.grp][regno] |= ICC_AP1R_EL1_NMI;
} else {
cs->icc_apr[cs->hppi.grp][regno] |= (1 << regbit);
cs->icc_apr[cs->hppi.grp][regno] |= (1U << regbit);
}
if (irq < GIC_INTERNAL) {

View File

@ -392,22 +392,15 @@ static void omap_intc_class_init(ObjectClass *klass, void *data)
}
static const TypeInfo omap_intc_info = {
.name = "omap-intc",
.parent = TYPE_OMAP_INTC,
.name = TYPE_OMAP_INTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(OMAPIntcState),
.instance_init = omap_intc_init,
.class_init = omap_intc_class_init,
};
static const TypeInfo omap_intc_type_info = {
.name = TYPE_OMAP_INTC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(OMAPIntcState),
.abstract = true,
};
static void omap_intc_register_types(void)
{
type_register_static(&omap_intc_type_info);
type_register_static(&omap_intc_info);
}

View File

@ -27,7 +27,6 @@ subdir('nvram')
subdir('pci')
subdir('pci-bridge')
subdir('pci-host')
subdir('pcmcia')
subdir('rtc')
subdir('scsi')
subdir('sd')

View File

@ -74,7 +74,6 @@ config IVSHMEM_DEVICE
config ECCMEMCTL
bool
select ECC
config IMX
bool
@ -82,6 +81,9 @@ config IMX
select SSI
select USB_EHCI_SYSBUS
config STM32_RCC
bool
config STM32F2XX_SYSCFG
bool

View File

@ -100,6 +100,7 @@ system_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files(
system_ss.add(when: 'CONFIG_XLNX_VERSAL_TRNG', if_true: files(
'xlnx-versal-trng.c',
))
system_ss.add(when: 'CONFIG_STM32_RCC', if_true: files('stm32_rcc.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'))

162
hw/misc/stm32_rcc.c Normal file
View File

@ -0,0 +1,162 @@
/*
* STM32 RCC (only reset and enable registers are implemented)
*
* Copyright (c) 2024 Román Cárdenas <rcardenas.rod@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "trace.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
#include "hw/misc/stm32_rcc.h"
static void stm32_rcc_reset(DeviceState *dev)
{
STM32RccState *s = STM32_RCC(dev);
for (int i = 0; i < STM32_RCC_NREGS; i++) {
s->regs[i] = 0;
}
}
static uint64_t stm32_rcc_read(void *opaque, hwaddr addr, unsigned int size)
{
STM32RccState *s = STM32_RCC(opaque);
uint32_t value = 0;
if (addr > STM32_RCC_DCKCFGR2) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
__func__, addr);
} else {
value = s->regs[addr >> 2];
}
trace_stm32_rcc_read(addr, value);
return value;
}
static void stm32_rcc_write(void *opaque, hwaddr addr,
uint64_t val64, unsigned int size)
{
STM32RccState *s = STM32_RCC(opaque);
uint32_t value = val64;
uint32_t prev_value, new_value, irq_offset;
trace_stm32_rcc_write(value, addr);
if (addr > STM32_RCC_DCKCFGR2) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n",
__func__, addr);
return;
}
switch (addr) {
case STM32_RCC_AHB1_RSTR...STM32_RCC_APB2_RSTR:
prev_value = s->regs[addr / 4];
s->regs[addr / 4] = value;
irq_offset = ((addr - STM32_RCC_AHB1_RSTR) / 4) * 32;
for (int i = 0; i < 32; i++) {
new_value = extract32(value, i, 1);
if (extract32(prev_value, i, 1) && !new_value) {
trace_stm32_rcc_pulse_reset(irq_offset + i, new_value);
qemu_set_irq(s->reset_irq[irq_offset + i], new_value);
}
}
return;
case STM32_RCC_AHB1_ENR...STM32_RCC_APB2_ENR:
prev_value = s->regs[addr / 4];
s->regs[addr / 4] = value;
irq_offset = ((addr - STM32_RCC_AHB1_ENR) / 4) * 32;
for (int i = 0; i < 32; i++) {
new_value = extract32(value, i, 1);
if (!extract32(prev_value, i, 1) && new_value) {
trace_stm32_rcc_pulse_enable(irq_offset + i, new_value);
qemu_set_irq(s->enable_irq[irq_offset + i], new_value);
}
}
return;
default:
qemu_log_mask(
LOG_UNIMP,
"%s: The RCC peripheral only supports enable and reset in QEMU\n",
__func__
);
s->regs[addr >> 2] = value;
}
}
static const MemoryRegionOps stm32_rcc_ops = {
.read = stm32_rcc_read,
.write = stm32_rcc_write,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static void stm32_rcc_init(Object *obj)
{
STM32RccState *s = STM32_RCC(obj);
memory_region_init_io(&s->mmio, obj, &stm32_rcc_ops, s,
TYPE_STM32_RCC, STM32_RCC_PERIPHERAL_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
qdev_init_gpio_out(DEVICE(obj), s->reset_irq, STM32_RCC_NIRQS);
qdev_init_gpio_out(DEVICE(obj), s->enable_irq, STM32_RCC_NIRQS);
for (int i = 0; i < STM32_RCC_NIRQS; i++) {
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->reset_irq[i]);
sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->enable_irq[i]);
}
}
static const VMStateDescription vmstate_stm32_rcc = {
.name = TYPE_STM32_RCC,
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_UINT32_ARRAY(regs, STM32RccState, STM32_RCC_NREGS),
VMSTATE_END_OF_LIST()
}
};
static void stm32_rcc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->vmsd = &vmstate_stm32_rcc;
device_class_set_legacy_reset(dc, stm32_rcc_reset);
}
static const TypeInfo stm32_rcc_info = {
.name = TYPE_STM32_RCC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(STM32RccState),
.instance_init = stm32_rcc_init,
.class_init = stm32_rcc_class_init,
};
static void stm32_rcc_register_types(void)
{
type_register_static(&stm32_rcc_info);
}
type_init(stm32_rcc_register_types)

View File

@ -26,6 +26,9 @@
#include "trace.h"
#include "hw/irq.h"
#include "migration/vmstate.h"
#include "hw/clock.h"
#include "hw/qdev-clock.h"
#include "qapi/error.h"
#include "hw/misc/stm32l4x5_syscfg.h"
#include "hw/gpio/stm32l4x5_gpio.h"
@ -225,12 +228,22 @@ static void stm32l4x5_syscfg_init(Object *obj)
qdev_init_gpio_in(DEVICE(obj), stm32l4x5_syscfg_set_irq,
GPIO_NUM_PINS * NUM_GPIOS);
qdev_init_gpio_out(DEVICE(obj), s->gpio_out, GPIO_NUM_PINS);
s->clk = qdev_init_clock_in(DEVICE(s), "clk", NULL, s, 0);
}
static void stm32l4x5_syscfg_realize(DeviceState *dev, Error **errp)
{
Stm32l4x5SyscfgState *s = STM32L4X5_SYSCFG(dev);
if (!clock_has_source(s->clk)) {
error_setg(errp, "SYSCFG: clk input must be connected");
return;
}
}
static const VMStateDescription vmstate_stm32l4x5_syscfg = {
.name = TYPE_STM32L4X5_SYSCFG,
.version_id = 1,
.minimum_version_id = 1,
.version_id = 2,
.minimum_version_id = 2,
.fields = (VMStateField[]) {
VMSTATE_UINT32(memrmp, Stm32l4x5SyscfgState),
VMSTATE_UINT32(cfgr1, Stm32l4x5SyscfgState),
@ -241,6 +254,7 @@ static const VMStateDescription vmstate_stm32l4x5_syscfg = {
VMSTATE_UINT32(swpr, Stm32l4x5SyscfgState),
VMSTATE_UINT32(skr, Stm32l4x5SyscfgState),
VMSTATE_UINT32(swpr2, Stm32l4x5SyscfgState),
VMSTATE_CLOCK(clk, Stm32l4x5SyscfgState),
VMSTATE_END_OF_LIST()
}
};
@ -251,6 +265,7 @@ static void stm32l4x5_syscfg_class_init(ObjectClass *klass, void *data)
ResettableClass *rc = RESETTABLE_CLASS(klass);
dc->vmsd = &vmstate_stm32l4x5_syscfg;
dc->realize = stm32l4x5_syscfg_realize;
rc->phases.hold = stm32l4x5_syscfg_hold_reset;
}

View File

@ -156,6 +156,12 @@ npcm7xx_pwm_write(const char *id, uint64_t offset, uint32_t value) "%s offset: 0
npcm7xx_pwm_update_freq(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Freq: old_freq: %u, new_freq: %u"
npcm7xx_pwm_update_duty(const char *id, uint8_t index, uint32_t old_value, uint32_t new_value) "%s pwm[%u] Update Duty: old_duty: %u, new_duty: %u"
# stm32_rcc.c
stm32_rcc_read(uint64_t addr, uint64_t data) "reg read: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
stm32_rcc_write(uint64_t addr, uint64_t data) "reg write: addr: 0x%" PRIx64 " val: 0x%" PRIx64 ""
stm32_rcc_pulse_enable(int line, int level) "Enable: %d to %d"
stm32_rcc_pulse_reset(int line, int level) "Reset: %d to %d"
# stm32f4xx_syscfg.c
stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interrupt: GPIO: %d, Line: %d; Level: %d"
stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"

View File

@ -1,2 +0,0 @@
config PCMCIA
bool

View File

@ -1 +0,0 @@
system_ss.add(when: 'CONFIG_PCMCIA', if_true: files('pcmcia.c'))

View File

@ -1,24 +0,0 @@
/*
* PCMCIA emulation
*
* Copyright 2013 SUSE LINUX Products GmbH
*/
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "hw/pcmcia.h"
static const TypeInfo pcmcia_card_type_info = {
.name = TYPE_PCMCIA_CARD,
.parent = TYPE_DEVICE,
.instance_size = sizeof(PCMCIACardState),
.abstract = true,
.class_size = sizeof(PCMCIACardClass),
};
static void pcmcia_register_types(void)
{
type_register_static(&pcmcia_card_type_info);
}
type_init(pcmcia_register_types)

View File

@ -28,3 +28,7 @@ config BCM2835_SPI
config PNV_SPI
bool
select SSI
config ALLWINNER_A10_SPI
bool
select SSI

561
hw/ssi/allwinner-a10-spi.c Normal file
View File

@ -0,0 +1,561 @@
/*
* Allwinner SPI Bus Serial Interface Emulation
*
* Copyright (C) 2024 Strahinja Jankovic <strahinja.p.jankovic@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "hw/irq.h"
#include "hw/ssi/allwinner-a10-spi.h"
#include "migration/vmstate.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "trace.h"
/* Allwinner SPI memory map */
#define SPI_RXDATA_REG 0x00 /* receive data register */
#define SPI_TXDATA_REG 0x04 /* transmit data register */
#define SPI_CTL_REG 0x08 /* control register */
#define SPI_INTCTL_REG 0x0c /* interrupt control register */
#define SPI_INT_STA_REG 0x10 /* interrupt status register */
#define SPI_DMACTL_REG 0x14 /* DMA control register */
#define SPI_WAIT_REG 0x18 /* wait clock counter register */
#define SPI_CCTL_REG 0x1c /* clock rate control register */
#define SPI_BC_REG 0x20 /* burst control register */
#define SPI_TC_REG 0x24 /* transmit counter register */
#define SPI_FIFO_STA_REG 0x28 /* FIFO status register */
/* Data register */
#define SPI_DATA_RESET 0
/* Control register */
#define SPI_CTL_SDC (1 << 19)
#define SPI_CTL_TP_EN (1 << 18)
#define SPI_CTL_SS_LEVEL (1 << 17)
#define SPI_CTL_SS_CTRL (1 << 16)
#define SPI_CTL_DHB (1 << 15)
#define SPI_CTL_DDB (1 << 14)
#define SPI_CTL_SS (3 << 12)
#define SPI_CTL_SS_SHIFT 12
#define SPI_CTL_RPSM (1 << 11)
#define SPI_CTL_XCH (1 << 10)
#define SPI_CTL_RF_RST (1 << 9)
#define SPI_CTL_TF_RST (1 << 8)
#define SPI_CTL_SSCTL (1 << 7)
#define SPI_CTL_LMTF (1 << 6)
#define SPI_CTL_DMAMC (1 << 5)
#define SPI_CTL_SSPOL (1 << 4)
#define SPI_CTL_POL (1 << 3)
#define SPI_CTL_PHA (1 << 2)
#define SPI_CTL_MODE (1 << 1)
#define SPI_CTL_EN (1 << 0)
#define SPI_CTL_MASK 0xFFFFFu
#define SPI_CTL_RESET 0x0002001Cu
/* Interrupt control register */
#define SPI_INTCTL_SS_INT_EN (1 << 17)
#define SPI_INTCTL_TX_INT_EN (1 << 16)
#define SPI_INTCTL_TF_UR_INT_EN (1 << 14)
#define SPI_INTCTL_TF_OF_INT_EN (1 << 13)
#define SPI_INTCTL_TF_E34_INT_EN (1 << 12)
#define SPI_INTCTL_TF_E14_INT_EN (1 << 11)
#define SPI_INTCTL_TF_FL_INT_EN (1 << 10)
#define SPI_INTCTL_TF_HALF_EMP_INT_EN (1 << 9)
#define SPI_INTCTL_TF_EMP_INT_EN (1 << 8)
#define SPI_INTCTL_RF_UR_INT_EN (1 << 6)
#define SPI_INTCTL_RF_OF_INT_EN (1 << 5)
#define SPI_INTCTL_RF_E34_INT_EN (1 << 4)
#define SPI_INTCTL_RF_E14_INT_EN (1 << 3)
#define SPI_INTCTL_RF_FU_INT_EN (1 << 2)
#define SPI_INTCTL_RF_HALF_FU_INT_EN (1 << 1)
#define SPI_INTCTL_RF_RDY_INT_EN (1 << 0)
#define SPI_INTCTL_MASK 0x37F7Fu
#define SPI_INTCTL_RESET 0
/* Interrupt status register */
#define SPI_INT_STA_INT_CBF (1 << 31)
#define SPI_INT_STA_SSI (1 << 17)
#define SPI_INT_STA_TC (1 << 16)
#define SPI_INT_STA_TU (1 << 14)
#define SPI_INT_STA_TO (1 << 13)
#define SPI_INT_STA_TE34 (1 << 12)
#define SPI_INT_STA_TE14 (1 << 11)
#define SPI_INT_STA_TF (1 << 10)
#define SPI_INT_STA_THE (1 << 9)
#define SPI_INT_STA_TE (1 << 8)
#define SPI_INT_STA_RU (1 << 6)
#define SPI_INT_STA_RO (1 << 5)
#define SPI_INT_STA_RF34 (1 << 4)
#define SPI_INT_STA_RF14 (1 << 3)
#define SPI_INT_STA_RF (1 << 2)
#define SPI_INT_STA_RHF (1 << 1)
#define SPI_INT_STA_RR (1 << 0)
#define SPI_INT_STA_MASK 0x80037F7Fu
#define SPI_INT_STA_RESET 0x00001B00u
/* DMA control register - not implemented */
#define SPI_DMACTL_RESET 0
/* Wait clock register */
#define SPI_WAIT_REG_WCC_MASK 0xFFFFu
#define SPI_WAIT_RESET 0
/* Clock control register - not implemented */
#define SPI_CCTL_RESET 2
/* Burst count register */
#define SPI_BC_BC_MASK 0xFFFFFFu
#define SPI_BC_RESET 0
/* Transmi counter register */
#define SPI_TC_WTC_MASK 0xFFFFFFu
#define SPI_TC_RESET 0
/* FIFO status register */
#define SPI_FIFO_STA_CNT_MASK 0x7F
#define SPI_FIFO_STA_TF_CNT_SHIFT 16
#define SPI_FIFO_STA_RF_CNT_SHIFT 0
#define SPI_FIFO_STA_RESET 0
#define REG_INDEX(offset) (offset / sizeof(uint32_t))
static const char *allwinner_a10_spi_get_regname(unsigned offset)
{
switch (offset) {
case SPI_RXDATA_REG:
return "RXDATA";
case SPI_TXDATA_REG:
return "TXDATA";
case SPI_CTL_REG:
return "CTL";
case SPI_INTCTL_REG:
return "INTCTL";
case SPI_INT_STA_REG:
return "INT_STA";
case SPI_DMACTL_REG:
return "DMACTL";
case SPI_WAIT_REG:
return "WAIT";
case SPI_CCTL_REG:
return "CCTL";
case SPI_BC_REG:
return "BC";
case SPI_TC_REG:
return "TC";
case SPI_FIFO_STA_REG:
return "FIFO_STA";
default:
return "[?]";
}
}
static bool allwinner_a10_spi_is_enabled(AWA10SPIState *s)
{
return s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_EN;
}
static void allwinner_a10_spi_txfifo_reset(AWA10SPIState *s)
{
fifo8_reset(&s->tx_fifo);
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= (SPI_INT_STA_TE | SPI_INT_STA_TE14 |
SPI_INT_STA_THE | SPI_INT_STA_TE34);
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~(SPI_INT_STA_TU | SPI_INT_STA_TO);
}
static void allwinner_a10_spi_rxfifo_reset(AWA10SPIState *s)
{
fifo8_reset(&s->rx_fifo);
s->regs[REG_INDEX(SPI_INT_STA_REG)] &=
~(SPI_INT_STA_RU | SPI_INT_STA_RO | SPI_INT_STA_RF | SPI_INT_STA_RR |
SPI_INT_STA_RHF | SPI_INT_STA_RF14 | SPI_INT_STA_RF34);
}
static uint8_t allwinner_a10_spi_selected_channel(AWA10SPIState *s)
{
return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SS) >> SPI_CTL_SS_SHIFT;
}
static void allwinner_a10_spi_reset_hold(Object *obj, ResetType type)
{
AWA10SPIState *s = AW_A10_SPI(obj);
s->regs[REG_INDEX(SPI_RXDATA_REG)] = SPI_DATA_RESET;
s->regs[REG_INDEX(SPI_TXDATA_REG)] = SPI_DATA_RESET;
s->regs[REG_INDEX(SPI_CTL_REG)] = SPI_CTL_RESET;
s->regs[REG_INDEX(SPI_INTCTL_REG)] = SPI_INTCTL_RESET;
s->regs[REG_INDEX(SPI_INT_STA_REG)] = SPI_INT_STA_RESET;
s->regs[REG_INDEX(SPI_DMACTL_REG)] = SPI_DMACTL_RESET;
s->regs[REG_INDEX(SPI_WAIT_REG)] = SPI_WAIT_RESET;
s->regs[REG_INDEX(SPI_CCTL_REG)] = SPI_CCTL_RESET;
s->regs[REG_INDEX(SPI_BC_REG)] = SPI_BC_RESET;
s->regs[REG_INDEX(SPI_TC_REG)] = SPI_TC_RESET;
s->regs[REG_INDEX(SPI_FIFO_STA_REG)] = SPI_FIFO_STA_RESET;
allwinner_a10_spi_txfifo_reset(s);
allwinner_a10_spi_rxfifo_reset(s);
}
static void allwinner_a10_spi_update_irq(AWA10SPIState *s)
{
bool level;
if (fifo8_is_empty(&s->rx_fifo)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RR;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RR;
}
if (fifo8_num_used(&s->rx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 2)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF14;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF14;
}
if (fifo8_num_used(&s->rx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 1)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RHF;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RHF;
}
if (fifo8_num_free(&s->rx_fifo) <= (AW_A10_SPI_FIFO_SIZE >> 2)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF34;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF34;
}
if (fifo8_is_full(&s->rx_fifo)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_RF;
}
if (fifo8_is_empty(&s->tx_fifo)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE;
}
if (fifo8_num_free(&s->tx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 2)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE14;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE14;
}
if (fifo8_num_free(&s->tx_fifo) >= (AW_A10_SPI_FIFO_SIZE >> 1)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_THE;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_THE;
}
if (fifo8_num_used(&s->tx_fifo) <= (AW_A10_SPI_FIFO_SIZE >> 2)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TE34;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TE34;
}
if (fifo8_is_full(&s->rx_fifo)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TF;
} else {
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~SPI_INT_STA_TF;
}
level = (s->regs[REG_INDEX(SPI_INT_STA_REG)] &
s->regs[REG_INDEX(SPI_INTCTL_REG)]) != 0;
qemu_set_irq(s->irq, level);
trace_allwinner_a10_spi_update_irq(level);
}
static void allwinner_a10_spi_flush_txfifo(AWA10SPIState *s)
{
uint32_t burst_count = s->regs[REG_INDEX(SPI_BC_REG)];
uint32_t tx_burst = s->regs[REG_INDEX(SPI_TC_REG)];
trace_allwinner_a10_spi_burst_length(tx_burst);
trace_allwinner_a10_spi_flush_txfifo_begin(fifo8_num_used(&s->tx_fifo),
fifo8_num_used(&s->rx_fifo));
while (!fifo8_is_empty(&s->tx_fifo)) {
uint8_t tx = fifo8_pop(&s->tx_fifo);
uint8_t rx = 0;
bool fill_rx = true;
trace_allwinner_a10_spi_tx(tx);
/* Write one byte at a time */
rx = ssi_transfer(s->bus, tx);
trace_allwinner_a10_spi_rx(rx);
/* Check DHB here to determine if RX bytes should be stored */
if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_DHB) {
/* Store rx bytes only after WTC transfers */
if (tx_burst > 0u) {
fill_rx = false;
tx_burst--;
}
}
if (fill_rx) {
if (fifo8_is_full(&s->rx_fifo)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_RF;
} else {
fifo8_push(&s->rx_fifo, rx);
}
}
allwinner_a10_spi_update_irq(s);
burst_count--;
if (burst_count == 0) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TC;
s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_XCH;
break;
}
}
if (fifo8_is_empty(&s->tx_fifo)) {
s->regs[REG_INDEX(SPI_INT_STA_REG)] |= SPI_INT_STA_TC;
s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_XCH;
}
trace_allwinner_a10_spi_flush_txfifo_end(fifo8_num_used(&s->tx_fifo),
fifo8_num_used(&s->rx_fifo));
}
static uint64_t allwinner_a10_spi_read(void *opaque, hwaddr offset,
unsigned size)
{
uint32_t value = 0;
AWA10SPIState *s = opaque;
uint32_t index = offset >> 2;
if (offset > SPI_FIFO_STA_REG) {
qemu_log_mask(LOG_GUEST_ERROR,
"[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
TYPE_AW_A10_SPI, __func__, offset);
return 0;
}
value = s->regs[index];
if (allwinner_a10_spi_is_enabled(s)) {
switch (offset) {
case SPI_RXDATA_REG:
if (fifo8_is_empty(&s->rx_fifo)) {
/* value is undefined */
value = 0xdeadbeef;
} else {
/* read from the RX FIFO */
value = fifo8_pop(&s->rx_fifo);
}
break;
case SPI_TXDATA_REG:
qemu_log_mask(LOG_GUEST_ERROR,
"[%s]%s: Trying to read from TX FIFO\n",
TYPE_AW_A10_SPI, __func__);
/* Reading from TXDATA gives 0 */
break;
case SPI_FIFO_STA_REG:
/* Read current tx/rx fifo data count */
value = fifo8_num_used(&s->tx_fifo) << SPI_FIFO_STA_TF_CNT_SHIFT |
fifo8_num_used(&s->rx_fifo) << SPI_FIFO_STA_RF_CNT_SHIFT;
break;
case SPI_CTL_REG:
case SPI_INTCTL_REG:
case SPI_INT_STA_REG:
case SPI_DMACTL_REG:
case SPI_WAIT_REG:
case SPI_CCTL_REG:
case SPI_BC_REG:
case SPI_TC_REG:
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad offset 0x%x\n", __func__,
(uint32_t)offset);
break;
}
allwinner_a10_spi_update_irq(s);
}
trace_allwinner_a10_spi_read(allwinner_a10_spi_get_regname(offset), value);
return value;
}
static bool allwinner_a10_spi_update_cs_level(AWA10SPIState *s, int cs_line_nr)
{
if (cs_line_nr == allwinner_a10_spi_selected_channel(s)) {
return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SS_LEVEL) != 0;
} else {
return (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_SSPOL) != 0;
}
}
static void allwinner_a10_spi_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
AWA10SPIState *s = opaque;
uint32_t index = offset >> 2;
int i = 0;
if (offset > SPI_FIFO_STA_REG) {
qemu_log_mask(LOG_GUEST_ERROR,
"[%s]%s: Bad register at offset 0x%" HWADDR_PRIx "\n",
TYPE_AW_A10_SPI, __func__, offset);
return;
}
trace_allwinner_a10_spi_write(allwinner_a10_spi_get_regname(offset),
(uint32_t)value);
if (!allwinner_a10_spi_is_enabled(s)) {
/* Block is disabled */
if (offset != SPI_CTL_REG) {
/* Ignore access */
return;
}
}
switch (offset) {
case SPI_RXDATA_REG:
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to write to RX FIFO\n",
TYPE_AW_A10_SPI, __func__);
break;
case SPI_TXDATA_REG:
if (fifo8_is_full(&s->tx_fifo)) {
/* Ignore writes if queue is full */
break;
}
fifo8_push(&s->tx_fifo, (uint8_t)value);
break;
case SPI_INT_STA_REG:
/* Handle W1C bits - everything except SPI_INT_STA_INT_CBF. */
value &= ~SPI_INT_STA_INT_CBF;
s->regs[REG_INDEX(SPI_INT_STA_REG)] &= ~(value & SPI_INT_STA_MASK);
break;
case SPI_CTL_REG:
s->regs[REG_INDEX(SPI_CTL_REG)] = value;
for (i = 0; i < AW_A10_SPI_CS_LINES_NR; i++) {
qemu_set_irq(
s->cs_lines[i],
allwinner_a10_spi_update_cs_level(s, i));
}
if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_XCH) {
/* Request to start emitting */
allwinner_a10_spi_flush_txfifo(s);
}
if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_TF_RST) {
allwinner_a10_spi_txfifo_reset(s);
s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_TF_RST;
}
if (s->regs[REG_INDEX(SPI_CTL_REG)] & SPI_CTL_RF_RST) {
allwinner_a10_spi_rxfifo_reset(s);
s->regs[REG_INDEX(SPI_CTL_REG)] &= ~SPI_CTL_RF_RST;
}
break;
case SPI_INTCTL_REG:
case SPI_DMACTL_REG:
case SPI_WAIT_REG:
case SPI_CCTL_REG:
case SPI_BC_REG:
case SPI_TC_REG:
case SPI_FIFO_STA_REG:
s->regs[index] = value;
break;
default:
qemu_log_mask(LOG_GUEST_ERROR,
"%s: bad offset 0x%x\n", __func__,
(uint32_t)offset);
break;
}
allwinner_a10_spi_update_irq(s);
}
static const MemoryRegionOps allwinner_a10_spi_ops = {
.read = allwinner_a10_spi_read,
.write = allwinner_a10_spi_write,
.valid.min_access_size = 1,
.valid.max_access_size = 4,
.endianness = DEVICE_NATIVE_ENDIAN,
};
static const VMStateDescription allwinner_a10_spi_vmstate = {
.name = TYPE_AW_A10_SPI,
.version_id = 1,
.minimum_version_id = 1,
.fields = (const VMStateField[]) {
VMSTATE_FIFO8(tx_fifo, AWA10SPIState),
VMSTATE_FIFO8(rx_fifo, AWA10SPIState),
VMSTATE_UINT32_ARRAY(regs, AWA10SPIState, AW_A10_SPI_REGS_NUM),
VMSTATE_END_OF_LIST()
}
};
static void allwinner_a10_spi_realize(DeviceState *dev, Error **errp)
{
AWA10SPIState *s = AW_A10_SPI(dev);
int i = 0;
memory_region_init_io(&s->iomem, OBJECT(s), &allwinner_a10_spi_ops, s,
TYPE_AW_A10_SPI, AW_A10_SPI_IOSIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
s->bus = ssi_create_bus(dev, "spi");
for (i = 0; i < AW_A10_SPI_CS_LINES_NR; i++) {
sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->cs_lines[i]);
}
fifo8_create(&s->tx_fifo, AW_A10_SPI_FIFO_SIZE);
fifo8_create(&s->rx_fifo, AW_A10_SPI_FIFO_SIZE);
}
static void allwinner_a10_spi_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ResettableClass *rc = RESETTABLE_CLASS(klass);
rc->phases.hold = allwinner_a10_spi_reset_hold;
dc->vmsd = &allwinner_a10_spi_vmstate;
dc->realize = allwinner_a10_spi_realize;
dc->desc = "Allwinner A10 SPI Controller";
}
static const TypeInfo allwinner_a10_spi_type_info = {
.name = TYPE_AW_A10_SPI,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(AWA10SPIState),
.class_init = allwinner_a10_spi_class_init,
};
static void allwinner_a10_spi_register_types(void)
{
type_register_static(&allwinner_a10_spi_type_info);
}
type_init(allwinner_a10_spi_register_types)

View File

@ -1,3 +1,4 @@
system_ss.add(when: 'CONFIG_ALLWINNER_A10_SPI', if_true: files('allwinner-a10-spi.c'))
system_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
system_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c', 'npcm_pspi.c'))

View File

@ -53,3 +53,13 @@ pnv_spi_rx_read_N2frame(void) ""
pnv_spi_shift_rx(uint8_t byte, uint32_t index) "byte = 0x%2.2x into RDR from payload index %d"
pnv_spi_sequencer_stop_requested(const char* reason) "due to %s"
pnv_spi_RDR_match(const char* result) "%s"
# allwinner_a10_spi.c
allwinner_a10_spi_update_irq(uint32_t level) "IRQ level is %d"
allwinner_a10_spi_flush_txfifo_begin(uint32_t tx, uint32_t rx) "Begin: TX Fifo Size = %d, RX Fifo Size = %d"
allwinner_a10_spi_flush_txfifo_end(uint32_t tx, uint32_t rx) "End: TX Fifo Size = %d, RX Fifo Size = %d"
allwinner_a10_spi_burst_length(uint32_t len) "Burst length = %d"
allwinner_a10_spi_tx(uint8_t byte) "write 0x%02x"
allwinner_a10_spi_rx(uint8_t byte) "read 0x%02x"
allwinner_a10_spi_read(const char* regname, uint32_t value) "reg[%s] => 0x%08x"
allwinner_a10_spi_write(const char* regname, uint32_t value) "reg[%s] <= 0x%08x"

View File

@ -20,6 +20,7 @@
#include "qemu/coroutine-core.h"
#include "qemu/queue.h"
#include "qemu/event_notifier.h"
#include "qemu/lockcnt.h"
#include "qemu/thread.h"
#include "qemu/timer.h"
#include "block/graph-lock.h"

View File

@ -1,56 +0,0 @@
/*
* Maxim MAX1110/1111 ADC chip emulation.
*
* Copyright (c) 2006 Openedhand Ltd.
* Written by Andrzej Zaborowski <balrog@zabor.org>
*
* This code is licensed under the GNU GPLv2.
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_MISC_MAX111X_H
#define HW_MISC_MAX111X_H
#include "hw/ssi/ssi.h"
#include "qom/object.h"
/*
* This is a model of the Maxim MAX1110/1111 ADC chip, which for QEMU
* is an SSI slave device. It has either 4 (max1110) or 8 (max1111)
* 8-bit ADC channels.
*
* QEMU interface:
* + GPIO inputs 0..3 (for max1110) or 0..7 (for max1111): set the value
* of each ADC input, as an unsigned 8-bit value
* + GPIO output 0: interrupt line
* + Properties "input0" to "input3" (max1110) or "input0" to "input7"
* (max1111): initial reset values for ADC inputs.
*
* Known bugs:
* + the interrupt line is not correctly implemented, and will never
* be lowered once it has been asserted.
*/
struct MAX111xState {
SSIPeripheral parent_obj;
qemu_irq interrupt;
/* Values of inputs at system reset (settable by QOM property) */
uint8_t reset_input[8];
uint8_t tb1, rb2, rb3;
int cycle;
uint8_t input[8];
int inputs, com;
};
#define TYPE_MAX_111X "max111x"
OBJECT_DECLARE_SIMPLE_TYPE(MAX111xState, MAX_111X)
#define TYPE_MAX_1110 "max1110"
#define TYPE_MAX_1111 "max1111"
#endif

View File

@ -12,6 +12,7 @@
#include "hw/misc/allwinner-a10-ccm.h"
#include "hw/misc/allwinner-a10-dramc.h"
#include "hw/i2c/allwinner-i2c.h"
#include "hw/ssi/allwinner-a10-spi.h"
#include "hw/watchdog/allwinner-wdt.h"
#include "sysemu/block-backend.h"
@ -40,6 +41,7 @@ struct AwA10State {
AllwinnerAHCIState sata;
AwSdHostState mmc0;
AWI2CState i2c0;
AWA10SPIState spi0;
AwRtcState rtc;
AwWdtState wdt;
MemoryRegion sram_a;

View File

@ -59,7 +59,7 @@ int64_t omap_clk_getrate(omap_clk clk);
void omap_clk_reparent(omap_clk clk, omap_clk parent);
/* omap_intc.c */
#define TYPE_OMAP_INTC "common-omap-intc"
#define TYPE_OMAP_INTC "omap-intc"
typedef struct OMAPIntcState OMAPIntcState;
DECLARE_INSTANCE_CHECKER(OMAPIntcState, OMAP_INTC, TYPE_OMAP_INTC)
@ -490,15 +490,7 @@ qemu_irq *omap_mpuio_in_get(struct omap_mpuio_s *s);
void omap_mpuio_out_set(struct omap_mpuio_s *s, int line, qemu_irq handler);
void omap_mpuio_key(struct omap_mpuio_s *s, int row, int col, int down);
typedef struct uWireSlave {
uint16_t (*receive)(void *opaque);
void (*send)(void *opaque, uint16_t data);
void *opaque;
} uWireSlave;
struct omap_uwire_s;
void omap_uwire_attach(struct omap_uwire_s *s,
uWireSlave *slave, int chipselect);
struct I2SCodec {
void *opaque;

View File

@ -25,6 +25,7 @@
#ifndef HW_ARM_STM32F405_SOC_H
#define HW_ARM_STM32F405_SOC_H
#include "hw/misc/stm32_rcc.h"
#include "hw/misc/stm32f4xx_syscfg.h"
#include "hw/timer/stm32f2xx_timer.h"
#include "hw/char/stm32f2xx_usart.h"
@ -55,6 +56,7 @@ struct STM32F405State {
ARMv7MState armv7m;
STM32RccState rcc;
STM32F4xxSyscfgState syscfg;
STM32F4xxExtiState exti;
STM32F2XXUsartState usart[STM_NUM_USARTS];

View File

@ -62,17 +62,6 @@ uint32_t nand_getbuswidth(DeviceState *dev);
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c
/* ecc.c */
typedef struct {
uint8_t cp; /* Column parity */
uint16_t lp[2]; /* Line parity */
uint16_t count;
} ECCState;
uint8_t ecc_digest(ECCState *s, uint8_t sample);
void ecc_reset(ECCState *s);
extern const VMStateDescription vmstate_ecc_state;
/* m25p80.c */
#define TYPE_M25P80 "m25p80-generic"

View File

@ -33,6 +33,7 @@
#include "qemu/bitmap.h"
#include "qemu/rcu_queue.h"
#include "qemu/queue.h"
#include "qemu/lockcnt.h"
#include "qemu/thread.h"
#include "qom/object.h"

View File

@ -0,0 +1,91 @@
/*
* STM32 RCC (only reset and enable registers are implemented)
*
* Copyright (c) 2024 Román Cárdenas <rcardenas.rod@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef HW_STM32_RCC_H
#define HW_STM32_RCC_H
#include "hw/sysbus.h"
#include "qom/object.h"
#define STM32_RCC_CR 0x00
#define STM32_RCC_PLL_CFGR 0x04
#define STM32_RCC_CFGR 0x08
#define STM32_RCC_CIR 0x0C
#define STM32_RCC_AHB1_RSTR 0x10
#define STM32_RCC_AHB2_RSTR 0x14
#define STM32_RCC_AHB3_RSTR 0x18
#define STM32_RCC_APB1_RSTR 0x20
#define STM32_RCC_APB2_RSTR 0x24
#define STM32_RCC_AHB1_ENR 0x30
#define STM32_RCC_AHB2_ENR 0x34
#define STM32_RCC_AHB3_ENR 0x38
#define STM32_RCC_APB1_ENR 0x40
#define STM32_RCC_APB2_ENR 0x44
#define STM32_RCC_AHB1_LPENR 0x50
#define STM32_RCC_AHB2_LPENR 0x54
#define STM32_RCC_AHB3_LPENR 0x58
#define STM32_RCC_APB1_LPENR 0x60
#define STM32_RCC_APB2_LPENR 0x64
#define STM32_RCC_BDCR 0x70
#define STM32_RCC_CSR 0x74
#define STM32_RCC_SSCGR 0x80
#define STM32_RCC_PLLI2SCFGR 0x84
#define STM32_RCC_PLLSAI_CFGR 0x88
#define STM32_RCC_DCKCFGR 0x8C
#define STM32_RCC_CKGATENR 0x90
#define STM32_RCC_DCKCFGR2 0x94
#define STM32_RCC_NREGS ((STM32_RCC_DCKCFGR2 >> 2) + 1)
#define STM32_RCC_PERIPHERAL_SIZE 0x400
#define STM32_RCC_NIRQS (32 * 5) /* 32 bits per reg, 5 en/rst regs */
#define STM32_RCC_GPIO_IRQ_OFFSET 0
#define TYPE_STM32_RCC "stm32.rcc"
typedef struct STM32RccState STM32RccState;
DECLARE_INSTANCE_CHECKER(STM32RccState, STM32_RCC, TYPE_STM32_RCC)
#define NUM_GPIO_EVENT_IN_LINES 16
struct STM32RccState {
SysBusDevice parent_obj;
MemoryRegion mmio;
uint32_t regs[STM32_RCC_NREGS];
qemu_irq enable_irq[STM32_RCC_NIRQS];
qemu_irq reset_irq[STM32_RCC_NIRQS];
};
#endif /* HW_STM32_RCC_H */

View File

@ -48,6 +48,7 @@ struct Stm32l4x5SyscfgState {
uint32_t swpr2;
qemu_irq gpio_out[GPIO_NUM_PINS];
Clock *clk;
};
#endif

View File

@ -1,66 +0,0 @@
#ifndef HW_PCMCIA_H
#define HW_PCMCIA_H
/* PCMCIA/Cardbus */
#include "hw/qdev-core.h"
#include "qom/object.h"
typedef struct PCMCIASocket {
qemu_irq irq;
bool attached;
} PCMCIASocket;
#define TYPE_PCMCIA_CARD "pcmcia-card"
OBJECT_DECLARE_TYPE(PCMCIACardState, PCMCIACardClass, PCMCIA_CARD)
struct PCMCIACardState {
/*< private >*/
DeviceState parent_obj;
/*< public >*/
PCMCIASocket *slot;
};
struct PCMCIACardClass {
/*< private >*/
DeviceClass parent_class;
/*< public >*/
int (*attach)(PCMCIACardState *state);
int (*detach)(PCMCIACardState *state);
const uint8_t *cis;
int cis_len;
/* Only valid if attached */
uint8_t (*attr_read)(PCMCIACardState *card, uint32_t address);
void (*attr_write)(PCMCIACardState *card, uint32_t address, uint8_t value);
uint16_t (*common_read)(PCMCIACardState *card, uint32_t address);
void (*common_write)(PCMCIACardState *card,
uint32_t address, uint16_t value);
uint16_t (*io_read)(PCMCIACardState *card, uint32_t address);
void (*io_write)(PCMCIACardState *card, uint32_t address, uint16_t value);
};
#define CISTPL_DEVICE 0x01 /* 5V Device Information Tuple */
#define CISTPL_NO_LINK 0x14 /* No Link Tuple */
#define CISTPL_VERS_1 0x15 /* Level 1 Version Tuple */
#define CISTPL_JEDEC_C 0x18 /* JEDEC ID Tuple */
#define CISTPL_JEDEC_A 0x19 /* JEDEC ID Tuple */
#define CISTPL_CONFIG 0x1a /* Configuration Tuple */
#define CISTPL_CFTABLE_ENTRY 0x1b /* 16-bit PCCard Configuration */
#define CISTPL_DEVICE_OC 0x1c /* Additional Device Information */
#define CISTPL_DEVICE_OA 0x1d /* Additional Device Information */
#define CISTPL_DEVICE_GEO 0x1e /* Additional Device Information */
#define CISTPL_DEVICE_GEO_A 0x1f /* Additional Device Information */
#define CISTPL_MANFID 0x20 /* Manufacture ID Tuple */
#define CISTPL_FUNCID 0x21 /* Function ID Tuple */
#define CISTPL_FUNCE 0x22 /* Function Extension Tuple */
#define CISTPL_END 0xff /* Tuple End */
#define CISTPL_ENDMARK 0xff
/* dscm1xxxx.c */
PCMCIACardState *dscm1xxxx_init(DriveInfo *bdrv);
#endif

View File

@ -0,0 +1,57 @@
/*
* Allwinner SPI Bus Serial Interface registers definition
*
* Copyright (C) 2024 Strahinja Jankovic. <strahinja.p.jankovic@gmail.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef ALLWINNER_A10_SPI_H
#define ALLWINNER_A10_SPI_H
#include "hw/ssi/ssi.h"
#include "hw/sysbus.h"
#include "qemu/fifo8.h"
#include "qom/object.h"
/** Size of register I/O address space used by SPI device */
#define AW_A10_SPI_IOSIZE (0x1000)
/** Total number of known registers */
#define AW_A10_SPI_REGS_NUM (AW_A10_SPI_IOSIZE / sizeof(uint32_t))
#define AW_A10_SPI_FIFO_SIZE (64)
#define AW_A10_SPI_CS_LINES_NR (4)
#define TYPE_AW_A10_SPI "allwinner.spi"
OBJECT_DECLARE_SIMPLE_TYPE(AWA10SPIState, AW_A10_SPI)
struct AWA10SPIState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
SSIBus *bus;
qemu_irq irq;
qemu_irq cs_lines[AW_A10_SPI_CS_LINES_NR];
uint32_t regs[AW_A10_SPI_REGS_NUM];
Fifo8 rx_fifo;
Fifo8 tx_fifo;
};
#endif /* ALLWINNER_A10_SPI_H */

130
include/qemu/lockcnt.h Normal file
View File

@ -0,0 +1,130 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* QemuLockCnt implementation
*
* Copyright Red Hat, Inc. 2017
*
* Author:
* Paolo Bonzini <pbonzini@redhat.com>
*
*/
#ifndef QEMU_LOCKCNT_H
#define QEMU_LOCKCNT_H
#include "qemu/thread.h"
typedef struct QemuLockCnt QemuLockCnt;
struct QemuLockCnt {
#ifndef CONFIG_LINUX
QemuMutex mutex;
#endif
unsigned count;
};
/**
* qemu_lockcnt_init: initialize a QemuLockcnt
* @lockcnt: the lockcnt to initialize
*
* Initialize lockcnt's counter to zero and prepare its mutex
* for usage.
*/
void qemu_lockcnt_init(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_destroy: destroy a QemuLockcnt
* @lockcnt: the lockcnt to destruct
*
* Destroy lockcnt's mutex.
*/
void qemu_lockcnt_destroy(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_inc: increment a QemuLockCnt's counter
* @lockcnt: the lockcnt to operate on
*
* If the lockcnt's count is zero, wait for critical sections
* to finish and increment lockcnt's count to 1. If the count
* is not zero, just increment it.
*
* Because this function can wait on the mutex, it must not be
* called while the lockcnt's mutex is held by the current thread.
* For the same reason, qemu_lockcnt_inc can also contribute to
* AB-BA deadlocks. This is a sample deadlock scenario::
*
* thread 1 thread 2
* -------------------------------------------------------
* qemu_lockcnt_lock(&lc1);
* qemu_lockcnt_lock(&lc2);
* qemu_lockcnt_inc(&lc2);
* qemu_lockcnt_inc(&lc1);
*/
void qemu_lockcnt_inc(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_dec: decrement a QemuLockCnt's counter
* @lockcnt: the lockcnt to operate on
*/
void qemu_lockcnt_dec(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_dec_and_lock: decrement a QemuLockCnt's counter and
* possibly lock it.
* @lockcnt: the lockcnt to operate on
*
* Decrement lockcnt's count. If the new count is zero, lock
* the mutex and return true. Otherwise, return false.
*/
bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_dec_if_lock: possibly decrement a QemuLockCnt's counter and
* lock it.
* @lockcnt: the lockcnt to operate on
*
* If the count is 1, decrement the count to zero, lock
* the mutex and return true. Otherwise, return false.
*/
bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_lock: lock a QemuLockCnt's mutex.
* @lockcnt: the lockcnt to operate on
*
* Remember that concurrent visits are not blocked unless the count is
* also zero. You can use qemu_lockcnt_count to check for this inside a
* critical section.
*/
void qemu_lockcnt_lock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_unlock: release a QemuLockCnt's mutex.
* @lockcnt: the lockcnt to operate on.
*/
void qemu_lockcnt_unlock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_inc_and_unlock: combined unlock/increment on a QemuLockCnt.
* @lockcnt: the lockcnt to operate on.
*
* This is the same as
*
* qemu_lockcnt_unlock(lockcnt);
* qemu_lockcnt_inc(lockcnt);
*
* but more efficient.
*/
void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_count: query a LockCnt's count.
* @lockcnt: the lockcnt to query.
*
* Note that the count can change at any time. Still, while the
* lockcnt is locked, one can usefully check whether the count
* is non-zero.
*/
unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt);
#endif

View File

@ -293,115 +293,4 @@ static inline void qemu_spin_unlock(QemuSpin *spin)
#endif
}
struct QemuLockCnt {
#ifndef CONFIG_LINUX
QemuMutex mutex;
#endif
unsigned count;
};
/**
* qemu_lockcnt_init: initialize a QemuLockcnt
* @lockcnt: the lockcnt to initialize
*
* Initialize lockcnt's counter to zero and prepare its mutex
* for usage.
*/
void qemu_lockcnt_init(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_destroy: destroy a QemuLockcnt
* @lockcnt: the lockcnt to destruct
*
* Destroy lockcnt's mutex.
*/
void qemu_lockcnt_destroy(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_inc: increment a QemuLockCnt's counter
* @lockcnt: the lockcnt to operate on
*
* If the lockcnt's count is zero, wait for critical sections
* to finish and increment lockcnt's count to 1. If the count
* is not zero, just increment it.
*
* Because this function can wait on the mutex, it must not be
* called while the lockcnt's mutex is held by the current thread.
* For the same reason, qemu_lockcnt_inc can also contribute to
* AB-BA deadlocks. This is a sample deadlock scenario:
*
* thread 1 thread 2
* -------------------------------------------------------
* qemu_lockcnt_lock(&lc1);
* qemu_lockcnt_lock(&lc2);
* qemu_lockcnt_inc(&lc2);
* qemu_lockcnt_inc(&lc1);
*/
void qemu_lockcnt_inc(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_dec: decrement a QemuLockCnt's counter
* @lockcnt: the lockcnt to operate on
*/
void qemu_lockcnt_dec(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_dec_and_lock: decrement a QemuLockCnt's counter and
* possibly lock it.
* @lockcnt: the lockcnt to operate on
*
* Decrement lockcnt's count. If the new count is zero, lock
* the mutex and return true. Otherwise, return false.
*/
bool qemu_lockcnt_dec_and_lock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_dec_if_lock: possibly decrement a QemuLockCnt's counter and
* lock it.
* @lockcnt: the lockcnt to operate on
*
* If the count is 1, decrement the count to zero, lock
* the mutex and return true. Otherwise, return false.
*/
bool qemu_lockcnt_dec_if_lock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_lock: lock a QemuLockCnt's mutex.
* @lockcnt: the lockcnt to operate on
*
* Remember that concurrent visits are not blocked unless the count is
* also zero. You can use qemu_lockcnt_count to check for this inside a
* critical section.
*/
void qemu_lockcnt_lock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_unlock: release a QemuLockCnt's mutex.
* @lockcnt: the lockcnt to operate on.
*/
void qemu_lockcnt_unlock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_inc_and_unlock: combined unlock/increment on a QemuLockCnt.
* @lockcnt: the lockcnt to operate on.
*
* This is the same as
*
* qemu_lockcnt_unlock(lockcnt);
* qemu_lockcnt_inc(lockcnt);
*
* but more efficient.
*/
void qemu_lockcnt_inc_and_unlock(QemuLockCnt *lockcnt);
/**
* qemu_lockcnt_count: query a LockCnt's count.
* @lockcnt: the lockcnt to query.
*
* Note that the count can change at any time. Still, while the
* lockcnt is locked, one can usefully check whether the count
* is non-zero.
*/
unsigned qemu_lockcnt_count(QemuLockCnt *lockcnt);
#endif

View File

@ -152,7 +152,7 @@ static inline MemTxResult dma_memory_read(AddressSpace *as, dma_addr_t addr,
}
/**
* address_space_write: Write to address space from DMA controller.
* dma_memory_write: Write to address space from DMA controller.
*
* Return a MemTxResult indicating whether the operation succeeded
* or failed (eg unassigned memory, device rejected the transaction,
@ -189,7 +189,7 @@ MemTxResult dma_memory_set(AddressSpace *as, dma_addr_t addr,
uint8_t c, dma_addr_t len, MemTxAttrs attrs);
/**
* address_space_map: Map a physical memory region into a host virtual address.
* dma_memory_map: Map a physical memory region into a host virtual address.
*
* May map a subset of the requested range, given by and returned in @plen.
* May return %NULL and set *@plen to zero(0), if resources needed to perform
@ -216,16 +216,15 @@ static inline void *dma_memory_map(AddressSpace *as,
}
/**
* address_space_unmap: Unmaps a memory region previously mapped
* by dma_memory_map()
* dma_memory_unmap: Unmaps a memory region previously mapped by dma_memory_map()
*
* Will also mark the memory as dirty if @dir == %DMA_DIRECTION_FROM_DEVICE.
* @access_len gives the amount of memory that was actually read or written
* by the caller.
*
* @as: #AddressSpace used
* @buffer: host pointer as returned by address_space_map()
* @len: buffer length as returned by address_space_map()
* @buffer: host pointer as returned by dma_memory_map()
* @len: buffer length as returned by dma_memory_map()
* @dir: indicates the transfer direction
* @access_len: amount of data actually transferred
*/

View File

@ -41,7 +41,6 @@ extern int graphic_height;
extern int graphic_depth;
extern int display_opengl;
extern const char *keyboard_layout;
extern int graphic_rotate;
extern int old_param;
extern uint8_t *boot_splash_filedata;
extern bool enable_mlock;

View File

@ -2331,22 +2331,6 @@ SRST
pick the first available. (Since 2.9)
ERST
DEF("portrait", 0, QEMU_OPTION_portrait,
"-portrait rotate graphical output 90 deg left (only PXA LCD)\n",
QEMU_ARCH_ALL)
SRST
``-portrait``
Rotate graphical output 90 deg left (only PXA LCD).
ERST
DEF("rotate", HAS_ARG, QEMU_OPTION_rotate,
"-rotate <deg> rotate graphical output some deg left (only PXA LCD)\n",
QEMU_ARCH_ALL)
SRST
``-rotate deg``
Rotate graphical output some deg left (only PXA LCD).
ERST
DEF("vga", HAS_ARG, QEMU_OPTION_vga,
"-vga [std|cirrus|vmware|qxl|xenfb|tcx|cg3|virtio|none]\n"
" select video card type\n", QEMU_ARCH_ALL)

View File

@ -40,7 +40,6 @@ int autostart = 1;
int vga_interface_type = VGA_NONE;
bool vga_interface_created;
Chardev *parallel_hds[MAX_PARALLEL_PORTS];
int graphic_rotate;
QEMUOptionRom option_rom[MAX_OPTION_ROMS];
int nb_option_roms;
int old_param;

View File

@ -2910,17 +2910,6 @@ void qemu_init(int argc, char **argv)
nographic = true;
dpy.type = DISPLAY_TYPE_NONE;
break;
case QEMU_OPTION_portrait:
graphic_rotate = 90;
break;
case QEMU_OPTION_rotate:
graphic_rotate = strtol(optarg, (char **) &optarg, 10);
if (graphic_rotate != 0 && graphic_rotate != 90 &&
graphic_rotate != 180 && graphic_rotate != 270) {
error_report("only 90, 180, 270 deg rotation is available");
exit(1);
}
break;
case QEMU_OPTION_kernel:
qdict_put_str(machine_opts_dict, "kernel", optarg);
break;

42
tests/qtest/stm32l4x5.h Normal file
View File

@ -0,0 +1,42 @@
/*
* QTest testcase header for STM32L4X5 :
* used for consolidating common objects in stm32l4x5_*-test.c
*
* Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr>
* Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "libqtest.h"
/* copied from clock.h */
#define CLOCK_PERIOD_1SEC (1000000000llu << 32)
#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u)
/*
* MSI (4 MHz) is used as system clock source after startup
* from Reset.
* AHB, APB1 and APB2 prescalers are set to 1 at reset.
*/
#define SYSCLK_PERIOD CLOCK_PERIOD_FROM_HZ(4000000)
#define RCC_AHB2ENR 0x4002104C
#define RCC_APB1ENR1 0x40021058
#define RCC_APB1ENR2 0x4002105C
#define RCC_APB2ENR 0x40021060
static inline uint64_t get_clock_period(QTestState *qts, const char *path)
{
uint64_t clock_period = 0;
QDict *r;
r = qtest_qmp(qts, "{ 'execute': 'qom-get', 'arguments':"
" { 'path': %s, 'property': 'qtest-clock-period'} }", path);
g_assert_false(qdict_haskey(r, "error"));
clock_period = qdict_get_int(r, "return");
qobject_unref(r);
return clock_period;
}

View File

@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "libqtest-single.h"
#include "stm32l4x5.h"
#define GPIO_BASE_ADDR 0x48000000
#define GPIO_SIZE 0x400
@ -505,6 +506,26 @@ static void test_bsrr_brr(const void *data)
gpio_writel(gpio, ODR, reset(gpio, ODR));
}
static void test_clock_enable(void)
{
/*
* For each GPIO, enable its clock in RCC
* and check that its clock period changes to SYSCLK_PERIOD
*/
unsigned int gpio_id;
for (uint32_t gpio = GPIO_A; gpio <= GPIO_H; gpio += GPIO_B - GPIO_A) {
gpio_id = get_gpio_id(gpio);
g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c/clk",
gpio_id + 'a');
g_assert_cmpuint(get_clock_period(global_qtest, path), ==, 0);
/* Enable the gpio clock */
writel(RCC_AHB2ENR, readl(RCC_AHB2ENR) | (0x1 << gpio_id));
g_assert_cmpuint(get_clock_period(global_qtest, path), ==,
SYSCLK_PERIOD);
}
}
int main(int argc, char **argv)
{
int ret;
@ -556,6 +577,8 @@ int main(int argc, char **argv)
qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr2",
test_data(GPIO_D, 0),
test_bsrr_brr);
qtest_add_func("stm32l4x5/gpio/test_clock_enable",
test_clock_enable);
qtest_start("-machine b-l475e-iot01a");
ret = g_test_run();

View File

@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "libqtest-single.h"
#include "stm32l4x5.h"
#define SYSCFG_BASE_ADDR 0x40010000
#define SYSCFG_MEMRMP 0x00
@ -26,7 +27,9 @@
#define INVALID_ADDR 0x2C
/* SoC forwards GPIOs to SysCfg */
#define SYSCFG "/machine/soc"
#define SOC "/machine/soc"
#define SYSCFG "/machine/soc/syscfg"
#define SYSCFG_CLK "/machine/soc/syscfg/clk"
#define EXTI "/machine/soc/exti"
static void syscfg_writel(unsigned int offset, uint32_t value)
@ -41,7 +44,7 @@ static uint32_t syscfg_readl(unsigned int offset)
static void syscfg_set_irq(int num, int level)
{
qtest_set_irq_in(global_qtest, SYSCFG, NULL, num, level);
qtest_set_irq_in(global_qtest, SOC, NULL, num, level);
}
static void system_reset(void)
@ -301,6 +304,17 @@ static void test_irq_gpio_multiplexer(void)
syscfg_writel(SYSCFG_EXTICR1, 0x00000000);
}
static void test_clock_enable(void)
{
g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==, 0);
/* Enable SYSCFG clock */
writel(RCC_APB2ENR, readl(RCC_APB2ENR) | (0x1 << 0));
g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==,
SYSCLK_PERIOD);
}
int main(int argc, char **argv)
{
int ret;
@ -325,6 +339,8 @@ int main(int argc, char **argv)
test_irq_pin_multiplexer);
qtest_add_func("stm32l4x5/syscfg/test_irq_gpio_multiplexer",
test_irq_gpio_multiplexer);
qtest_add_func("stm32l4x5/syscfg/test_clock_enable",
test_clock_enable);
qtest_start("-machine b-l475e-iot01a");
ret = g_test_run();

View File

@ -12,6 +12,7 @@
#include "libqtest.h"
#include "hw/misc/stm32l4x5_rcc_internals.h"
#include "hw/registerfields.h"
#include "stm32l4x5.h"
#define RCC_BASE_ADDR 0x40021000
/* Use USART 1 ADDR, assume the others work the same */
@ -331,6 +332,32 @@ static void test_ack(void)
qtest_quit(qts);
}
static void check_clock(QTestState *qts, const char *path, uint32_t rcc_reg,
uint32_t reg_offset)
{
g_assert_cmpuint(get_clock_period(qts, path), ==, 0);
qtest_writel(qts, rcc_reg, qtest_readl(qts, rcc_reg) | (0x1 << reg_offset));
g_assert_cmpuint(get_clock_period(qts, path), ==, SYSCLK_PERIOD);
}
static void test_clock_enable(void)
{
/*
* For each USART device, enable its clock in RCC
* and check that its clock frequency is SYSCLK_PERIOD
*/
QTestState *qts = qtest_init("-M b-l475e-iot01a");
check_clock(qts, "machine/soc/usart[0]/clk", RCC_APB2ENR, 14);
check_clock(qts, "machine/soc/usart[1]/clk", RCC_APB1ENR1, 17);
check_clock(qts, "machine/soc/usart[2]/clk", RCC_APB1ENR1, 18);
check_clock(qts, "machine/soc/uart[0]/clk", RCC_APB1ENR1, 19);
check_clock(qts, "machine/soc/uart[1]/clk", RCC_APB1ENR1, 20);
check_clock(qts, "machine/soc/lpuart1/clk", RCC_APB1ENR2, 0);
qtest_quit(qts);
}
int main(int argc, char **argv)
{
int ret;
@ -344,6 +371,7 @@ int main(int argc, char **argv)
qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str);
qtest_add_func("stm32l4x5/usart/send_str", test_send_str);
qtest_add_func("stm32l4x5/usart/ack", test_ack);
qtest_add_func("stm32l4x5/usart/clock_enable", test_clock_enable);
ret = g_test_run();
return ret;

View File

@ -174,37 +174,6 @@ void qmp_input_send_event(const char *device,
qemu_input_event_sync();
}
static int qemu_input_transform_invert_abs_value(int value)
{
return (int64_t)INPUT_EVENT_ABS_MAX - value + INPUT_EVENT_ABS_MIN;
}
static void qemu_input_transform_abs_rotate(InputEvent *evt)
{
InputMoveEvent *move = evt->u.abs.data;
switch (graphic_rotate) {
case 90:
if (move->axis == INPUT_AXIS_X) {
move->axis = INPUT_AXIS_Y;
} else if (move->axis == INPUT_AXIS_Y) {
move->axis = INPUT_AXIS_X;
move->value = qemu_input_transform_invert_abs_value(move->value);
}
break;
case 180:
move->value = qemu_input_transform_invert_abs_value(move->value);
break;
case 270:
if (move->axis == INPUT_AXIS_X) {
move->axis = INPUT_AXIS_Y;
move->value = qemu_input_transform_invert_abs_value(move->value);
} else if (move->axis == INPUT_AXIS_Y) {
move->axis = INPUT_AXIS_X;
}
break;
}
}
static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt)
{
const char *name;
@ -340,11 +309,6 @@ void qemu_input_event_send_impl(QemuConsole *src, InputEvent *evt)
qemu_input_event_trace(src, evt);
/* pre processing */
if (graphic_rotate && (evt->type == INPUT_EVENT_KIND_ABS)) {
qemu_input_transform_abs_rotate(evt);
}
/* send event */
s = qemu_input_find_handler(1 << evt->type, src);
if (!s) {

View File

@ -17,6 +17,7 @@
#include "block/block.h"
#include "block/thread-pool.h"
#include "qemu/main-loop.h"
#include "qemu/lockcnt.h"
#include "qemu/rcu.h"
#include "qemu/rcu_queue.h"
#include "qemu/sockets.h"

View File

@ -18,6 +18,7 @@
#include "qemu/osdep.h"
#include "block/block.h"
#include "qemu/main-loop.h"
#include "qemu/lockcnt.h"
#include "qemu/queue.h"
#include "qemu/sockets.h"
#include "qapi/error.h"

View File

@ -30,6 +30,7 @@
#include "block/graph-lock.h"
#include "qemu/main-loop.h"
#include "qemu/atomic.h"
#include "qemu/lockcnt.h"
#include "qemu/rcu_queue.h"
#include "block/raw-aio.h"
#include "qemu/coroutine_int.h"

View File

@ -5,6 +5,7 @@
#include "qemu/osdep.h"
#include <sys/epoll.h>
#include "qemu/lockcnt.h"
#include "qemu/rcu_queue.h"
#include "aio-posix.h"

View File

@ -7,6 +7,7 @@
* Paolo Bonzini <pbonzini@redhat.com>
*/
#include "qemu/osdep.h"
#include "qemu/lockcnt.h"
#include "qemu/thread.h"
#include "qemu/atomic.h"
#include "trace.h"