target-arm queue:
* hw/core/or-irq: Fix incorrect assert forbidding num-lines == MAX_OR_LINES * target/arm/arm-semi: Don't let the guest close stdin/stdout/stderr * aspeed: some minor bugfixes * aspeed: add eMMC controller model for AST2600 SoC * hw/arm/raspi: Remove obsolete use of -smp to set the soc 'enabled-cpus' * New 3-phase reset API for device models * hw/intc/arm_gicv3_kvm: Stop wrongly programming GICR_PENDBASER.PTZ bit * Arm KVM: stop/restart the guest counter when the VM is stopped and started -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAl4zAPUZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3pcFD/9eHf8S9/yBCaNJwqhjXw1d 09B+PMLnImek1BqhgKlOxpK/9W7JToo5gmR2fauWzeY9Yo2uOcpyjq+DKFJyui7O /gYeM4UP85lnmouqk0lzuEo9vFIUR2JVdQemgt+cA+7nyMtoIjRrlgzd5c4RA8D1 fbRwVk0xhInVSl5N7ZqHFAkd0Udzokm/C6F7qOgPhY5AHrF908cC+4VXAzQUT+EX bLDkhyuS4fSNOzrhutb+p5LYsT/YjJzk/0uY44252lDy1IRijf2gwrMDO6usWHF4 cmjkMcJ+QAIR4k57D9TXBA5UmSCESMePj/7J6z0w4oXGhyURVs8JKi6DYjatkvBJ bme/2wiVQVmPU+KUIHxHlppN2NRUKf14hwtHUmRcMa81Au6lRbWXF3NEEt4kwYhL qvveyNMk+eujC5xt1ukJ4Uer45fvJMQE/ouSg1YFGZkSrMkVAycTZQDkiQbB2BlN k3gxQiGWZThjhr0UkwbJbSp5fw5f/JT/DajObyfnvuXMIOUuv+yLfj5LmKvzV8cm w7CAJVfmpJlVnDDInWT/G16I0QuPrWw2phx8/G1bq2Fek8etc0zaf1e08HAm7c/w 81L1Uz5Zuc3My6RJAu95chhse4a3BZzuu/1xC7ETP7AfygsQP7nN2ftkBO+983Tn wL7P3mqbuS5zTx4ZZd50Ow== =Vbu6 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20200130' into staging target-arm queue: * hw/core/or-irq: Fix incorrect assert forbidding num-lines == MAX_OR_LINES * target/arm/arm-semi: Don't let the guest close stdin/stdout/stderr * aspeed: some minor bugfixes * aspeed: add eMMC controller model for AST2600 SoC * hw/arm/raspi: Remove obsolete use of -smp to set the soc 'enabled-cpus' * New 3-phase reset API for device models * hw/intc/arm_gicv3_kvm: Stop wrongly programming GICR_PENDBASER.PTZ bit * Arm KVM: stop/restart the guest counter when the VM is stopped and started # gpg: Signature made Thu 30 Jan 2020 16:14:45 GMT # 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] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20200130: (26 commits) target/arm/cpu: Add the kvm-no-adjvtime CPU property target/arm/kvm: Implement virtual time adjustment tests/arm-cpu-features: Check feature default values target/arm/kvm64: kvm64 cpus have timer registers hw/arm/virt: Add missing 5.0 options call to 4.2 options target/arm/kvm: trivial: Clean up header documentation hw/intc/arm_gicv3_kvm: Stop wrongly programming GICR_PENDBASER.PTZ bit hw/s390x/ipl: replace deprecated qdev_reset_all registration vl: replace deprecated qbus_reset_all registration docs/devel/reset.rst: add doc about Resettable interface hw/core: deprecate old reset functions and introduce new ones hw/core/qdev: update hotplug reset regarding resettable hw/core/qdev: handle parent bus change regarding resettable hw/core/resettable: add support for changing parent hw/core: add Resettable support to BusClass and DeviceClass hw/core: create Resettable QOM interface hw/core/qdev: add trace events to help with resettable transition add device_legacy_reset function to prepare for reset api change hw/arm/raspi: Remove obsolete use of -smp to set the soc 'enabled-cpus' misc/pca9552: Add qom set and get ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
928173659d
@ -31,7 +31,9 @@ supporting the feature or only supporting the feature under certain
|
||||
configurations. For example, the `aarch64` CPU feature, which, when
|
||||
disabled, enables the optional AArch32 CPU feature, is only supported
|
||||
when using the KVM accelerator and when running on a host CPU type that
|
||||
supports the feature.
|
||||
supports the feature. While `aarch64` currently only works with KVM,
|
||||
it could work with TCG. CPU features that are specific to KVM are
|
||||
prefixed with "kvm-" and are described in "KVM VCPU Features".
|
||||
|
||||
CPU Feature Probing
|
||||
===================
|
||||
@ -171,6 +173,39 @@ disabling many SVE vector lengths would be quite verbose, the `sve<N>` CPU
|
||||
properties have special semantics (see "SVE CPU Property Parsing
|
||||
Semantics").
|
||||
|
||||
KVM VCPU Features
|
||||
=================
|
||||
|
||||
KVM VCPU features are CPU features that are specific to KVM, such as
|
||||
paravirt features or features that enable CPU virtualization extensions.
|
||||
The features' CPU properties are only available when KVM is enabled and
|
||||
are named with the prefix "kvm-". KVM VCPU features may be probed,
|
||||
enabled, and disabled in the same way as other CPU features. Below is
|
||||
the list of KVM VCPU features and their descriptions.
|
||||
|
||||
kvm-no-adjvtime By default kvm-no-adjvtime is disabled. This
|
||||
means that by default the virtual time
|
||||
adjustment is enabled (vtime is *not not*
|
||||
adjusted).
|
||||
|
||||
When virtual time adjustment is enabled each
|
||||
time the VM transitions back to running state
|
||||
the VCPU's virtual counter is updated to ensure
|
||||
stopped time is not counted. This avoids time
|
||||
jumps surprising guest OSes and applications,
|
||||
as long as they use the virtual counter for
|
||||
timekeeping. However it has the side effect of
|
||||
the virtual and physical counters diverging.
|
||||
All timekeeping based on the virtual counter
|
||||
will appear to lag behind any timekeeping that
|
||||
does not subtract VM stopped time. The guest
|
||||
may resynchronize its virtual counter with
|
||||
other time sources as needed.
|
||||
|
||||
Enable kvm-no-adjvtime to disable virtual time
|
||||
adjustment, also restoring the legacy (pre-5.0)
|
||||
behavior.
|
||||
|
||||
SVE CPU Properties
|
||||
==================
|
||||
|
||||
|
@ -24,3 +24,4 @@ Contents:
|
||||
tcg
|
||||
tcg-plugins
|
||||
bitops
|
||||
reset
|
||||
|
289
docs/devel/reset.rst
Normal file
289
docs/devel/reset.rst
Normal file
@ -0,0 +1,289 @@
|
||||
|
||||
=======================================
|
||||
Reset in QEMU: the Resettable interface
|
||||
=======================================
|
||||
|
||||
The reset of qemu objects is handled using the resettable interface declared
|
||||
in ``include/hw/resettable.h``.
|
||||
|
||||
This interface allows objects to be grouped (on a tree basis); so that the
|
||||
whole group can be reset consistently. Each individual member object does not
|
||||
have to care about others; in particular, problems of order (which object is
|
||||
reset first) are addressed.
|
||||
|
||||
As of now DeviceClass and BusClass implement this interface.
|
||||
|
||||
|
||||
Triggering reset
|
||||
----------------
|
||||
|
||||
This section documents the APIs which "users" of a resettable object should use
|
||||
to control it. All resettable control functions must be called while holding
|
||||
the iothread lock.
|
||||
|
||||
You can apply a reset to an object using ``resettable_assert_reset()``. You need
|
||||
to call ``resettable_release_reset()`` to release the object from reset. To
|
||||
instantly reset an object, without keeping it in reset state, just call
|
||||
``resettable_reset()``. These functions take two parameters: a pointer to the
|
||||
object to reset and a reset type.
|
||||
|
||||
Several types of reset will be supported. For now only cold reset is defined;
|
||||
others may be added later. The Resettable interface handles reset types with an
|
||||
enum:
|
||||
|
||||
``RESET_TYPE_COLD``
|
||||
Cold reset is supported by every resettable object. In QEMU, it means we reset
|
||||
to the initial state corresponding to the start of QEMU; this might differ
|
||||
from what is a real hardware cold reset. It differs from other resets (like
|
||||
warm or bus resets) which may keep certain parts untouched.
|
||||
|
||||
Calling ``resettable_reset()`` is equivalent to calling
|
||||
``resettable_assert_reset()`` then ``resettable_release_reset()``. It is
|
||||
possible to interleave multiple calls to these three functions. There may
|
||||
be several reset sources/controllers of a given object. The interface handles
|
||||
everything and the different reset controllers do not need to know anything
|
||||
about each others. The object will leave reset state only when each other
|
||||
controllers end their reset operation. This point is handled internally by
|
||||
maintaining a count of in-progress resets; it is crucial to call
|
||||
``resettable_release_reset()`` one time and only one time per
|
||||
``resettable_assert_reset()`` call.
|
||||
|
||||
For now migration of a device or bus in reset is not supported. Care must be
|
||||
taken not to delay ``resettable_release_reset()`` after its
|
||||
``resettable_assert_reset()`` counterpart.
|
||||
|
||||
Note that, since resettable is an interface, the API takes a simple Object as
|
||||
parameter. Still, it is a programming error to call a resettable function on a
|
||||
non-resettable object and it will trigger a run time assert error. Since most
|
||||
calls to resettable interface are done through base class functions, such an
|
||||
error is not likely to happen.
|
||||
|
||||
For Devices and Buses, the following helper functions exist:
|
||||
|
||||
- ``device_cold_reset()``
|
||||
- ``bus_cold_reset()``
|
||||
|
||||
These are simple wrappers around resettable_reset() function; they only cast the
|
||||
Device or Bus into an Object and pass the cold reset type. When possible
|
||||
prefer to use these functions instead of ``resettable_reset()``.
|
||||
|
||||
Device and bus functions co-exist because there can be semantic differences
|
||||
between resetting a bus and resetting the controller bridge which owns it.
|
||||
For example, consider a SCSI controller. Resetting the controller puts all
|
||||
its registers back to what reset state was as well as reset everything on the
|
||||
SCSI bus, whereas resetting just the SCSI bus only resets everything that's on
|
||||
it but not the controller.
|
||||
|
||||
|
||||
Multi-phase mechanism
|
||||
---------------------
|
||||
|
||||
This section documents the internals of the resettable interface.
|
||||
|
||||
The resettable interface uses a multi-phase system to relieve objects and
|
||||
machines from reset ordering problems. To address this, the reset operation
|
||||
of an object is split into three well defined phases.
|
||||
|
||||
When resetting several objects (for example the whole machine at simulation
|
||||
startup), all first phases of all objects are executed, then all second phases
|
||||
and then all third phases.
|
||||
|
||||
The three phases are:
|
||||
|
||||
1. The **enter** phase is executed when the object enters reset. It resets only
|
||||
local state of the object; it must not do anything that has a side-effect
|
||||
on other objects, such as raising or lowering a qemu_irq line or reading or
|
||||
writing guest memory.
|
||||
|
||||
2. The **hold** phase is executed for entry into reset, once every object in the
|
||||
group which is being reset has had its *enter* phase executed. At this point
|
||||
devices can do actions that affect other objects.
|
||||
|
||||
3. The **exit** phase is executed when the object leaves the reset state.
|
||||
Actions affecting other objects are permitted.
|
||||
|
||||
As said in previous section, the interface maintains a count of reset. This
|
||||
count is used to ensure phases are executed only when required. *enter* and
|
||||
*hold* phases are executed only when asserting reset for the first time
|
||||
(if an object is already in reset state when calling
|
||||
``resettable_assert_reset()`` or ``resettable_reset()``, they are not
|
||||
executed).
|
||||
The *exit* phase is executed only when the last reset operation ends. Therefore
|
||||
the object does not need to care how many of reset controllers it has and how
|
||||
many of them have started a reset.
|
||||
|
||||
|
||||
Handling reset in a resettable object
|
||||
-------------------------------------
|
||||
|
||||
This section documents the APIs that an implementation of a resettable object
|
||||
must provide and what functions it has access to. It is intended for people
|
||||
who want to implement or convert a class which has the resettable interface;
|
||||
for example when specializing an existing device or bus.
|
||||
|
||||
Methods to implement
|
||||
....................
|
||||
|
||||
Three methods should be defined or left empty. Each method corresponds to a
|
||||
phase of the reset; they are name ``phases.enter()``, ``phases.hold()`` and
|
||||
``phases.exit()``. They all take the object as parameter. The *enter* method
|
||||
also take the reset type as second parameter.
|
||||
|
||||
When extending an existing class, these methods may need to be extended too.
|
||||
The ``resettable_class_set_parent_phases()`` class function may be used to
|
||||
backup parent class methods.
|
||||
|
||||
Here follows an example to implement reset for a Device which sets an IO while
|
||||
in reset.
|
||||
|
||||
::
|
||||
|
||||
static void mydev_reset_enter(Object *obj, ResetType type)
|
||||
{
|
||||
MyDevClass *myclass = MYDEV_GET_CLASS(obj);
|
||||
MyDevState *mydev = MYDEV(obj);
|
||||
/* call parent class enter phase */
|
||||
if (myclass->parent_phases.enter) {
|
||||
myclass->parent_phases.enter(obj, type);
|
||||
}
|
||||
/* initialize local state only */
|
||||
mydev->var = 0;
|
||||
}
|
||||
|
||||
static void mydev_reset_hold(Object *obj)
|
||||
{
|
||||
MyDevClass *myclass = MYDEV_GET_CLASS(obj);
|
||||
MyDevState *mydev = MYDEV(obj);
|
||||
/* call parent class hold phase */
|
||||
if (myclass->parent_phases.hold) {
|
||||
myclass->parent_phases.hold(obj);
|
||||
}
|
||||
/* set an IO */
|
||||
qemu_set_irq(mydev->irq, 1);
|
||||
}
|
||||
|
||||
static void mydev_reset_exit(Object *obj)
|
||||
{
|
||||
MyDevClass *myclass = MYDEV_GET_CLASS(obj);
|
||||
MyDevState *mydev = MYDEV(obj);
|
||||
/* call parent class exit phase */
|
||||
if (myclass->parent_phases.exit) {
|
||||
myclass->parent_phases.exit(obj);
|
||||
}
|
||||
/* clear an IO */
|
||||
qemu_set_irq(mydev->irq, 0);
|
||||
}
|
||||
|
||||
typedef struct MyDevClass {
|
||||
MyParentClass parent_class;
|
||||
/* to store eventual parent reset methods */
|
||||
ResettablePhases parent_phases;
|
||||
} MyDevClass;
|
||||
|
||||
static void mydev_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
MyDevClass *myclass = MYDEV_CLASS(class);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(class);
|
||||
resettable_class_set_parent_reset_phases(rc,
|
||||
mydev_reset_enter,
|
||||
mydev_reset_hold,
|
||||
mydev_reset_exit,
|
||||
&myclass->parent_phases);
|
||||
}
|
||||
|
||||
In the above example, we override all three phases. It is possible to override
|
||||
only some of them by passing NULL instead of a function pointer to
|
||||
``resettable_class_set_parent_reset_phases()``. For example, the following will
|
||||
only override the *enter* phase and leave *hold* and *exit* untouched::
|
||||
|
||||
resettable_class_set_parent_reset_phases(rc, mydev_reset_enter,
|
||||
NULL, NULL,
|
||||
&myclass->parent_phases);
|
||||
|
||||
This is equivalent to providing a trivial implementation of the hold and exit
|
||||
phases which does nothing but call the parent class's implementation of the
|
||||
phase.
|
||||
|
||||
Polling the reset state
|
||||
.......................
|
||||
|
||||
Resettable interface provides the ``resettable_is_in_reset()`` function.
|
||||
This function returns true if the object parameter is currently under reset.
|
||||
|
||||
An object is under reset from the beginning of the *init* phase to the end of
|
||||
the *exit* phase. During all three phases, the function will return that the
|
||||
object is in reset.
|
||||
|
||||
This function may be used if the object behavior has to be adapted
|
||||
while in reset state. For example if a device has an irq input,
|
||||
it will probably need to ignore it while in reset; then it can for
|
||||
example check the reset state at the beginning of the irq callback.
|
||||
|
||||
Note that until migration of the reset state is supported, an object
|
||||
should not be left in reset. So apart from being currently executing
|
||||
one of the reset phases, the only cases when this function will return
|
||||
true is if an external interaction (like changing an io) is made during
|
||||
*hold* or *exit* phase of another object in the same reset group.
|
||||
|
||||
Helpers ``device_is_in_reset()`` and ``bus_is_in_reset()`` are also provided
|
||||
for devices and buses and should be preferred.
|
||||
|
||||
|
||||
Base class handling of reset
|
||||
----------------------------
|
||||
|
||||
This section documents parts of the reset mechanism that you only need to know
|
||||
about if you are extending it to work with a new base class other than
|
||||
DeviceClass or BusClass, or maintaining the existing code in those classes. Most
|
||||
people can ignore it.
|
||||
|
||||
Methods to implement
|
||||
....................
|
||||
|
||||
There are two other methods that need to exist in a class implementing the
|
||||
interface: ``get_state()`` and ``child_foreach()``.
|
||||
|
||||
``get_state()`` is simple. *resettable* is an interface and, as a consequence,
|
||||
does not have any class state structure. But in order to factorize the code, we
|
||||
need one. This method must return a pointer to ``ResettableState`` structure.
|
||||
The structure must be allocated by the base class; preferably it should be
|
||||
located inside the object instance structure.
|
||||
|
||||
``child_foreach()`` is more complex. It should execute the given callback on
|
||||
every reset child of the given resettable object. All children must be
|
||||
resettable too. Additional parameters (a reset type and an opaque pointer) must
|
||||
be passed to the callback too.
|
||||
|
||||
In ``DeviceClass`` and ``BusClass`` the ``ResettableState`` is located
|
||||
``DeviceState`` and ``BusState`` structure. ``child_foreach()`` is implemented
|
||||
to follow the bus hierarchy; for a bus, it calls the function on every child
|
||||
device; for a device, it calls the function on every bus child. When we reset
|
||||
the main system bus, we reset the whole machine bus tree.
|
||||
|
||||
Changing a resettable parent
|
||||
............................
|
||||
|
||||
One thing which should be taken care of by the base class is handling reset
|
||||
hierarchy changes.
|
||||
|
||||
The reset hierarchy is supposed to be static and built during machine creation.
|
||||
But there are actually some exceptions. To cope with this, the resettable API
|
||||
provides ``resettable_change_parent()``. This function allows to set, update or
|
||||
remove the parent of a resettable object after machine creation is done. As
|
||||
parameters, it takes the object being moved, the old parent if any and the new
|
||||
parent if any.
|
||||
|
||||
This function can be used at any time when not in a reset operation. During
|
||||
a reset operation it must be used only in *hold* phase. Using it in *enter* or
|
||||
*exit* phase is an error.
|
||||
Also it should not be used during machine creation, although it is harmless to
|
||||
do so: the function is a no-op as long as old and new parent are NULL or not
|
||||
in reset.
|
||||
|
||||
There is currently 2 cases where this function is used:
|
||||
|
||||
1. *device hotplug*; it means a new device is introduced on a live bus.
|
||||
|
||||
2. *hot bus change*; it means an existing live device is added, moved or
|
||||
removed in the bus hierarchy. At the moment, it occurs only in the raspi
|
||||
machines for changing the sdbus used by sd card.
|
@ -171,6 +171,19 @@ static void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype,
|
||||
}
|
||||
}
|
||||
|
||||
static void sdhci_attach_drive(SDHCIState *sdhci, DriveInfo *dinfo)
|
||||
{
|
||||
DeviceState *card;
|
||||
|
||||
card = qdev_create(qdev_get_child_bus(DEVICE(sdhci), "sd-bus"),
|
||||
TYPE_SD_CARD);
|
||||
if (dinfo) {
|
||||
qdev_prop_set_drive(card, "drive", blk_by_legacy_dinfo(dinfo),
|
||||
&error_fatal);
|
||||
}
|
||||
object_property_set_bool(OBJECT(card), true, "realized", &error_fatal);
|
||||
}
|
||||
|
||||
static void aspeed_machine_init(MachineState *machine)
|
||||
{
|
||||
AspeedBoardState *bmc;
|
||||
@ -248,11 +261,18 @@ static void aspeed_machine_init(MachineState *machine)
|
||||
* SoC and 128MB for the AST2500 SoC, which is twice as big as
|
||||
* needed by the flash modules of the Aspeed machines.
|
||||
*/
|
||||
memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom",
|
||||
fl->size, &error_abort);
|
||||
memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR,
|
||||
boot_rom);
|
||||
write_boot_rom(drive0, FIRMWARE_ADDR, fl->size, &error_abort);
|
||||
if (ASPEED_MACHINE(machine)->mmio_exec) {
|
||||
memory_region_init_alias(boot_rom, OBJECT(bmc), "aspeed.boot_rom",
|
||||
&fl->mmio, 0, fl->size);
|
||||
memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR,
|
||||
boot_rom);
|
||||
} else {
|
||||
memory_region_init_rom(boot_rom, OBJECT(bmc), "aspeed.boot_rom",
|
||||
fl->size, &error_abort);
|
||||
memory_region_add_subregion(get_system_memory(), FIRMWARE_ADDR,
|
||||
boot_rom);
|
||||
write_boot_rom(drive0, FIRMWARE_ADDR, fl->size, &error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
aspeed_board_binfo.ram_size = ram_size;
|
||||
@ -263,17 +283,12 @@ static void aspeed_machine_init(MachineState *machine)
|
||||
amc->i2c_init(bmc);
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bmc->soc.sdhci.slots); i++) {
|
||||
SDHCIState *sdhci = &bmc->soc.sdhci.slots[i];
|
||||
DriveInfo *dinfo = drive_get_next(IF_SD);
|
||||
BlockBackend *blk;
|
||||
DeviceState *card;
|
||||
for (i = 0; i < bmc->soc.sdhci.num_slots; i++) {
|
||||
sdhci_attach_drive(&bmc->soc.sdhci.slots[i], drive_get_next(IF_SD));
|
||||
}
|
||||
|
||||
blk = dinfo ? blk_by_legacy_dinfo(dinfo) : NULL;
|
||||
card = qdev_create(qdev_get_child_bus(DEVICE(sdhci), "sd-bus"),
|
||||
TYPE_SD_CARD);
|
||||
qdev_prop_set_drive(card, "drive", blk, &error_fatal);
|
||||
object_property_set_bool(OBJECT(card), true, "realized", &error_fatal);
|
||||
if (bmc->soc.emmc.num_slots) {
|
||||
sdhci_attach_drive(&bmc->soc.emmc.slots[0], drive_get_next(IF_SD));
|
||||
}
|
||||
|
||||
arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo);
|
||||
@ -391,6 +406,30 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc)
|
||||
/* Bus 11: TODO ucd90160@64 */
|
||||
}
|
||||
|
||||
static bool aspeed_get_mmio_exec(Object *obj, Error **errp)
|
||||
{
|
||||
return ASPEED_MACHINE(obj)->mmio_exec;
|
||||
}
|
||||
|
||||
static void aspeed_set_mmio_exec(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
ASPEED_MACHINE(obj)->mmio_exec = value;
|
||||
}
|
||||
|
||||
static void aspeed_machine_instance_init(Object *obj)
|
||||
{
|
||||
ASPEED_MACHINE(obj)->mmio_exec = false;
|
||||
}
|
||||
|
||||
static void aspeed_machine_class_props_init(ObjectClass *oc)
|
||||
{
|
||||
object_class_property_add_bool(oc, "execute-in-place",
|
||||
aspeed_get_mmio_exec,
|
||||
aspeed_set_mmio_exec, &error_abort);
|
||||
object_class_property_set_description(oc, "execute-in-place",
|
||||
"boot directly from CE0 flash device", &error_abort);
|
||||
}
|
||||
|
||||
static void aspeed_machine_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
MachineClass *mc = MACHINE_CLASS(oc);
|
||||
@ -400,6 +439,8 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data)
|
||||
mc->no_floppy = 1;
|
||||
mc->no_cdrom = 1;
|
||||
mc->no_parallel = 1;
|
||||
|
||||
aspeed_machine_class_props_init(oc);
|
||||
}
|
||||
|
||||
static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data)
|
||||
@ -542,6 +583,7 @@ static const TypeInfo aspeed_machine_types[] = {
|
||||
.name = TYPE_ASPEED_MACHINE,
|
||||
.parent = TYPE_MACHINE,
|
||||
.instance_size = sizeof(AspeedMachine),
|
||||
.instance_init = aspeed_machine_instance_init,
|
||||
.class_size = sizeof(AspeedMachineClass),
|
||||
.class_init = aspeed_machine_class_init,
|
||||
.abstract = true,
|
||||
|
@ -46,6 +46,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
|
||||
[ASPEED_ADC] = 0x1E6E9000,
|
||||
[ASPEED_VIDEO] = 0x1E700000,
|
||||
[ASPEED_SDHCI] = 0x1E740000,
|
||||
[ASPEED_EMMC] = 0x1E750000,
|
||||
[ASPEED_GPIO] = 0x1E780000,
|
||||
[ASPEED_GPIO_1_8V] = 0x1E780800,
|
||||
[ASPEED_RTC] = 0x1E781000,
|
||||
@ -64,6 +65,7 @@ static const hwaddr aspeed_soc_ast2600_memmap[] = {
|
||||
|
||||
#define ASPEED_SOC_AST2600_MAX_IRQ 128
|
||||
|
||||
/* Shared Peripheral Interrupt values below are offset by -32 from datasheet */
|
||||
static const int aspeed_soc_ast2600_irqmap[] = {
|
||||
[ASPEED_UART1] = 47,
|
||||
[ASPEED_UART2] = 48,
|
||||
@ -77,6 +79,7 @@ static const int aspeed_soc_ast2600_irqmap[] = {
|
||||
[ASPEED_ADC] = 78,
|
||||
[ASPEED_XDMA] = 6,
|
||||
[ASPEED_SDHCI] = 43,
|
||||
[ASPEED_EMMC] = 15,
|
||||
[ASPEED_GPIO] = 40,
|
||||
[ASPEED_GPIO_1_8V] = 11,
|
||||
[ASPEED_RTC] = 13,
|
||||
@ -196,14 +199,26 @@ static void aspeed_soc_ast2600_init(Object *obj)
|
||||
sysbus_init_child_obj(obj, "gpio_1_8v", OBJECT(&s->gpio_1_8v),
|
||||
sizeof(s->gpio_1_8v), typename);
|
||||
|
||||
sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci),
|
||||
TYPE_ASPEED_SDHCI);
|
||||
sysbus_init_child_obj(obj, "sd-controller", OBJECT(&s->sdhci),
|
||||
sizeof(s->sdhci), TYPE_ASPEED_SDHCI);
|
||||
|
||||
object_property_set_int(OBJECT(&s->sdhci), 2, "num-slots", &error_abort);
|
||||
|
||||
/* Init sd card slot class here so that they're under the correct parent */
|
||||
for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) {
|
||||
sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]),
|
||||
sysbus_init_child_obj(obj, "sd-controller.sdhci[*]",
|
||||
OBJECT(&s->sdhci.slots[i]),
|
||||
sizeof(s->sdhci.slots[i]), TYPE_SYSBUS_SDHCI);
|
||||
}
|
||||
|
||||
sysbus_init_child_obj(obj, "emmc-controller", OBJECT(&s->emmc),
|
||||
sizeof(s->emmc), TYPE_ASPEED_SDHCI);
|
||||
|
||||
object_property_set_int(OBJECT(&s->emmc), 1, "num-slots", &error_abort);
|
||||
|
||||
sysbus_init_child_obj(obj, "emmc-controller.sdhci",
|
||||
OBJECT(&s->emmc.slots[0]), sizeof(s->emmc.slots[0]),
|
||||
TYPE_SYSBUS_SDHCI);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -495,6 +510,16 @@ static void aspeed_soc_ast2600_realize(DeviceState *dev, Error **errp)
|
||||
sc->memmap[ASPEED_SDHCI]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->sdhci), 0,
|
||||
aspeed_soc_get_irq(s, ASPEED_SDHCI));
|
||||
|
||||
/* eMMC */
|
||||
object_property_set_bool(OBJECT(&s->emmc), true, "realized", &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
sysbus_mmio_map(SYS_BUS_DEVICE(&s->emmc), 0, sc->memmap[ASPEED_EMMC]);
|
||||
sysbus_connect_irq(SYS_BUS_DEVICE(&s->emmc), 0,
|
||||
aspeed_soc_get_irq(s, ASPEED_EMMC));
|
||||
}
|
||||
|
||||
static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data)
|
||||
|
@ -209,6 +209,8 @@ static void aspeed_soc_init(Object *obj)
|
||||
sysbus_init_child_obj(obj, "sdc", OBJECT(&s->sdhci), sizeof(s->sdhci),
|
||||
TYPE_ASPEED_SDHCI);
|
||||
|
||||
object_property_set_int(OBJECT(&s->sdhci), 2, "num-slots", &error_abort);
|
||||
|
||||
/* Init sd card slot class here so that they're under the correct parent */
|
||||
for (i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) {
|
||||
sysbus_init_child_obj(obj, "sdhci[*]", OBJECT(&s->sdhci.slots[i]),
|
||||
|
@ -192,8 +192,6 @@ static void raspi_init(MachineState *machine, int version)
|
||||
/* Setup the SOC */
|
||||
object_property_add_const_link(OBJECT(&s->soc), "ram", OBJECT(&s->ram),
|
||||
&error_abort);
|
||||
object_property_set_int(OBJECT(&s->soc), machine->smp.cpus, "enabled-cpus",
|
||||
&error_abort);
|
||||
int board_rev = version == 3 ? 0xa02082 : 0xa21041;
|
||||
object_property_set_int(OBJECT(&s->soc), board_rev, "board-rev",
|
||||
&error_abort);
|
||||
|
@ -1663,6 +1663,11 @@ static void machvirt_init(MachineState *machine)
|
||||
}
|
||||
}
|
||||
|
||||
if (vmc->kvm_no_adjvtime &&
|
||||
object_property_find(cpuobj, "kvm-no-adjvtime", NULL)) {
|
||||
object_property_set_bool(cpuobj, true, "kvm-no-adjvtime", NULL);
|
||||
}
|
||||
|
||||
if (vmc->no_pmu && object_property_find(cpuobj, "pmu", NULL)) {
|
||||
object_property_set_bool(cpuobj, false, "pmu", NULL);
|
||||
}
|
||||
@ -2153,7 +2158,11 @@ DEFINE_VIRT_MACHINE_AS_LATEST(5, 0)
|
||||
|
||||
static void virt_machine_4_2_options(MachineClass *mc)
|
||||
{
|
||||
VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
|
||||
|
||||
virt_machine_5_0_options(mc);
|
||||
compat_props_add(mc->compat_props, hw_compat_4_2, hw_compat_4_2_len);
|
||||
vmc->kvm_no_adjvtime = true;
|
||||
}
|
||||
DEFINE_VIRT_MACHINE(4, 2)
|
||||
|
||||
|
@ -1087,7 +1087,7 @@ static void intel_hda_reset(DeviceState *dev)
|
||||
QTAILQ_FOREACH(kid, &d->codecs.qbus.children, sibling) {
|
||||
DeviceState *qdev = kid->child;
|
||||
cdev = HDA_CODEC_DEVICE(qdev);
|
||||
device_reset(DEVICE(cdev));
|
||||
device_legacy_reset(DEVICE(cdev));
|
||||
d->state_sts |= (1 << cdev->cad);
|
||||
}
|
||||
intel_hda_update_irq(d);
|
||||
|
@ -2,6 +2,7 @@
|
||||
common-obj-y += qdev.o qdev-properties.o
|
||||
common-obj-y += bus.o
|
||||
common-obj-y += cpu.o
|
||||
common-obj-y += resettable.o
|
||||
common-obj-y += hotplug.o
|
||||
common-obj-y += vmstate-if.o
|
||||
# irq.o needed for qdev GPIO handling:
|
||||
|
102
hw/core/bus.c
102
hw/core/bus.c
@ -68,6 +68,33 @@ int qbus_walk_children(BusState *bus,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void bus_cold_reset(BusState *bus)
|
||||
{
|
||||
resettable_reset(OBJECT(bus), RESET_TYPE_COLD);
|
||||
}
|
||||
|
||||
bool bus_is_in_reset(BusState *bus)
|
||||
{
|
||||
return resettable_is_in_reset(OBJECT(bus));
|
||||
}
|
||||
|
||||
static ResettableState *bus_get_reset_state(Object *obj)
|
||||
{
|
||||
BusState *bus = BUS(obj);
|
||||
return &bus->reset;
|
||||
}
|
||||
|
||||
static void bus_reset_child_foreach(Object *obj, ResettableChildCallback cb,
|
||||
void *opaque, ResetType type)
|
||||
{
|
||||
BusState *bus = BUS(obj);
|
||||
BusChild *kid;
|
||||
|
||||
QTAILQ_FOREACH(kid, &bus->children, sibling) {
|
||||
cb(OBJECT(kid->child), opaque, type);
|
||||
}
|
||||
}
|
||||
|
||||
static void qbus_realize(BusState *bus, DeviceState *parent, const char *name)
|
||||
{
|
||||
const char *typename = object_get_typename(OBJECT(bus));
|
||||
@ -199,12 +226,83 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev)
|
||||
return g_strdup(object_get_typename(OBJECT(dev)));
|
||||
}
|
||||
|
||||
/**
|
||||
* bus_phases_reset:
|
||||
* Transition reset method for buses to allow moving
|
||||
* smoothly from legacy reset method to multi-phases
|
||||
*/
|
||||
static void bus_phases_reset(BusState *bus)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(bus);
|
||||
|
||||
if (rc->phases.enter) {
|
||||
rc->phases.enter(OBJECT(bus), RESET_TYPE_COLD);
|
||||
}
|
||||
if (rc->phases.hold) {
|
||||
rc->phases.hold(OBJECT(bus));
|
||||
}
|
||||
if (rc->phases.exit) {
|
||||
rc->phases.exit(OBJECT(bus));
|
||||
}
|
||||
}
|
||||
|
||||
static void bus_transitional_reset(Object *obj)
|
||||
{
|
||||
BusClass *bc = BUS_GET_CLASS(obj);
|
||||
|
||||
/*
|
||||
* This will call either @bus_phases_reset (for multi-phases transitioned
|
||||
* buses) or a bus's specific method for not-yet transitioned buses.
|
||||
* In both case, it does not reset children.
|
||||
*/
|
||||
if (bc->reset) {
|
||||
bc->reset(BUS(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* bus_get_transitional_reset:
|
||||
* check if the bus's class is ready for multi-phase
|
||||
*/
|
||||
static ResettableTrFunction bus_get_transitional_reset(Object *obj)
|
||||
{
|
||||
BusClass *dc = BUS_GET_CLASS(obj);
|
||||
if (dc->reset != bus_phases_reset) {
|
||||
/*
|
||||
* dc->reset has been overridden by a subclass,
|
||||
* the bus is not ready for multi phase yet.
|
||||
*/
|
||||
return bus_transitional_reset;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bus_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
BusClass *bc = BUS_CLASS(class);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(class);
|
||||
|
||||
class->unparent = bus_unparent;
|
||||
bc->get_fw_dev_path = default_bus_get_fw_dev_path;
|
||||
|
||||
rc->get_state = bus_get_reset_state;
|
||||
rc->child_foreach = bus_reset_child_foreach;
|
||||
|
||||
/*
|
||||
* @bus_phases_reset is put as the default reset method below, allowing
|
||||
* to do the multi-phase transition from base classes to leaf classes. It
|
||||
* allows a legacy-reset Bus class to extend a multi-phases-reset
|
||||
* Bus class for the following reason:
|
||||
* + If a base class B has been moved to multi-phase, then it does not
|
||||
* override this default reset method and may have defined phase methods.
|
||||
* + A child class C (extending class B) which uses
|
||||
* bus_class_set_parent_reset() (or similar means) to override the
|
||||
* reset method will still work as expected. @bus_phases_reset function
|
||||
* will be registered as the parent reset method and effectively call
|
||||
* parent reset phases.
|
||||
*/
|
||||
bc->reset = bus_phases_reset;
|
||||
rc->get_transitional_function = bus_get_transitional_reset;
|
||||
}
|
||||
|
||||
static void qbus_finalize(Object *obj)
|
||||
@ -223,6 +321,10 @@ static const TypeInfo bus_info = {
|
||||
.instance_init = qbus_initfn,
|
||||
.instance_finalize = qbus_finalize,
|
||||
.class_init = bus_class_init,
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_RESETTABLE_INTERFACE },
|
||||
{ }
|
||||
},
|
||||
};
|
||||
|
||||
static void bus_register_types(void)
|
||||
|
@ -58,7 +58,7 @@ static void or_irq_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
qemu_or_irq *s = OR_IRQ(dev);
|
||||
|
||||
assert(s->num_lines < MAX_OR_LINES);
|
||||
assert(s->num_lines <= MAX_OR_LINES);
|
||||
|
||||
qdev_init_gpio_in(dev, or_irq_handler, s->num_lines);
|
||||
}
|
||||
|
160
hw/core/qdev.c
160
hw/core/qdev.c
@ -38,6 +38,7 @@
|
||||
#include "hw/boards.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "trace.h"
|
||||
|
||||
bool qdev_hotplug = false;
|
||||
static bool qdev_hot_added = false;
|
||||
@ -95,21 +96,31 @@ static void bus_add_child(BusState *bus, DeviceState *child)
|
||||
|
||||
void qdev_set_parent_bus(DeviceState *dev, BusState *bus)
|
||||
{
|
||||
bool replugging = dev->parent_bus != NULL;
|
||||
BusState *old_parent_bus = dev->parent_bus;
|
||||
|
||||
if (replugging) {
|
||||
/* Keep a reference to the device while it's not plugged into
|
||||
if (old_parent_bus) {
|
||||
trace_qdev_update_parent_bus(dev, object_get_typename(OBJECT(dev)),
|
||||
old_parent_bus, object_get_typename(OBJECT(old_parent_bus)),
|
||||
OBJECT(bus), object_get_typename(OBJECT(bus)));
|
||||
/*
|
||||
* Keep a reference to the device while it's not plugged into
|
||||
* any bus, to avoid it potentially evaporating when it is
|
||||
* dereffed in bus_remove_child().
|
||||
* Also keep the ref of the parent bus until the end, so that
|
||||
* we can safely call resettable_change_parent() below.
|
||||
*/
|
||||
object_ref(OBJECT(dev));
|
||||
bus_remove_child(dev->parent_bus, dev);
|
||||
object_unref(OBJECT(dev->parent_bus));
|
||||
}
|
||||
dev->parent_bus = bus;
|
||||
object_ref(OBJECT(bus));
|
||||
bus_add_child(bus, dev);
|
||||
if (replugging) {
|
||||
if (dev->realized) {
|
||||
resettable_change_parent(OBJECT(dev), OBJECT(bus),
|
||||
OBJECT(old_parent_bus));
|
||||
}
|
||||
if (old_parent_bus) {
|
||||
object_unref(OBJECT(old_parent_bus));
|
||||
object_unref(OBJECT(dev));
|
||||
}
|
||||
}
|
||||
@ -296,9 +307,21 @@ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev)
|
||||
return hotplug_ctrl;
|
||||
}
|
||||
|
||||
static int qdev_prereset(DeviceState *dev, void *opaque)
|
||||
{
|
||||
trace_qdev_reset_tree(dev, object_get_typename(OBJECT(dev)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qbus_prereset(BusState *bus, void *opaque)
|
||||
{
|
||||
trace_qbus_reset_tree(bus, object_get_typename(OBJECT(bus)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qdev_reset_one(DeviceState *dev, void *opaque)
|
||||
{
|
||||
device_reset(dev);
|
||||
device_legacy_reset(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -306,6 +329,7 @@ static int qdev_reset_one(DeviceState *dev, void *opaque)
|
||||
static int qbus_reset_one(BusState *bus, void *opaque)
|
||||
{
|
||||
BusClass *bc = BUS_GET_CLASS(bus);
|
||||
trace_qbus_reset(bus, object_get_typename(OBJECT(bus)));
|
||||
if (bc->reset) {
|
||||
bc->reset(bus);
|
||||
}
|
||||
@ -314,7 +338,9 @@ static int qbus_reset_one(BusState *bus, void *opaque)
|
||||
|
||||
void qdev_reset_all(DeviceState *dev)
|
||||
{
|
||||
qdev_walk_children(dev, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
|
||||
trace_qdev_reset_all(dev, object_get_typename(OBJECT(dev)));
|
||||
qdev_walk_children(dev, qdev_prereset, qbus_prereset,
|
||||
qdev_reset_one, qbus_reset_one, NULL);
|
||||
}
|
||||
|
||||
void qdev_reset_all_fn(void *opaque)
|
||||
@ -324,7 +350,9 @@ void qdev_reset_all_fn(void *opaque)
|
||||
|
||||
void qbus_reset_all(BusState *bus)
|
||||
{
|
||||
qbus_walk_children(bus, NULL, NULL, qdev_reset_one, qbus_reset_one, NULL);
|
||||
trace_qbus_reset_all(bus, object_get_typename(OBJECT(bus)));
|
||||
qbus_walk_children(bus, qdev_prereset, qbus_prereset,
|
||||
qdev_reset_one, qbus_reset_one, NULL);
|
||||
}
|
||||
|
||||
void qbus_reset_all_fn(void *opaque)
|
||||
@ -333,6 +361,33 @@ void qbus_reset_all_fn(void *opaque)
|
||||
qbus_reset_all(bus);
|
||||
}
|
||||
|
||||
void device_cold_reset(DeviceState *dev)
|
||||
{
|
||||
resettable_reset(OBJECT(dev), RESET_TYPE_COLD);
|
||||
}
|
||||
|
||||
bool device_is_in_reset(DeviceState *dev)
|
||||
{
|
||||
return resettable_is_in_reset(OBJECT(dev));
|
||||
}
|
||||
|
||||
static ResettableState *device_get_reset_state(Object *obj)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
return &dev->reset;
|
||||
}
|
||||
|
||||
static void device_reset_child_foreach(Object *obj, ResettableChildCallback cb,
|
||||
void *opaque, ResetType type)
|
||||
{
|
||||
DeviceState *dev = DEVICE(obj);
|
||||
BusState *bus;
|
||||
|
||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||
cb(OBJECT(bus), opaque, type);
|
||||
}
|
||||
}
|
||||
|
||||
/* can be used as ->unplug() callback for the simple cases */
|
||||
void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev,
|
||||
DeviceState *dev, Error **errp)
|
||||
@ -859,6 +914,12 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear the reset state, in case the object was previously unrealized
|
||||
* with a dirty state.
|
||||
*/
|
||||
resettable_state_clear(&dev->reset);
|
||||
|
||||
QLIST_FOREACH(bus, &dev->child_bus, sibling) {
|
||||
object_property_set_bool(OBJECT(bus), true, "realized",
|
||||
&local_err);
|
||||
@ -867,7 +928,14 @@ static void device_set_realized(Object *obj, bool value, Error **errp)
|
||||
}
|
||||
}
|
||||
if (dev->hotplugged) {
|
||||
device_reset(dev);
|
||||
/*
|
||||
* Reset the device, as well as its subtree which, at this point,
|
||||
* should be realized too.
|
||||
*/
|
||||
resettable_assert_reset(OBJECT(dev), RESET_TYPE_COLD);
|
||||
resettable_change_parent(OBJECT(dev), OBJECT(dev->parent_bus),
|
||||
NULL);
|
||||
resettable_release_reset(OBJECT(dev), RESET_TYPE_COLD);
|
||||
}
|
||||
dev->pending_deleted_event = false;
|
||||
|
||||
@ -1035,10 +1103,62 @@ device_vmstate_if_get_id(VMStateIf *obj)
|
||||
return qdev_get_dev_path(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* device_phases_reset:
|
||||
* Transition reset method for devices to allow moving
|
||||
* smoothly from legacy reset method to multi-phases
|
||||
*/
|
||||
static void device_phases_reset(DeviceState *dev)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(dev);
|
||||
|
||||
if (rc->phases.enter) {
|
||||
rc->phases.enter(OBJECT(dev), RESET_TYPE_COLD);
|
||||
}
|
||||
if (rc->phases.hold) {
|
||||
rc->phases.hold(OBJECT(dev));
|
||||
}
|
||||
if (rc->phases.exit) {
|
||||
rc->phases.exit(OBJECT(dev));
|
||||
}
|
||||
}
|
||||
|
||||
static void device_transitional_reset(Object *obj)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(obj);
|
||||
|
||||
/*
|
||||
* This will call either @device_phases_reset (for multi-phases transitioned
|
||||
* devices) or a device's specific method for not-yet transitioned devices.
|
||||
* In both case, it does not reset children.
|
||||
*/
|
||||
if (dc->reset) {
|
||||
dc->reset(DEVICE(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* device_get_transitional_reset:
|
||||
* check if the device's class is ready for multi-phase
|
||||
*/
|
||||
static ResettableTrFunction device_get_transitional_reset(Object *obj)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_GET_CLASS(obj);
|
||||
if (dc->reset != device_phases_reset) {
|
||||
/*
|
||||
* dc->reset has been overridden by a subclass,
|
||||
* the device is not ready for multi phase yet.
|
||||
*/
|
||||
return device_transitional_reset;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void device_class_init(ObjectClass *class, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(class);
|
||||
VMStateIfClass *vc = VMSTATE_IF_CLASS(class);
|
||||
ResettableClass *rc = RESETTABLE_CLASS(class);
|
||||
|
||||
class->unparent = device_unparent;
|
||||
|
||||
@ -1051,6 +1171,24 @@ static void device_class_init(ObjectClass *class, void *data)
|
||||
dc->hotpluggable = true;
|
||||
dc->user_creatable = true;
|
||||
vc->get_id = device_vmstate_if_get_id;
|
||||
rc->get_state = device_get_reset_state;
|
||||
rc->child_foreach = device_reset_child_foreach;
|
||||
|
||||
/*
|
||||
* @device_phases_reset is put as the default reset method below, allowing
|
||||
* to do the multi-phase transition from base classes to leaf classes. It
|
||||
* allows a legacy-reset Device class to extend a multi-phases-reset
|
||||
* Device class for the following reason:
|
||||
* + If a base class B has been moved to multi-phase, then it does not
|
||||
* override this default reset method and may have defined phase methods.
|
||||
* + A child class C (extending class B) which uses
|
||||
* device_class_set_parent_reset() (or similar means) to override the
|
||||
* reset method will still work as expected. @device_phases_reset function
|
||||
* will be registered as the parent reset method and effectively call
|
||||
* parent reset phases.
|
||||
*/
|
||||
dc->reset = device_phases_reset;
|
||||
rc->get_transitional_function = device_get_transitional_reset;
|
||||
|
||||
object_class_property_add_bool(class, "realized",
|
||||
device_get_realized, device_set_realized,
|
||||
@ -1101,10 +1239,11 @@ void device_class_set_parent_unrealize(DeviceClass *dc,
|
||||
dc->unrealize = dev_unrealize;
|
||||
}
|
||||
|
||||
void device_reset(DeviceState *dev)
|
||||
void device_legacy_reset(DeviceState *dev)
|
||||
{
|
||||
DeviceClass *klass = DEVICE_GET_CLASS(dev);
|
||||
|
||||
trace_qdev_reset(dev, object_get_typename(OBJECT(dev)));
|
||||
if (klass->reset) {
|
||||
klass->reset(dev);
|
||||
}
|
||||
@ -1134,6 +1273,7 @@ static const TypeInfo device_type_info = {
|
||||
.class_size = sizeof(DeviceClass),
|
||||
.interfaces = (InterfaceInfo[]) {
|
||||
{ TYPE_VMSTATE_IF },
|
||||
{ TYPE_RESETTABLE_INTERFACE },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
301
hw/core/resettable.c
Normal file
301
hw/core/resettable.c
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
* Resettable interface.
|
||||
*
|
||||
* Copyright (c) 2019 GreenSocs SAS
|
||||
*
|
||||
* Authors:
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "qemu/module.h"
|
||||
#include "hw/resettable.h"
|
||||
#include "trace.h"
|
||||
|
||||
/**
|
||||
* resettable_phase_enter/hold/exit:
|
||||
* Function executing a phase recursively in a resettable object and its
|
||||
* children.
|
||||
*/
|
||||
static void resettable_phase_enter(Object *obj, void *opaque, ResetType type);
|
||||
static void resettable_phase_hold(Object *obj, void *opaque, ResetType type);
|
||||
static void resettable_phase_exit(Object *obj, void *opaque, ResetType type);
|
||||
|
||||
/**
|
||||
* enter_phase_in_progress:
|
||||
* True if we are currently in reset enter phase.
|
||||
*
|
||||
* exit_phase_in_progress:
|
||||
* count the number of exit phase we are in.
|
||||
*
|
||||
* Note: These flags are only used to guarantee (using asserts) that the reset
|
||||
* API is used correctly. We can use global variables because we rely on the
|
||||
* iothread mutex to ensure only one reset operation is in a progress at a
|
||||
* given time.
|
||||
*/
|
||||
static bool enter_phase_in_progress;
|
||||
static unsigned exit_phase_in_progress;
|
||||
|
||||
void resettable_reset(Object *obj, ResetType type)
|
||||
{
|
||||
trace_resettable_reset(obj, type);
|
||||
resettable_assert_reset(obj, type);
|
||||
resettable_release_reset(obj, type);
|
||||
}
|
||||
|
||||
void resettable_assert_reset(Object *obj, ResetType type)
|
||||
{
|
||||
/* TODO: change this assert when adding support for other reset types */
|
||||
assert(type == RESET_TYPE_COLD);
|
||||
trace_resettable_reset_assert_begin(obj, type);
|
||||
assert(!enter_phase_in_progress);
|
||||
|
||||
enter_phase_in_progress = true;
|
||||
resettable_phase_enter(obj, NULL, type);
|
||||
enter_phase_in_progress = false;
|
||||
|
||||
resettable_phase_hold(obj, NULL, type);
|
||||
|
||||
trace_resettable_reset_assert_end(obj);
|
||||
}
|
||||
|
||||
void resettable_release_reset(Object *obj, ResetType type)
|
||||
{
|
||||
/* TODO: change this assert when adding support for other reset types */
|
||||
assert(type == RESET_TYPE_COLD);
|
||||
trace_resettable_reset_release_begin(obj, type);
|
||||
assert(!enter_phase_in_progress);
|
||||
|
||||
exit_phase_in_progress += 1;
|
||||
resettable_phase_exit(obj, NULL, type);
|
||||
exit_phase_in_progress -= 1;
|
||||
|
||||
trace_resettable_reset_release_end(obj);
|
||||
}
|
||||
|
||||
bool resettable_is_in_reset(Object *obj)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
|
||||
ResettableState *s = rc->get_state(obj);
|
||||
|
||||
return s->count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* resettable_child_foreach:
|
||||
* helper to avoid checking the existence of the method.
|
||||
*/
|
||||
static void resettable_child_foreach(ResettableClass *rc, Object *obj,
|
||||
ResettableChildCallback cb,
|
||||
void *opaque, ResetType type)
|
||||
{
|
||||
if (rc->child_foreach) {
|
||||
rc->child_foreach(obj, cb, opaque, type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* resettable_get_tr_func:
|
||||
* helper to fetch transitional reset callback if any.
|
||||
*/
|
||||
static ResettableTrFunction resettable_get_tr_func(ResettableClass *rc,
|
||||
Object *obj)
|
||||
{
|
||||
ResettableTrFunction tr_func = NULL;
|
||||
if (rc->get_transitional_function) {
|
||||
tr_func = rc->get_transitional_function(obj);
|
||||
}
|
||||
return tr_func;
|
||||
}
|
||||
|
||||
static void resettable_phase_enter(Object *obj, void *opaque, ResetType type)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
|
||||
ResettableState *s = rc->get_state(obj);
|
||||
const char *obj_typename = object_get_typename(obj);
|
||||
bool action_needed = false;
|
||||
|
||||
/* exit phase has to finish properly before entering back in reset */
|
||||
assert(!s->exit_phase_in_progress);
|
||||
|
||||
trace_resettable_phase_enter_begin(obj, obj_typename, s->count, type);
|
||||
|
||||
/* Only take action if we really enter reset for the 1st time. */
|
||||
/*
|
||||
* TODO: if adding more ResetType support, some additional checks
|
||||
* are probably needed here.
|
||||
*/
|
||||
if (s->count++ == 0) {
|
||||
action_needed = true;
|
||||
}
|
||||
/*
|
||||
* We limit the count to an arbitrary "big" value. The value is big
|
||||
* enough not to be triggered normally.
|
||||
* The assert will stop an infinite loop if there is a cycle in the
|
||||
* reset tree. The loop goes through resettable_foreach_child below
|
||||
* which at some point will call us again.
|
||||
*/
|
||||
assert(s->count <= 50);
|
||||
|
||||
/*
|
||||
* handle the children even if action_needed is at false so that
|
||||
* child counts are incremented too
|
||||
*/
|
||||
resettable_child_foreach(rc, obj, resettable_phase_enter, NULL, type);
|
||||
|
||||
/* execute enter phase for the object if needed */
|
||||
if (action_needed) {
|
||||
trace_resettable_phase_enter_exec(obj, obj_typename, type,
|
||||
!!rc->phases.enter);
|
||||
if (rc->phases.enter && !resettable_get_tr_func(rc, obj)) {
|
||||
rc->phases.enter(obj, type);
|
||||
}
|
||||
s->hold_phase_pending = true;
|
||||
}
|
||||
trace_resettable_phase_enter_end(obj, obj_typename, s->count);
|
||||
}
|
||||
|
||||
static void resettable_phase_hold(Object *obj, void *opaque, ResetType type)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
|
||||
ResettableState *s = rc->get_state(obj);
|
||||
const char *obj_typename = object_get_typename(obj);
|
||||
|
||||
/* exit phase has to finish properly before entering back in reset */
|
||||
assert(!s->exit_phase_in_progress);
|
||||
|
||||
trace_resettable_phase_hold_begin(obj, obj_typename, s->count, type);
|
||||
|
||||
/* handle children first */
|
||||
resettable_child_foreach(rc, obj, resettable_phase_hold, NULL, type);
|
||||
|
||||
/* exec hold phase */
|
||||
if (s->hold_phase_pending) {
|
||||
s->hold_phase_pending = false;
|
||||
ResettableTrFunction tr_func = resettable_get_tr_func(rc, obj);
|
||||
trace_resettable_phase_hold_exec(obj, obj_typename, !!rc->phases.hold);
|
||||
if (tr_func) {
|
||||
trace_resettable_transitional_function(obj, obj_typename);
|
||||
tr_func(obj);
|
||||
} else if (rc->phases.hold) {
|
||||
rc->phases.hold(obj);
|
||||
}
|
||||
}
|
||||
trace_resettable_phase_hold_end(obj, obj_typename, s->count);
|
||||
}
|
||||
|
||||
static void resettable_phase_exit(Object *obj, void *opaque, ResetType type)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
|
||||
ResettableState *s = rc->get_state(obj);
|
||||
const char *obj_typename = object_get_typename(obj);
|
||||
|
||||
assert(!s->exit_phase_in_progress);
|
||||
trace_resettable_phase_exit_begin(obj, obj_typename, s->count, type);
|
||||
|
||||
/* exit_phase_in_progress ensures this phase is 'atomic' */
|
||||
s->exit_phase_in_progress = true;
|
||||
resettable_child_foreach(rc, obj, resettable_phase_exit, NULL, type);
|
||||
|
||||
assert(s->count > 0);
|
||||
if (s->count == 1) {
|
||||
trace_resettable_phase_exit_exec(obj, obj_typename, !!rc->phases.exit);
|
||||
if (rc->phases.exit && !resettable_get_tr_func(rc, obj)) {
|
||||
rc->phases.exit(obj);
|
||||
}
|
||||
s->count = 0;
|
||||
}
|
||||
s->exit_phase_in_progress = false;
|
||||
trace_resettable_phase_exit_end(obj, obj_typename, s->count);
|
||||
}
|
||||
|
||||
/*
|
||||
* resettable_get_count:
|
||||
* Get the count of the Resettable object @obj. Return 0 if @obj is NULL.
|
||||
*/
|
||||
static unsigned resettable_get_count(Object *obj)
|
||||
{
|
||||
if (obj) {
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
|
||||
return rc->get_state(obj)->count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void resettable_change_parent(Object *obj, Object *newp, Object *oldp)
|
||||
{
|
||||
ResettableClass *rc = RESETTABLE_GET_CLASS(obj);
|
||||
ResettableState *s = rc->get_state(obj);
|
||||
unsigned newp_count = resettable_get_count(newp);
|
||||
unsigned oldp_count = resettable_get_count(oldp);
|
||||
|
||||
/*
|
||||
* Ensure we do not change parent when in enter or exit phase.
|
||||
* During these phases, the reset subtree being updated is partly in reset
|
||||
* and partly not in reset (it depends on the actual position in
|
||||
* resettable_child_foreach()s). We are not able to tell in which part is a
|
||||
* leaving or arriving device. Thus we cannot set the reset count of the
|
||||
* moving device to the proper value.
|
||||
*/
|
||||
assert(!enter_phase_in_progress && !exit_phase_in_progress);
|
||||
trace_resettable_change_parent(obj, oldp, oldp_count, newp, newp_count);
|
||||
|
||||
/*
|
||||
* At most one of the two 'for' loops will be executed below
|
||||
* in order to cope with the difference between the two counts.
|
||||
*/
|
||||
/* if newp is more reset than oldp */
|
||||
for (unsigned i = oldp_count; i < newp_count; i++) {
|
||||
resettable_assert_reset(obj, RESET_TYPE_COLD);
|
||||
}
|
||||
/*
|
||||
* if obj is leaving a bus under reset, we need to ensure
|
||||
* hold phase is not pending.
|
||||
*/
|
||||
if (oldp_count && s->hold_phase_pending) {
|
||||
resettable_phase_hold(obj, NULL, RESET_TYPE_COLD);
|
||||
}
|
||||
/* if oldp is more reset than newp */
|
||||
for (unsigned i = newp_count; i < oldp_count; i++) {
|
||||
resettable_release_reset(obj, RESET_TYPE_COLD);
|
||||
}
|
||||
}
|
||||
|
||||
void resettable_cold_reset_fn(void *opaque)
|
||||
{
|
||||
resettable_reset((Object *) opaque, RESET_TYPE_COLD);
|
||||
}
|
||||
|
||||
void resettable_class_set_parent_phases(ResettableClass *rc,
|
||||
ResettableEnterPhase enter,
|
||||
ResettableHoldPhase hold,
|
||||
ResettableExitPhase exit,
|
||||
ResettablePhases *parent_phases)
|
||||
{
|
||||
*parent_phases = rc->phases;
|
||||
if (enter) {
|
||||
rc->phases.enter = enter;
|
||||
}
|
||||
if (hold) {
|
||||
rc->phases.hold = hold;
|
||||
}
|
||||
if (exit) {
|
||||
rc->phases.exit = exit;
|
||||
}
|
||||
}
|
||||
|
||||
static const TypeInfo resettable_interface_info = {
|
||||
.name = TYPE_RESETTABLE_INTERFACE,
|
||||
.parent = TYPE_INTERFACE,
|
||||
.class_size = sizeof(ResettableClass),
|
||||
};
|
||||
|
||||
static void reset_register_types(void)
|
||||
{
|
||||
type_register_static(&resettable_interface_info);
|
||||
}
|
||||
|
||||
type_init(reset_register_types)
|
@ -1,2 +1,29 @@
|
||||
# loader.c
|
||||
loader_write_rom(const char *name, uint64_t gpa, uint64_t size, bool isrom) "%s: @0x%"PRIx64" size=0x%"PRIx64" ROM=%d"
|
||||
|
||||
# qdev.c
|
||||
qdev_reset(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
qdev_reset_all(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
qdev_reset_tree(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
qbus_reset(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
qbus_reset_all(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
qbus_reset_tree(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
qdev_update_parent_bus(void *obj, const char *objtype, void *oldp, const char *oldptype, void *newp, const char *newptype) "obj=%p(%s) old_parent=%p(%s) new_parent=%p(%s)"
|
||||
|
||||
# resettable.c
|
||||
resettable_reset(void *obj, int cold) "obj=%p cold=%d"
|
||||
resettable_reset_assert_begin(void *obj, int cold) "obj=%p cold=%d"
|
||||
resettable_reset_assert_end(void *obj) "obj=%p"
|
||||
resettable_reset_release_begin(void *obj, int cold) "obj=%p cold=%d"
|
||||
resettable_reset_release_end(void *obj) "obj=%p"
|
||||
resettable_change_parent(void *obj, void *o, unsigned oc, void *n, unsigned nc) "obj=%p from=%p(%d) to=%p(%d)"
|
||||
resettable_phase_enter_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d"
|
||||
resettable_phase_enter_exec(void *obj, const char *objtype, int type, int has_method) "obj=%p(%s) type=%d method=%d"
|
||||
resettable_phase_enter_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d"
|
||||
resettable_phase_hold_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d"
|
||||
resettable_phase_hold_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d"
|
||||
resettable_phase_hold_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d"
|
||||
resettable_phase_exit_begin(void *obj, const char *objtype, unsigned count, int type) "obj=%p(%s) count=%d type=%d"
|
||||
resettable_phase_exit_exec(void *obj, const char *objtype, int has_method) "obj=%p(%s) method=%d"
|
||||
resettable_phase_exit_end(void *obj, const char *objtype, unsigned count) "obj=%p(%s) count=%d"
|
||||
resettable_transitional_function(void *obj, const char *objtype) "obj=%p(%s)"
|
||||
|
@ -140,7 +140,7 @@ void hyperv_synic_reset(CPUState *cs)
|
||||
SynICState *synic = get_synic(cs);
|
||||
|
||||
if (synic) {
|
||||
device_reset(DEVICE(synic));
|
||||
device_legacy_reset(DEVICE(synic));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,7 +370,7 @@ static void microvm_machine_reset(MachineState *machine)
|
||||
cpu = X86_CPU(cs);
|
||||
|
||||
if (cpu->apic_state) {
|
||||
device_reset(cpu->apic_state);
|
||||
device_legacy_reset(cpu->apic_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1879,7 +1879,7 @@ static void pc_machine_reset(MachineState *machine)
|
||||
cpu = X86_CPU(cs);
|
||||
|
||||
if (cpu->apic_state) {
|
||||
device_reset(cpu->apic_state);
|
||||
device_legacy_reset(cpu->apic_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,7 @@ static void md_attr_write(PCMCIACardState *card, uint32_t at, uint8_t value)
|
||||
case 0x00: /* Configuration Option Register */
|
||||
s->opt = value & 0xcf;
|
||||
if (value & OPT_SRESET) {
|
||||
device_reset(DEVICE(s));
|
||||
device_legacy_reset(DEVICE(s));
|
||||
}
|
||||
md_interrupt_update(s);
|
||||
break;
|
||||
@ -316,7 +316,7 @@ static void md_common_write(PCMCIACardState *card, uint32_t at, uint16_t value)
|
||||
case 0xe: /* Device Control */
|
||||
s->ctrl = value;
|
||||
if (value & CTRL_SRST) {
|
||||
device_reset(DEVICE(s));
|
||||
device_legacy_reset(DEVICE(s));
|
||||
}
|
||||
md_interrupt_update(s);
|
||||
break;
|
||||
@ -541,7 +541,7 @@ static int dscm1xxxx_attach(PCMCIACardState *card)
|
||||
md->attr_base = pcc->cis[0x74] | (pcc->cis[0x76] << 8);
|
||||
md->io_base = 0x0;
|
||||
|
||||
device_reset(DEVICE(md));
|
||||
device_legacy_reset(DEVICE(md));
|
||||
md_interrupt_update(md);
|
||||
|
||||
return 0;
|
||||
@ -551,7 +551,7 @@ static int dscm1xxxx_detach(PCMCIACardState *card)
|
||||
{
|
||||
MicroDriveState *md = MICRODRIVE(card);
|
||||
|
||||
device_reset(DEVICE(md));
|
||||
device_legacy_reset(DEVICE(md));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -336,7 +336,10 @@ static void kvm_arm_gicv3_put(GICv3State *s)
|
||||
kvm_gicd_access(s, GICD_CTLR, ®, true);
|
||||
|
||||
if (redist_typer & GICR_TYPER_PLPIS) {
|
||||
/* Set base addresses before LPIs are enabled by GICR_CTLR write */
|
||||
/*
|
||||
* Restore base addresses before LPIs are potentially enabled by
|
||||
* GICR_CTLR write
|
||||
*/
|
||||
for (ncpu = 0; ncpu < s->num_cpu; ncpu++) {
|
||||
GICv3CPUState *c = &s->cpu[ncpu];
|
||||
|
||||
@ -347,12 +350,6 @@ static void kvm_arm_gicv3_put(GICv3State *s)
|
||||
kvm_gicr_access(s, GICR_PROPBASER + 4, ncpu, ®h, true);
|
||||
|
||||
reg64 = c->gicr_pendbaser;
|
||||
if (!(c->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
|
||||
/* Setting PTZ is advised if LPIs are disabled, to reduce
|
||||
* GIC initialization time.
|
||||
*/
|
||||
reg64 |= GICR_PENDBASER_PTZ;
|
||||
}
|
||||
regl = (uint32_t)reg64;
|
||||
kvm_gicr_access(s, GICR_PENDBASER, ncpu, ®l, true);
|
||||
regh = (uint32_t)(reg64 >> 32);
|
||||
|
@ -1766,7 +1766,7 @@ static target_ulong h_int_reset(PowerPCCPU *cpu,
|
||||
return H_PARAMETER;
|
||||
}
|
||||
|
||||
device_reset(DEVICE(xive));
|
||||
device_legacy_reset(DEVICE(xive));
|
||||
|
||||
if (kvm_irqchip_in_kernel()) {
|
||||
Error *local_err = NULL;
|
||||
|
@ -15,12 +15,16 @@
|
||||
#include "hw/misc/pca9552.h"
|
||||
#include "hw/misc/pca9552_regs.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "qapi/error.h"
|
||||
#include "qapi/visitor.h"
|
||||
|
||||
#define PCA9552_LED_ON 0x0
|
||||
#define PCA9552_LED_OFF 0x1
|
||||
#define PCA9552_LED_PWM0 0x2
|
||||
#define PCA9552_LED_PWM1 0x3
|
||||
|
||||
static const char *led_state[] = {"on", "off", "pwm0", "pwm1"};
|
||||
|
||||
static uint8_t pca9552_pin_get_config(PCA9552State *s, int pin)
|
||||
{
|
||||
uint8_t reg = PCA9552_LS0 + (pin / 4);
|
||||
@ -169,6 +173,82 @@ static int pca9552_event(I2CSlave *i2c, enum i2c_event event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pca9552_get_led(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
PCA9552State *s = PCA9552(obj);
|
||||
int led, rc, reg;
|
||||
uint8_t state;
|
||||
|
||||
rc = sscanf(name, "led%2d", &led);
|
||||
if (rc != 1) {
|
||||
error_setg(errp, "%s: error reading %s", __func__, name);
|
||||
return;
|
||||
}
|
||||
if (led < 0 || led > s->nr_leds) {
|
||||
error_setg(errp, "%s invalid led %s", __func__, name);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Get the LSx register as the qom interface should expose the device
|
||||
* state, not the modeled 'input line' behaviour which would come from
|
||||
* reading the INPUTx reg
|
||||
*/
|
||||
reg = PCA9552_LS0 + led / 4;
|
||||
state = (pca9552_read(s, reg) >> (led % 8)) & 0x3;
|
||||
visit_type_str(v, name, (char **)&led_state[state], errp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an LED selector register value based on an existing one, with
|
||||
* the appropriate 2-bit state value set for the given LED number (0-3).
|
||||
*/
|
||||
static inline uint8_t pca955x_ledsel(uint8_t oldval, int led_num, int state)
|
||||
{
|
||||
return (oldval & (~(0x3 << (led_num << 1)))) |
|
||||
((state & 0x3) << (led_num << 1));
|
||||
}
|
||||
|
||||
static void pca9552_set_led(Object *obj, Visitor *v, const char *name,
|
||||
void *opaque, Error **errp)
|
||||
{
|
||||
PCA9552State *s = PCA9552(obj);
|
||||
Error *local_err = NULL;
|
||||
int led, rc, reg, val;
|
||||
uint8_t state;
|
||||
char *state_str;
|
||||
|
||||
visit_type_str(v, name, &state_str, &local_err);
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
}
|
||||
rc = sscanf(name, "led%2d", &led);
|
||||
if (rc != 1) {
|
||||
error_setg(errp, "%s: error reading %s", __func__, name);
|
||||
return;
|
||||
}
|
||||
if (led < 0 || led > s->nr_leds) {
|
||||
error_setg(errp, "%s invalid led %s", __func__, name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (state = 0; state < ARRAY_SIZE(led_state); state++) {
|
||||
if (!strcmp(state_str, led_state[state])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (state >= ARRAY_SIZE(led_state)) {
|
||||
error_setg(errp, "%s invalid led state %s", __func__, state_str);
|
||||
return;
|
||||
}
|
||||
|
||||
reg = PCA9552_LS0 + led / 4;
|
||||
val = pca9552_read(s, reg);
|
||||
val = pca955x_ledsel(val, led % 4, state);
|
||||
pca9552_write(s, reg, val);
|
||||
}
|
||||
|
||||
static const VMStateDescription pca9552_vmstate = {
|
||||
.name = "PCA9552",
|
||||
.version_id = 0,
|
||||
@ -204,6 +284,7 @@ static void pca9552_reset(DeviceState *dev)
|
||||
static void pca9552_initfn(Object *obj)
|
||||
{
|
||||
PCA9552State *s = PCA9552(obj);
|
||||
int led;
|
||||
|
||||
/* If support for the other PCA955X devices are implemented, these
|
||||
* constant values might be part of class structure describing the
|
||||
@ -211,6 +292,15 @@ static void pca9552_initfn(Object *obj)
|
||||
*/
|
||||
s->max_reg = PCA9552_LS3;
|
||||
s->nr_leds = 16;
|
||||
|
||||
for (led = 0; led < s->nr_leds; led++) {
|
||||
char *name;
|
||||
|
||||
name = g_strdup_printf("led%d", led);
|
||||
object_property_add(obj, name, "bool", pca9552_get_led, pca9552_set_led,
|
||||
NULL, NULL, NULL);
|
||||
g_free(name);
|
||||
}
|
||||
}
|
||||
|
||||
static void pca9552_class_init(ObjectClass *klass, void *data)
|
||||
|
@ -198,6 +198,8 @@ typedef struct {
|
||||
uint32_t des3;
|
||||
} FTGMAC100Desc;
|
||||
|
||||
#define FTGMAC100_DESC_ALIGNMENT 16
|
||||
|
||||
/*
|
||||
* Specific RTL8211E MII Registers
|
||||
*/
|
||||
@ -722,6 +724,12 @@ static void ftgmac100_write(void *opaque, hwaddr addr,
|
||||
s->itc = value;
|
||||
break;
|
||||
case FTGMAC100_RXR_BADR: /* Ring buffer address */
|
||||
if (!QEMU_IS_ALIGNED(value, FTGMAC100_DESC_ALIGNMENT)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad RX buffer alignment 0x%"
|
||||
HWADDR_PRIx "\n", __func__, value);
|
||||
return;
|
||||
}
|
||||
|
||||
s->rx_ring = value;
|
||||
s->rx_descriptor = s->rx_ring;
|
||||
break;
|
||||
@ -731,6 +739,11 @@ static void ftgmac100_write(void *opaque, hwaddr addr,
|
||||
break;
|
||||
|
||||
case FTGMAC100_NPTXR_BADR: /* Transmit buffer address */
|
||||
if (!QEMU_IS_ALIGNED(value, FTGMAC100_DESC_ALIGNMENT)) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad TX buffer alignment 0x%"
|
||||
HWADDR_PRIx "\n", __func__, value);
|
||||
return;
|
||||
}
|
||||
s->tx_ring = value;
|
||||
s->tx_descriptor = s->tx_ring;
|
||||
break;
|
||||
|
@ -466,7 +466,7 @@ static void pnv_psi_reset(DeviceState *dev)
|
||||
|
||||
static void pnv_psi_reset_handler(void *dev)
|
||||
{
|
||||
device_reset(DEVICE(dev));
|
||||
device_legacy_reset(DEVICE(dev));
|
||||
}
|
||||
|
||||
static void pnv_psi_realize(DeviceState *dev, Error **errp)
|
||||
@ -715,7 +715,7 @@ static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr,
|
||||
break;
|
||||
case PSIHB9_INTERRUPT_CONTROL:
|
||||
if (val & PSIHB9_IRQ_RESET) {
|
||||
device_reset(DEVICE(&psi9->source));
|
||||
device_legacy_reset(DEVICE(&psi9->source));
|
||||
}
|
||||
psi->regs[reg] = val;
|
||||
break;
|
||||
|
@ -2014,7 +2014,7 @@ static int spapr_phb_children_reset(Object *child, void *opaque)
|
||||
DeviceState *dev = (DeviceState *) object_dynamic_cast(child, TYPE_DEVICE);
|
||||
|
||||
if (dev) {
|
||||
device_reset(dev);
|
||||
device_legacy_reset(dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -304,7 +304,7 @@ int spapr_vio_send_crq(SpaprVioDevice *dev, uint8_t *crq)
|
||||
static void spapr_vio_quiesce_one(SpaprVioDevice *dev)
|
||||
{
|
||||
if (dev->tcet) {
|
||||
device_reset(DEVICE(dev->tcet));
|
||||
device_legacy_reset(DEVICE(dev->tcet));
|
||||
}
|
||||
free_crq(dev);
|
||||
}
|
||||
|
@ -237,7 +237,15 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp)
|
||||
*/
|
||||
ipl->compat_start_addr = ipl->start_addr;
|
||||
ipl->compat_bios_start_addr = ipl->bios_start_addr;
|
||||
qemu_register_reset(qdev_reset_all_fn, dev);
|
||||
/*
|
||||
* Because this Device is not on any bus in the qbus tree (it is
|
||||
* not a sysbus device and it's not on some other bus like a PCI
|
||||
* bus) it will not be automatically reset by the 'reset the
|
||||
* sysbus' hook registered by vl.c like most devices. So we must
|
||||
* manually register a reset hook for it.
|
||||
* TODO: there should be a better way to do this.
|
||||
*/
|
||||
qemu_register_reset(resettable_cold_reset_fn, dev);
|
||||
error:
|
||||
error_propagate(errp, err);
|
||||
}
|
||||
|
@ -243,7 +243,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra)
|
||||
stw_p(&ressetpci->hdr.rsp, CLP_RC_SETPCIFN_FHOP);
|
||||
goto out;
|
||||
}
|
||||
device_reset(DEVICE(pbdev));
|
||||
device_legacy_reset(DEVICE(pbdev));
|
||||
pbdev->fh &= ~FH_MASK_ENABLE;
|
||||
pbdev->state = ZPCI_FS_DISABLED;
|
||||
stl_p(&ressetpci->fh, pbdev->fh);
|
||||
|
@ -838,7 +838,7 @@ pvscsi_on_cmd_reset_device(PVSCSIState *s)
|
||||
|
||||
if (sdev != NULL) {
|
||||
s->resetting++;
|
||||
device_reset(&sdev->qdev);
|
||||
device_legacy_reset(&sdev->qdev);
|
||||
s->resetting--;
|
||||
return PVSCSI_COMMAND_PROCESSING_SUCCEEDED;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "qapi/error.h"
|
||||
#include "hw/irq.h"
|
||||
#include "migration/vmstate.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
#define ASPEED_SDHCI_INFO 0x00
|
||||
#define ASPEED_SDHCI_INFO_RESET 0x00030000
|
||||
@ -120,14 +121,14 @@ static void aspeed_sdhci_realize(DeviceState *dev, Error **errp)
|
||||
|
||||
/* Create input irqs for the slots */
|
||||
qdev_init_gpio_in_named_with_opaque(DEVICE(sbd), aspeed_sdhci_set_irq,
|
||||
sdhci, NULL, ASPEED_SDHCI_NUM_SLOTS);
|
||||
sdhci, NULL, sdhci->num_slots);
|
||||
|
||||
sysbus_init_irq(sbd, &sdhci->irq);
|
||||
memory_region_init_io(&sdhci->iomem, OBJECT(sdhci), &aspeed_sdhci_ops,
|
||||
sdhci, TYPE_ASPEED_SDHCI, 0x1000);
|
||||
sysbus_init_mmio(sbd, &sdhci->iomem);
|
||||
|
||||
for (int i = 0; i < ASPEED_SDHCI_NUM_SLOTS; ++i) {
|
||||
for (int i = 0; i < sdhci->num_slots; ++i) {
|
||||
Object *sdhci_slot = OBJECT(&sdhci->slots[i]);
|
||||
SysBusDevice *sbd_slot = SYS_BUS_DEVICE(&sdhci->slots[i]);
|
||||
|
||||
@ -174,6 +175,11 @@ static const VMStateDescription vmstate_aspeed_sdhci = {
|
||||
},
|
||||
};
|
||||
|
||||
static Property aspeed_sdhci_properties[] = {
|
||||
DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
};
|
||||
|
||||
static void aspeed_sdhci_class_init(ObjectClass *classp, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(classp);
|
||||
@ -181,6 +187,7 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data)
|
||||
dc->realize = aspeed_sdhci_realize;
|
||||
dc->reset = aspeed_sdhci_reset;
|
||||
dc->vmsd = &vmstate_aspeed_sdhci;
|
||||
device_class_set_props(dc, aspeed_sdhci_properties);
|
||||
}
|
||||
|
||||
static TypeInfo aspeed_sdhci_info = {
|
||||
|
@ -318,7 +318,7 @@ void omap_mmc_reset(struct omap_mmc_s *host)
|
||||
* into any bus, and we must reset it manually. When omap_mmc is
|
||||
* QOMified this must move into the QOM reset function.
|
||||
*/
|
||||
device_reset(DEVICE(host->card));
|
||||
device_legacy_reset(DEVICE(host->card));
|
||||
}
|
||||
|
||||
static uint64_t omap_mmc_read(void *opaque, hwaddr offset,
|
||||
|
@ -482,7 +482,7 @@ static void pl181_reset(DeviceState *d)
|
||||
/* Since we're still using the legacy SD API the card is not plugged
|
||||
* into any bus, and we must reset it manually.
|
||||
*/
|
||||
device_reset(DEVICE(s->card));
|
||||
device_legacy_reset(DEVICE(s->card));
|
||||
}
|
||||
|
||||
static void pl181_init(Object *obj)
|
||||
|
@ -19,6 +19,8 @@ typedef struct AspeedBoardState AspeedBoardState;
|
||||
|
||||
typedef struct AspeedMachine {
|
||||
MachineState parent_obj;
|
||||
|
||||
bool mmio_exec;
|
||||
} AspeedMachine;
|
||||
|
||||
#define ASPEED_MACHINE_CLASS(klass) \
|
||||
|
@ -57,6 +57,7 @@ typedef struct AspeedSoCState {
|
||||
AspeedGPIOState gpio;
|
||||
AspeedGPIOState gpio_1_8v;
|
||||
AspeedSDHCIState sdhci;
|
||||
AspeedSDHCIState emmc;
|
||||
} AspeedSoCState;
|
||||
|
||||
#define TYPE_ASPEED_SOC "aspeed-soc"
|
||||
@ -126,6 +127,7 @@ enum {
|
||||
ASPEED_MII4,
|
||||
ASPEED_SDRAM,
|
||||
ASPEED_XDMA,
|
||||
ASPEED_EMMC,
|
||||
};
|
||||
|
||||
#endif /* ASPEED_SOC_H */
|
||||
|
@ -109,6 +109,7 @@ typedef struct {
|
||||
bool smbios_old_sys_ver;
|
||||
bool no_highmem_ecam;
|
||||
bool no_ged; /* Machines < 4.2 has no support for ACPI GED device */
|
||||
bool kvm_no_adjvtime;
|
||||
} VirtMachineClass;
|
||||
|
||||
typedef struct {
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "qemu/bitmap.h"
|
||||
#include "qom/object.h"
|
||||
#include "hw/hotplug.h"
|
||||
#include "hw/resettable.h"
|
||||
|
||||
enum {
|
||||
DEV_NVECTORS_UNSPECIFIED = -1,
|
||||
@ -122,6 +123,11 @@ typedef struct DeviceClass {
|
||||
bool hotpluggable;
|
||||
|
||||
/* callbacks */
|
||||
/*
|
||||
* Reset method here is deprecated and replaced by methods in the
|
||||
* resettable class interface to implement a multi-phase reset.
|
||||
* TODO: remove once every reset callback is unused
|
||||
*/
|
||||
DeviceReset reset;
|
||||
DeviceRealize realize;
|
||||
DeviceUnrealize unrealize;
|
||||
@ -146,6 +152,7 @@ struct NamedGPIOList {
|
||||
/**
|
||||
* DeviceState:
|
||||
* @realized: Indicates whether the device has been fully constructed.
|
||||
* @reset: ResettableState for the device; handled by Resettable interface.
|
||||
*
|
||||
* This structure should not be accessed directly. We declare it here
|
||||
* so that it can be embedded in individual device state structures.
|
||||
@ -168,6 +175,7 @@ struct DeviceState {
|
||||
int num_child_bus;
|
||||
int instance_id_alias;
|
||||
int alias_required_for_version;
|
||||
ResettableState reset;
|
||||
};
|
||||
|
||||
struct DeviceListener {
|
||||
@ -220,6 +228,7 @@ typedef struct BusChild {
|
||||
/**
|
||||
* BusState:
|
||||
* @hotplug_handler: link to a hotplug handler associated with bus.
|
||||
* @reset: ResettableState for the bus; handled by Resettable interface.
|
||||
*/
|
||||
struct BusState {
|
||||
Object obj;
|
||||
@ -231,6 +240,7 @@ struct BusState {
|
||||
int num_children;
|
||||
QTAILQ_HEAD(, BusChild) children;
|
||||
QLIST_ENTRY(BusState) sibling;
|
||||
ResettableState reset;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -401,6 +411,13 @@ int qdev_walk_children(DeviceState *dev,
|
||||
qdev_walkerfn *post_devfn, qbus_walkerfn *post_busfn,
|
||||
void *opaque);
|
||||
|
||||
/**
|
||||
* @qdev_reset_all:
|
||||
* Reset @dev. See @qbus_reset_all() for more details.
|
||||
*
|
||||
* Note: This function is deprecated and will be removed when it becomes unused.
|
||||
* Please use device_cold_reset() now.
|
||||
*/
|
||||
void qdev_reset_all(DeviceState *dev);
|
||||
void qdev_reset_all_fn(void *opaque);
|
||||
|
||||
@ -413,10 +430,40 @@ void qdev_reset_all_fn(void *opaque);
|
||||
* hard reset means that qbus_reset_all will reset all state of the device.
|
||||
* For PCI devices, for example, this will include the base address registers
|
||||
* or configuration space.
|
||||
*
|
||||
* Note: This function is deprecated and will be removed when it becomes unused.
|
||||
* Please use bus_cold_reset() now.
|
||||
*/
|
||||
void qbus_reset_all(BusState *bus);
|
||||
void qbus_reset_all_fn(void *opaque);
|
||||
|
||||
/**
|
||||
* device_cold_reset:
|
||||
* Reset device @dev and perform a recursive processing using the resettable
|
||||
* interface. It triggers a RESET_TYPE_COLD.
|
||||
*/
|
||||
void device_cold_reset(DeviceState *dev);
|
||||
|
||||
/**
|
||||
* bus_cold_reset:
|
||||
*
|
||||
* Reset bus @bus and perform a recursive processing using the resettable
|
||||
* interface. It triggers a RESET_TYPE_COLD.
|
||||
*/
|
||||
void bus_cold_reset(BusState *bus);
|
||||
|
||||
/**
|
||||
* device_is_in_reset:
|
||||
* Return true if the device @dev is currently being reset.
|
||||
*/
|
||||
bool device_is_in_reset(DeviceState *dev);
|
||||
|
||||
/**
|
||||
* bus_is_in_reset:
|
||||
* Return true if the bus @bus is currently being reset.
|
||||
*/
|
||||
bool bus_is_in_reset(BusState *bus);
|
||||
|
||||
/* This should go away once we get rid of the NULL bus hack */
|
||||
BusState *sysbus_get_default(void);
|
||||
|
||||
@ -432,14 +479,21 @@ char *qdev_get_own_fw_dev_path_from_handler(BusState *bus, DeviceState *dev);
|
||||
void qdev_machine_init(void);
|
||||
|
||||
/**
|
||||
* @device_reset
|
||||
* device_legacy_reset:
|
||||
*
|
||||
* Reset a single device (by calling the reset method).
|
||||
* Note: This function is deprecated and will be removed when it becomes unused.
|
||||
* Please use device_cold_reset() now.
|
||||
*/
|
||||
void device_reset(DeviceState *dev);
|
||||
void device_legacy_reset(DeviceState *dev);
|
||||
|
||||
void device_class_set_props(DeviceClass *dc, Property *props);
|
||||
|
||||
/**
|
||||
* device_class_set_parent_reset:
|
||||
* TODO: remove the function when DeviceClass's reset method
|
||||
* is not used anymore.
|
||||
*/
|
||||
void device_class_set_parent_reset(DeviceClass *dc,
|
||||
DeviceReset dev_reset,
|
||||
DeviceReset *parent_reset);
|
||||
|
247
include/hw/resettable.h
Normal file
247
include/hw/resettable.h
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Resettable interface header.
|
||||
*
|
||||
* Copyright (c) 2019 GreenSocs SAS
|
||||
*
|
||||
* Authors:
|
||||
* Damien Hedde
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||
* See the COPYING file in the top-level directory.
|
||||
*/
|
||||
|
||||
#ifndef HW_RESETTABLE_H
|
||||
#define HW_RESETTABLE_H
|
||||
|
||||
#include "qom/object.h"
|
||||
|
||||
#define TYPE_RESETTABLE_INTERFACE "resettable"
|
||||
|
||||
#define RESETTABLE_CLASS(class) \
|
||||
OBJECT_CLASS_CHECK(ResettableClass, (class), TYPE_RESETTABLE_INTERFACE)
|
||||
|
||||
#define RESETTABLE_GET_CLASS(obj) \
|
||||
OBJECT_GET_CLASS(ResettableClass, (obj), TYPE_RESETTABLE_INTERFACE)
|
||||
|
||||
typedef struct ResettableState ResettableState;
|
||||
|
||||
/**
|
||||
* ResetType:
|
||||
* Types of reset.
|
||||
*
|
||||
* + Cold: reset resulting from a power cycle of the object.
|
||||
*
|
||||
* TODO: Support has to be added to handle more types. In particular,
|
||||
* ResettableState structure needs to be expanded.
|
||||
*/
|
||||
typedef enum ResetType {
|
||||
RESET_TYPE_COLD,
|
||||
} ResetType;
|
||||
|
||||
/*
|
||||
* ResettableClass:
|
||||
* Interface for resettable objects.
|
||||
*
|
||||
* See docs/devel/reset.rst for more detailed information about how QEMU models
|
||||
* reset. This whole API must only be used when holding the iothread mutex.
|
||||
*
|
||||
* All objects which can be reset must implement this interface;
|
||||
* it is usually provided by a base class such as DeviceClass or BusClass.
|
||||
* Every Resettable object must maintain some state tracking the
|
||||
* progress of a reset operation by providing a ResettableState structure.
|
||||
* The functions defined in this module take care of updating the
|
||||
* state of the reset.
|
||||
* The base class implementation of the interface provides this
|
||||
* state and implements the associated method: get_state.
|
||||
*
|
||||
* Concrete object implementations (typically specific devices
|
||||
* such as a UART model) should provide the functions
|
||||
* for the phases.enter, phases.hold and phases.exit methods, which
|
||||
* they can set in their class init function, either directly or
|
||||
* by calling resettable_class_set_parent_phases().
|
||||
* The phase methods are guaranteed to only only ever be called once
|
||||
* for any reset event, in the order 'enter', 'hold', 'exit'.
|
||||
* An object will always move quickly from 'enter' to 'hold'
|
||||
* but might remain in 'hold' for an arbitrary period of time
|
||||
* before eventually reset is deasserted and the 'exit' phase is called.
|
||||
* Object implementations should be prepared for functions handling
|
||||
* inbound connections from other devices (such as qemu_irq handler
|
||||
* functions) to be called at any point during reset after their
|
||||
* 'enter' method has been called.
|
||||
*
|
||||
* Users of a resettable object should not call these methods
|
||||
* directly, but instead use the function resettable_reset().
|
||||
*
|
||||
* @phases.enter: This phase is called when the object enters reset. It
|
||||
* should reset local state of the object, but it must not do anything that
|
||||
* has a side-effect on other objects, such as raising or lowering a qemu_irq
|
||||
* line or reading or writing guest memory. It takes the reset's type as
|
||||
* argument.
|
||||
*
|
||||
* @phases.hold: This phase is called for entry into reset, once every object
|
||||
* in the system which is being reset has had its @phases.enter method called.
|
||||
* At this point devices can do actions that affect other objects.
|
||||
*
|
||||
* @phases.exit: This phase is called when the object leaves the reset state.
|
||||
* Actions affecting other objects are permitted.
|
||||
*
|
||||
* @get_state: Mandatory method which must return a pointer to a
|
||||
* ResettableState.
|
||||
*
|
||||
* @get_transitional_function: transitional method to handle Resettable objects
|
||||
* not yet fully moved to this interface. It will be removed as soon as it is
|
||||
* not needed anymore. This method is optional and may return a pointer to a
|
||||
* function to be used instead of the phases. If the method exists and returns
|
||||
* a non-NULL function pointer then that function is executed as a replacement
|
||||
* of the 'hold' phase method taking the object as argument. The two other phase
|
||||
* methods are not executed.
|
||||
*
|
||||
* @child_foreach: Executes a given callback on every Resettable child. Child
|
||||
* in this context means a child in the qbus tree, so the children of a qbus
|
||||
* are the devices on it, and the children of a device are all the buses it
|
||||
* owns. This is not the same as the QOM object hierarchy. The function takes
|
||||
* additional opaque and ResetType arguments which must be passed unmodified to
|
||||
* the callback.
|
||||
*/
|
||||
typedef void (*ResettableEnterPhase)(Object *obj, ResetType type);
|
||||
typedef void (*ResettableHoldPhase)(Object *obj);
|
||||
typedef void (*ResettableExitPhase)(Object *obj);
|
||||
typedef ResettableState * (*ResettableGetState)(Object *obj);
|
||||
typedef void (*ResettableTrFunction)(Object *obj);
|
||||
typedef ResettableTrFunction (*ResettableGetTrFunction)(Object *obj);
|
||||
typedef void (*ResettableChildCallback)(Object *, void *opaque,
|
||||
ResetType type);
|
||||
typedef void (*ResettableChildForeach)(Object *obj,
|
||||
ResettableChildCallback cb,
|
||||
void *opaque, ResetType type);
|
||||
typedef struct ResettablePhases {
|
||||
ResettableEnterPhase enter;
|
||||
ResettableHoldPhase hold;
|
||||
ResettableExitPhase exit;
|
||||
} ResettablePhases;
|
||||
typedef struct ResettableClass {
|
||||
InterfaceClass parent_class;
|
||||
|
||||
/* Phase methods */
|
||||
ResettablePhases phases;
|
||||
|
||||
/* State access method */
|
||||
ResettableGetState get_state;
|
||||
|
||||
/* Transitional method for legacy reset compatibility */
|
||||
ResettableGetTrFunction get_transitional_function;
|
||||
|
||||
/* Hierarchy handling method */
|
||||
ResettableChildForeach child_foreach;
|
||||
} ResettableClass;
|
||||
|
||||
/**
|
||||
* ResettableState:
|
||||
* Structure holding reset related state. The fields should not be accessed
|
||||
* directly; the definition is here to allow further inclusion into other
|
||||
* objects.
|
||||
*
|
||||
* @count: Number of reset level the object is into. It is incremented when
|
||||
* the reset operation starts and decremented when it finishes.
|
||||
* @hold_phase_pending: flag which indicates that we need to invoke the 'hold'
|
||||
* phase handler for this object.
|
||||
* @exit_phase_in_progress: true if we are currently in the exit phase
|
||||
*/
|
||||
struct ResettableState {
|
||||
unsigned count;
|
||||
bool hold_phase_pending;
|
||||
bool exit_phase_in_progress;
|
||||
};
|
||||
|
||||
/**
|
||||
* resettable_state_clear:
|
||||
* Clear the state. It puts the state to the initial (zeroed) state required
|
||||
* to reuse an object. Typically used in realize step of base classes
|
||||
* implementing the interface.
|
||||
*/
|
||||
static inline void resettable_state_clear(ResettableState *state)
|
||||
{
|
||||
memset(state, 0, sizeof(ResettableState));
|
||||
}
|
||||
|
||||
/**
|
||||
* resettable_reset:
|
||||
* Trigger a reset on an object @obj of type @type. @obj must implement
|
||||
* Resettable interface.
|
||||
*
|
||||
* Calling this function is equivalent to calling @resettable_assert_reset()
|
||||
* then @resettable_release_reset().
|
||||
*/
|
||||
void resettable_reset(Object *obj, ResetType type);
|
||||
|
||||
/**
|
||||
* resettable_assert_reset:
|
||||
* Put an object @obj into reset. @obj must implement Resettable interface.
|
||||
*
|
||||
* @resettable_release_reset() must eventually be called after this call.
|
||||
* There must be one call to @resettable_release_reset() per call of
|
||||
* @resettable_assert_reset(), with the same type argument.
|
||||
*
|
||||
* NOTE: Until support for migration is added, the @resettable_release_reset()
|
||||
* must not be delayed. It must occur just after @resettable_assert_reset() so
|
||||
* that migration cannot be triggered in between. Prefer using
|
||||
* @resettable_reset() for now.
|
||||
*/
|
||||
void resettable_assert_reset(Object *obj, ResetType type);
|
||||
|
||||
/**
|
||||
* resettable_release_reset:
|
||||
* Release the object @obj from reset. @obj must implement Resettable interface.
|
||||
*
|
||||
* See @resettable_assert_reset() description for details.
|
||||
*/
|
||||
void resettable_release_reset(Object *obj, ResetType type);
|
||||
|
||||
/**
|
||||
* resettable_is_in_reset:
|
||||
* Return true if @obj is under reset.
|
||||
*
|
||||
* @obj must implement Resettable interface.
|
||||
*/
|
||||
bool resettable_is_in_reset(Object *obj);
|
||||
|
||||
/**
|
||||
* resettable_change_parent:
|
||||
* Indicate that the parent of Ressettable @obj is changing from @oldp to @newp.
|
||||
* All 3 objects must implement resettable interface. @oldp or @newp may be
|
||||
* NULL.
|
||||
*
|
||||
* This function will adapt the reset state of @obj so that it is coherent
|
||||
* with the reset state of @newp. It may trigger @resettable_assert_reset()
|
||||
* or @resettable_release_reset(). It will do such things only if the reset
|
||||
* state of @newp and @oldp are different.
|
||||
*
|
||||
* When using this function during reset, it must only be called during
|
||||
* a hold phase method. Calling this during enter or exit phase is an error.
|
||||
*/
|
||||
void resettable_change_parent(Object *obj, Object *newp, Object *oldp);
|
||||
|
||||
/**
|
||||
* resettable_cold_reset_fn:
|
||||
* Helper to call resettable_reset((Object *) opaque, RESET_TYPE_COLD).
|
||||
*
|
||||
* This function is typically useful to register a reset handler with
|
||||
* qemu_register_reset.
|
||||
*/
|
||||
void resettable_cold_reset_fn(void *opaque);
|
||||
|
||||
/**
|
||||
* resettable_class_set_parent_phases:
|
||||
*
|
||||
* Save @rc current reset phases into @parent_phases and override @rc phases
|
||||
* by the given new methods (@enter, @hold and @exit).
|
||||
* Each phase is overridden only if the new one is not NULL allowing to
|
||||
* override a subset of phases.
|
||||
*/
|
||||
void resettable_class_set_parent_phases(ResettableClass *rc,
|
||||
ResettableEnterPhase enter,
|
||||
ResettableHoldPhase hold,
|
||||
ResettableExitPhase exit,
|
||||
ResettablePhases *parent_phases);
|
||||
|
||||
#endif
|
@ -24,6 +24,7 @@ typedef struct AspeedSDHCIState {
|
||||
SysBusDevice parent;
|
||||
|
||||
SDHCIState slots[ASPEED_SDHCI_NUM_SLOTS];
|
||||
uint8_t num_slots;
|
||||
|
||||
MemoryRegion iomem;
|
||||
qemu_irq irq;
|
||||
|
@ -403,6 +403,15 @@ static uint32_t host_closefn(ARMCPU *cpu, GuestFD *gf)
|
||||
{
|
||||
CPUARMState *env = &cpu->env;
|
||||
|
||||
/*
|
||||
* Only close the underlying host fd if it's one we opened on behalf
|
||||
* of the guest in SYS_OPEN.
|
||||
*/
|
||||
if (gf->hostfd == STDIN_FILENO ||
|
||||
gf->hostfd == STDOUT_FILENO ||
|
||||
gf->hostfd == STDERR_FILENO) {
|
||||
return 0;
|
||||
}
|
||||
return set_swi_errno(env, close(gf->hostfd));
|
||||
}
|
||||
|
||||
|
@ -2551,6 +2551,7 @@ static void arm_max_initfn(Object *obj)
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvm_arm_set_cpu_features_from_host(cpu);
|
||||
kvm_arm_add_vcpu_properties(obj);
|
||||
} else {
|
||||
cortex_a15_initfn(obj);
|
||||
|
||||
@ -2743,6 +2744,7 @@ static void arm_host_initfn(Object *obj)
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
aarch64_add_sve_properties(obj);
|
||||
}
|
||||
kvm_arm_add_vcpu_properties(obj);
|
||||
arm_cpu_post_init(obj);
|
||||
}
|
||||
|
||||
|
@ -821,6 +821,13 @@ struct ARMCPU {
|
||||
/* KVM init features for this CPU */
|
||||
uint32_t kvm_init_features[7];
|
||||
|
||||
/* KVM CPU state */
|
||||
|
||||
/* KVM virtual time adjustment */
|
||||
bool kvm_adjvtime;
|
||||
bool kvm_vtime_dirty;
|
||||
uint64_t kvm_vtime;
|
||||
|
||||
/* Uniprocessor system with MP extensions */
|
||||
bool mp_is_up;
|
||||
|
||||
|
@ -605,6 +605,7 @@ static void aarch64_max_initfn(Object *obj)
|
||||
|
||||
if (kvm_enabled()) {
|
||||
kvm_arm_set_cpu_features_from_host(cpu);
|
||||
kvm_arm_add_vcpu_properties(obj);
|
||||
} else {
|
||||
uint64_t t;
|
||||
uint32_t u;
|
||||
|
120
target/arm/kvm.c
120
target/arm/kvm.c
@ -17,6 +17,8 @@
|
||||
#include "qemu/timer.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "qom/object.h"
|
||||
#include "qapi/error.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_int.h"
|
||||
@ -179,6 +181,32 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
|
||||
env->features = arm_host_cpu_features.features;
|
||||
}
|
||||
|
||||
static bool kvm_no_adjvtime_get(Object *obj, Error **errp)
|
||||
{
|
||||
return !ARM_CPU(obj)->kvm_adjvtime;
|
||||
}
|
||||
|
||||
static void kvm_no_adjvtime_set(Object *obj, bool value, Error **errp)
|
||||
{
|
||||
ARM_CPU(obj)->kvm_adjvtime = !value;
|
||||
}
|
||||
|
||||
/* KVM VCPU properties should be prefixed with "kvm-". */
|
||||
void kvm_arm_add_vcpu_properties(Object *obj)
|
||||
{
|
||||
if (!kvm_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ARM_CPU(obj)->kvm_adjvtime = true;
|
||||
object_property_add_bool(obj, "kvm-no-adjvtime", kvm_no_adjvtime_get,
|
||||
kvm_no_adjvtime_set, &error_abort);
|
||||
object_property_set_description(obj, "kvm-no-adjvtime",
|
||||
"Set on to disable the adjustment of "
|
||||
"the virtual counter. VM stopped time "
|
||||
"will be counted.", &error_abort);
|
||||
}
|
||||
|
||||
bool kvm_arm_pmu_supported(CPUState *cpu)
|
||||
{
|
||||
return kvm_check_extension(cpu->kvm_state, KVM_CAP_ARM_PMU_V3);
|
||||
@ -357,6 +385,22 @@ static int compare_u64(const void *a, const void *b)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* cpreg_values are sorted in ascending order by KVM register ID
|
||||
* (see kvm_arm_init_cpreg_list). This allows us to cheaply find
|
||||
* the storage for a KVM register by ID with a binary search.
|
||||
*/
|
||||
static uint64_t *kvm_arm_get_cpreg_ptr(ARMCPU *cpu, uint64_t regidx)
|
||||
{
|
||||
uint64_t *res;
|
||||
|
||||
res = bsearch(®idx, cpu->cpreg_indexes, cpu->cpreg_array_len,
|
||||
sizeof(uint64_t), compare_u64);
|
||||
assert(res);
|
||||
|
||||
return &cpu->cpreg_values[res - cpu->cpreg_indexes];
|
||||
}
|
||||
|
||||
/* Initialize the ARMCPU cpreg list according to the kernel's
|
||||
* definition of what CPU registers it knows about (and throw away
|
||||
* the previous TCG-created cpreg list).
|
||||
@ -510,6 +554,23 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level)
|
||||
return ok;
|
||||
}
|
||||
|
||||
void kvm_arm_cpu_pre_save(ARMCPU *cpu)
|
||||
{
|
||||
/* KVM virtual time adjustment */
|
||||
if (cpu->kvm_vtime_dirty) {
|
||||
*kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT) = cpu->kvm_vtime;
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arm_cpu_post_load(ARMCPU *cpu)
|
||||
{
|
||||
/* KVM virtual time adjustment */
|
||||
if (cpu->kvm_adjvtime) {
|
||||
cpu->kvm_vtime = *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT);
|
||||
cpu->kvm_vtime_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
void kvm_arm_reset_vcpu(ARMCPU *cpu)
|
||||
{
|
||||
int ret;
|
||||
@ -577,6 +638,50 @@ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arm_get_virtual_time(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
struct kvm_one_reg reg = {
|
||||
.id = KVM_REG_ARM_TIMER_CNT,
|
||||
.addr = (uintptr_t)&cpu->kvm_vtime,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (cpu->kvm_vtime_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
error_report("Failed to get KVM_REG_ARM_TIMER_CNT");
|
||||
abort();
|
||||
}
|
||||
|
||||
cpu->kvm_vtime_dirty = true;
|
||||
}
|
||||
|
||||
void kvm_arm_put_virtual_time(CPUState *cs)
|
||||
{
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
struct kvm_one_reg reg = {
|
||||
.id = KVM_REG_ARM_TIMER_CNT,
|
||||
.addr = (uintptr_t)&cpu->kvm_vtime,
|
||||
};
|
||||
int ret;
|
||||
|
||||
if (!cpu->kvm_vtime_dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
|
||||
if (ret) {
|
||||
error_report("Failed to set KVM_REG_ARM_TIMER_CNT");
|
||||
abort();
|
||||
}
|
||||
|
||||
cpu->kvm_vtime_dirty = false;
|
||||
}
|
||||
|
||||
int kvm_put_vcpu_events(ARMCPU *cpu)
|
||||
{
|
||||
CPUARMState *env = &cpu->env;
|
||||
@ -688,6 +793,21 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
|
||||
return MEMTXATTRS_UNSPECIFIED;
|
||||
}
|
||||
|
||||
void kvm_arm_vm_state_change(void *opaque, int running, RunState state)
|
||||
{
|
||||
CPUState *cs = opaque;
|
||||
ARMCPU *cpu = ARM_CPU(cs);
|
||||
|
||||
if (running) {
|
||||
if (cpu->kvm_adjvtime) {
|
||||
kvm_arm_put_virtual_time(cs);
|
||||
}
|
||||
} else {
|
||||
if (cpu->kvm_adjvtime) {
|
||||
kvm_arm_get_virtual_time(cs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
|
||||
{
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "qemu-common.h"
|
||||
#include "cpu.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_arm.h"
|
||||
#include "internals.h"
|
||||
@ -198,6 +199,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs);
|
||||
|
||||
/* Determine init features for this CPU */
|
||||
memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features));
|
||||
if (cpu->start_powered_off) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "qemu/host-utils.h"
|
||||
#include "qemu/main-loop.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "sysemu/runstate.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "sysemu/kvm_int.h"
|
||||
#include "kvm_arm.h"
|
||||
@ -604,6 +605,7 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
|
||||
set_feature(&features, ARM_FEATURE_NEON);
|
||||
set_feature(&features, ARM_FEATURE_AARCH64);
|
||||
set_feature(&features, ARM_FEATURE_PMU);
|
||||
set_feature(&features, ARM_FEATURE_GENERIC_TIMER);
|
||||
|
||||
ahcf->features = features;
|
||||
|
||||
@ -733,6 +735,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs);
|
||||
|
||||
/* Determine init features for this CPU */
|
||||
memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features));
|
||||
if (cpu->start_powered_off) {
|
||||
|
@ -28,9 +28,9 @@
|
||||
int kvm_arm_vcpu_init(CPUState *cs);
|
||||
|
||||
/**
|
||||
* kvm_arm_vcpu_finalize
|
||||
* kvm_arm_vcpu_finalize:
|
||||
* @cs: CPUState
|
||||
* @feature: int
|
||||
* @feature: feature to finalize
|
||||
*
|
||||
* Finalizes the configuration of the specified VCPU feature by
|
||||
* invoking the KVM_ARM_VCPU_FINALIZE ioctl. Features requiring
|
||||
@ -75,8 +75,8 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group,
|
||||
int kvm_arm_init_cpreg_list(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_reg_syncs_via_cpreg_list
|
||||
* regidx: KVM register index
|
||||
* kvm_arm_reg_syncs_via_cpreg_list:
|
||||
* @regidx: KVM register index
|
||||
*
|
||||
* Return true if this KVM register should be synchronized via the
|
||||
* cpreg list of arbitrary system registers, false if it is synchronized
|
||||
@ -85,8 +85,8 @@ int kvm_arm_init_cpreg_list(ARMCPU *cpu);
|
||||
bool kvm_arm_reg_syncs_via_cpreg_list(uint64_t regidx);
|
||||
|
||||
/**
|
||||
* kvm_arm_cpreg_level
|
||||
* regidx: KVM register index
|
||||
* kvm_arm_cpreg_level:
|
||||
* @regidx: KVM register index
|
||||
*
|
||||
* Return the level of this coprocessor/system register. Return value is
|
||||
* either KVM_PUT_RUNTIME_STATE, KVM_PUT_RESET_STATE, or KVM_PUT_FULL_STATE.
|
||||
@ -127,6 +127,23 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level);
|
||||
*/
|
||||
bool write_kvmstate_to_list(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_cpu_pre_save:
|
||||
* @cpu: ARMCPU
|
||||
*
|
||||
* Called after write_kvmstate_to_list() from cpu_pre_save() to update
|
||||
* the cpreg list with KVM CPU state.
|
||||
*/
|
||||
void kvm_arm_cpu_pre_save(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_cpu_post_load:
|
||||
* @cpu: ARMCPU
|
||||
*
|
||||
* Called from cpu_post_load() to update KVM CPU state from the cpreg list.
|
||||
*/
|
||||
void kvm_arm_cpu_post_load(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_reset_vcpu:
|
||||
* @cpu: ARMCPU
|
||||
@ -148,6 +165,8 @@ void kvm_arm_init_serror_injection(CPUState *cs);
|
||||
* @cpu: ARMCPU
|
||||
*
|
||||
* Get VCPU related state from kvm.
|
||||
*
|
||||
* Returns: 0 if success else < 0 error code
|
||||
*/
|
||||
int kvm_get_vcpu_events(ARMCPU *cpu);
|
||||
|
||||
@ -156,6 +175,8 @@ int kvm_get_vcpu_events(ARMCPU *cpu);
|
||||
* @cpu: ARMCPU
|
||||
*
|
||||
* Put VCPU related state to kvm.
|
||||
*
|
||||
* Returns: 0 if success else < 0 error code
|
||||
*/
|
||||
int kvm_put_vcpu_events(ARMCPU *cpu);
|
||||
|
||||
@ -205,10 +226,12 @@ typedef struct ARMHostCPUFeatures {
|
||||
|
||||
/**
|
||||
* kvm_arm_get_host_cpu_features:
|
||||
* @ahcc: ARMHostCPUClass to fill in
|
||||
* @ahcf: ARMHostCPUClass to fill in
|
||||
*
|
||||
* Probe the capabilities of the host kernel's preferred CPU and fill
|
||||
* in the ARMHostCPUClass struct accordingly.
|
||||
*
|
||||
* Returns true on success and false otherwise.
|
||||
*/
|
||||
bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf);
|
||||
|
||||
@ -232,6 +255,15 @@ void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map);
|
||||
*/
|
||||
void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_add_vcpu_properties:
|
||||
* @obj: The CPU object to add the properties to
|
||||
*
|
||||
* Add all KVM specific CPU properties to the CPU object. These
|
||||
* are the CPU properties with "kvm-" prefixed names.
|
||||
*/
|
||||
void kvm_arm_add_vcpu_properties(Object *obj);
|
||||
|
||||
/**
|
||||
* kvm_arm_aarch32_supported:
|
||||
* @cs: CPUState
|
||||
@ -242,7 +274,7 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu);
|
||||
bool kvm_arm_aarch32_supported(CPUState *cs);
|
||||
|
||||
/**
|
||||
* bool kvm_arm_pmu_supported:
|
||||
* kvm_arm_pmu_supported:
|
||||
* @cs: CPUState
|
||||
*
|
||||
* Returns: true if the KVM VCPU can enable its PMU
|
||||
@ -251,7 +283,7 @@ bool kvm_arm_aarch32_supported(CPUState *cs);
|
||||
bool kvm_arm_pmu_supported(CPUState *cs);
|
||||
|
||||
/**
|
||||
* bool kvm_arm_sve_supported:
|
||||
* kvm_arm_sve_supported:
|
||||
* @cs: CPUState
|
||||
*
|
||||
* Returns true if the KVM VCPU can enable SVE and false otherwise.
|
||||
@ -259,29 +291,51 @@ bool kvm_arm_pmu_supported(CPUState *cs);
|
||||
bool kvm_arm_sve_supported(CPUState *cs);
|
||||
|
||||
/**
|
||||
* kvm_arm_get_max_vm_ipa_size - Returns the number of bits in the
|
||||
* IPA address space supported by KVM
|
||||
*
|
||||
* kvm_arm_get_max_vm_ipa_size:
|
||||
* @ms: Machine state handle
|
||||
*
|
||||
* Returns the number of bits in the IPA address space supported by KVM
|
||||
*/
|
||||
int kvm_arm_get_max_vm_ipa_size(MachineState *ms);
|
||||
|
||||
/**
|
||||
* kvm_arm_sync_mpstate_to_kvm
|
||||
* kvm_arm_sync_mpstate_to_kvm:
|
||||
* @cpu: ARMCPU
|
||||
*
|
||||
* If supported set the KVM MP_STATE based on QEMU's model.
|
||||
*
|
||||
* Returns 0 on success and -1 on failure.
|
||||
*/
|
||||
int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_sync_mpstate_to_qemu
|
||||
* kvm_arm_sync_mpstate_to_qemu:
|
||||
* @cpu: ARMCPU
|
||||
*
|
||||
* If supported get the MP_STATE from KVM and store in QEMU's model.
|
||||
*
|
||||
* Returns 0 on success and aborts on failure.
|
||||
*/
|
||||
int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu);
|
||||
|
||||
/**
|
||||
* kvm_arm_get_virtual_time:
|
||||
* @cs: CPUState
|
||||
*
|
||||
* Gets the VCPU's virtual counter and stores it in the KVM CPU state.
|
||||
*/
|
||||
void kvm_arm_get_virtual_time(CPUState *cs);
|
||||
|
||||
/**
|
||||
* kvm_arm_put_virtual_time:
|
||||
* @cs: CPUState
|
||||
*
|
||||
* Sets the VCPU's virtual counter to the value stored in the KVM CPU state.
|
||||
*/
|
||||
void kvm_arm_put_virtual_time(CPUState *cs);
|
||||
|
||||
void kvm_arm_vm_state_change(void *opaque, int running, RunState state);
|
||||
|
||||
int kvm_arm_vgic_probe(void);
|
||||
|
||||
void kvm_arm_pmu_set_irq(CPUState *cs, int irq);
|
||||
@ -292,13 +346,16 @@ int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level);
|
||||
|
||||
static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu)
|
||||
{
|
||||
/* This should never actually be called in the "not KVM" case,
|
||||
/*
|
||||
* This should never actually be called in the "not KVM" case,
|
||||
* but set up the fields to indicate an error anyway.
|
||||
*/
|
||||
cpu->kvm_target = QEMU_KVM_ARM_TARGET_NONE;
|
||||
cpu->host_cpu_probe_failed = true;
|
||||
}
|
||||
|
||||
static inline void kvm_arm_add_vcpu_properties(Object *obj) {}
|
||||
|
||||
static inline bool kvm_arm_aarch32_supported(CPUState *cs)
|
||||
{
|
||||
return false;
|
||||
@ -328,6 +385,9 @@ static inline void kvm_arm_pmu_set_irq(CPUState *cs, int irq) {}
|
||||
static inline void kvm_arm_pmu_init(CPUState *cs) {}
|
||||
|
||||
static inline void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map) {}
|
||||
|
||||
static inline void kvm_arm_get_virtual_time(CPUState *cs) {}
|
||||
static inline void kvm_arm_put_virtual_time(CPUState *cs) {}
|
||||
#endif
|
||||
|
||||
static inline const char *gic_class_name(void)
|
||||
@ -377,23 +437,20 @@ bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit);
|
||||
*
|
||||
* Return: TRUE if any hardware breakpoints in use.
|
||||
*/
|
||||
|
||||
bool kvm_arm_hw_debug_active(CPUState *cs);
|
||||
|
||||
/**
|
||||
* kvm_arm_copy_hw_debug_data:
|
||||
*
|
||||
* @ptr: kvm_guest_debug_arch structure
|
||||
*
|
||||
* Copy the architecture specific debug registers into the
|
||||
* kvm_guest_debug ioctl structure.
|
||||
*/
|
||||
struct kvm_guest_debug_arch;
|
||||
|
||||
void kvm_arm_copy_hw_debug_data(struct kvm_guest_debug_arch *ptr);
|
||||
|
||||
/**
|
||||
* its_class_name
|
||||
* its_class_name:
|
||||
*
|
||||
* Return the ITS class name to use depending on whether KVM acceleration
|
||||
* and KVM CAP_SIGNAL_MSI are supported
|
||||
|
@ -642,6 +642,12 @@ static int cpu_pre_save(void *opaque)
|
||||
/* This should never fail */
|
||||
abort();
|
||||
}
|
||||
|
||||
/*
|
||||
* kvm_arm_cpu_pre_save() must be called after
|
||||
* write_kvmstate_to_list()
|
||||
*/
|
||||
kvm_arm_cpu_pre_save(cpu);
|
||||
} else {
|
||||
if (!write_cpustate_to_list(cpu, false)) {
|
||||
/* This should never fail. */
|
||||
@ -744,6 +750,7 @@ static int cpu_post_load(void *opaque, int version_id)
|
||||
* we're using it.
|
||||
*/
|
||||
write_list_to_cpustate(cpu);
|
||||
kvm_arm_cpu_post_load(cpu);
|
||||
} else {
|
||||
if (!write_list_to_cpustate(cpu)) {
|
||||
return -1;
|
||||
|
@ -103,6 +103,7 @@ static const char *cpu_model_advertised_features[] = {
|
||||
"sve128", "sve256", "sve384", "sve512",
|
||||
"sve640", "sve768", "sve896", "sve1024", "sve1152", "sve1280",
|
||||
"sve1408", "sve1536", "sve1664", "sve1792", "sve1920", "sve2048",
|
||||
"kvm-no-adjvtime",
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -429,6 +429,7 @@ tests/fp/%:
|
||||
tests/test-qdev-global-props$(EXESUF): tests/test-qdev-global-props.o \
|
||||
hw/core/qdev.o hw/core/qdev-properties.o hw/core/hotplug.o\
|
||||
hw/core/bus.o \
|
||||
hw/core/resettable.o \
|
||||
hw/core/irq.o \
|
||||
hw/core/fw-path-provider.o \
|
||||
hw/core/reset.o \
|
||||
|
@ -159,6 +159,25 @@ static bool resp_get_feature(QDict *resp, const char *feature)
|
||||
qobject_unref(_resp); \
|
||||
})
|
||||
|
||||
#define assert_feature(qts, cpu_type, feature, expected_value) \
|
||||
({ \
|
||||
QDict *_resp, *_props; \
|
||||
\
|
||||
_resp = do_query_no_props(qts, cpu_type); \
|
||||
g_assert(_resp); \
|
||||
g_assert(resp_has_props(_resp)); \
|
||||
_props = resp_get_props(_resp); \
|
||||
g_assert(qdict_get(_props, feature)); \
|
||||
g_assert(qdict_get_bool(_props, feature) == (expected_value)); \
|
||||
qobject_unref(_resp); \
|
||||
})
|
||||
|
||||
#define assert_has_feature_enabled(qts, cpu_type, feature) \
|
||||
assert_feature(qts, cpu_type, feature, true)
|
||||
|
||||
#define assert_has_feature_disabled(qts, cpu_type, feature) \
|
||||
assert_feature(qts, cpu_type, feature, false)
|
||||
|
||||
static void assert_type_full(QTestState *qts)
|
||||
{
|
||||
const char *error;
|
||||
@ -405,16 +424,18 @@ static void test_query_cpu_model_expansion(const void *data)
|
||||
assert_error(qts, "host", "The CPU type 'host' requires KVM", NULL);
|
||||
|
||||
/* Test expected feature presence/absence for some cpu types */
|
||||
assert_has_feature(qts, "max", "pmu");
|
||||
assert_has_feature(qts, "cortex-a15", "pmu");
|
||||
assert_has_feature_enabled(qts, "max", "pmu");
|
||||
assert_has_feature_enabled(qts, "cortex-a15", "pmu");
|
||||
assert_has_not_feature(qts, "cortex-a15", "aarch64");
|
||||
|
||||
assert_has_not_feature(qts, "max", "kvm-no-adjvtime");
|
||||
|
||||
if (g_str_equal(qtest_get_arch(), "aarch64")) {
|
||||
assert_has_feature(qts, "max", "aarch64");
|
||||
assert_has_feature(qts, "max", "sve");
|
||||
assert_has_feature(qts, "max", "sve128");
|
||||
assert_has_feature(qts, "cortex-a57", "pmu");
|
||||
assert_has_feature(qts, "cortex-a57", "aarch64");
|
||||
assert_has_feature_enabled(qts, "max", "aarch64");
|
||||
assert_has_feature_enabled(qts, "max", "sve");
|
||||
assert_has_feature_enabled(qts, "max", "sve128");
|
||||
assert_has_feature_enabled(qts, "cortex-a57", "pmu");
|
||||
assert_has_feature_enabled(qts, "cortex-a57", "aarch64");
|
||||
|
||||
sve_tests_default(qts, "max");
|
||||
|
||||
@ -443,6 +464,8 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
|
||||
return;
|
||||
}
|
||||
|
||||
assert_has_feature_disabled(qts, "host", "kvm-no-adjvtime");
|
||||
|
||||
if (g_str_equal(qtest_get_arch(), "aarch64")) {
|
||||
bool kvm_supports_sve;
|
||||
char max_name[8], name[8];
|
||||
@ -451,8 +474,8 @@ static void test_query_cpu_model_expansion_kvm(const void *data)
|
||||
QDict *resp;
|
||||
char *error;
|
||||
|
||||
assert_has_feature(qts, "host", "aarch64");
|
||||
assert_has_feature(qts, "host", "pmu");
|
||||
assert_has_feature_enabled(qts, "host", "aarch64");
|
||||
assert_has_feature_enabled(qts, "host", "pmu");
|
||||
|
||||
assert_error(qts, "cortex-a15",
|
||||
"We cannot guarantee the CPU type 'cortex-a15' works "
|
||||
|
10
vl.c
10
vl.c
@ -4381,7 +4381,15 @@ int main(int argc, char **argv, char **envp)
|
||||
|
||||
/* TODO: once all bus devices are qdevified, this should be done
|
||||
* when bus is created by qdev.c */
|
||||
qemu_register_reset(qbus_reset_all_fn, sysbus_get_default());
|
||||
/*
|
||||
* TODO: If we had a main 'reset container' that the whole system
|
||||
* lived in, we could reset that using the multi-phase reset
|
||||
* APIs. For the moment, we just reset the sysbus, which will cause
|
||||
* all devices hanging off it (and all their child buses, recursively)
|
||||
* to be reset. Note that this will *not* reset any Device objects
|
||||
* which are not attached to some part of the qbus tree!
|
||||
*/
|
||||
qemu_register_reset(resettable_cold_reset_fn, sysbus_get_default());
|
||||
qemu_run_machine_init_done_notifiers();
|
||||
|
||||
if (rom_check_and_register_reset() != 0) {
|
||||
|
Loading…
Reference in New Issue
Block a user