mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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
|
||||
|
||||
@@ -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.
|
||||
+57
-15
@@ -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,
|
||||
|
||||
+28
-3
@@ -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
@@ -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)
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+150
-10
@@ -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 },
|
||||
{ }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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)"
|
||||
|
||||
+1
-1
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -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);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user