aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2021-01-29 17:22:52 +0000
committerPeter Maydell <peter.maydell@linaro.org>2021-01-29 17:22:53 +0000
commit9df52f58e76e904fb141b10318362d718f470db2 (patch)
treeea3d1eaa9724304ba2b634c3af34f76537331ea2
parent3701c07e63bb945137bf80fe35e7058ad3784c45 (diff)
parent14711b6f54708b9583796db02b12ee7bd0331502 (diff)
downloadqemu-9df52f58e76e904fb141b10318362d718f470db2.zip
qemu-9df52f58e76e904fb141b10318362d718f470db2.tar.gz
qemu-9df52f58e76e904fb141b10318362d718f470db2.tar.bz2
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20210129-1' into staging
target-arm queue: * Implement ID_PFR2 * Conditionalize DBGDIDR * rename xlnx-zcu102.canbusN properties * provide powerdown/reset mechanism for secure firmware on 'virt' board * hw/misc: Fix arith overflow in NPCM7XX PWM module * target/arm: Replace magic value by MMU_DATA_LOAD definition * configure: fix preadv errors on Catalina macOS with new XCode * Various configure and other cleanups in preparation for iOS support * hvf: Add hypervisor entitlement to output binaries (needed for Big Sur) * Implement pvpanic-pci device * Convert the CMSDK timer devices to the Clock framework # gpg: Signature made Fri 29 Jan 2021 16:08:02 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-20210129-1: (46 commits) hw/arm/stellaris: Remove board-creation reset of STELLARIS_SYS arm: Remove frq properties on CMSDK timer, dualtimer, watchdog, ARMSSE arm: Don't set freq properties on CMSDK timer, dualtimer, watchdog, ARMSSE hw/arm/armsse: Use Clock to set system_clock_scale tests/qtest/cmsdk-apb-watchdog-test: Test clock changes hw/watchdog/cmsdk-apb-watchdog: Convert to use Clock input hw/timer/cmsdk-apb-dualtimer: Convert to use Clock input hw/timer/cmsdk-apb-timer: Convert to use Clock input hw/arm/stellaris: Create Clock input for watchdog hw/arm/stellaris: Convert SSYS to QOM device hw/arm/musca: Create and connect ARMSSE Clocks hw/arm/mps2-tz: Create and connect ARMSSE Clocks hw/arm/mps2: Create and connect SYSCLK Clock hw/arm/mps2: Inline CMSDK_APB_TIMER creation hw/arm/armsse: Wire up clocks hw/arm/armsse: Rename "MAINCLK" property to "MAINCLK_FRQ" hw/watchdog/cmsdk-apb-watchdog: Add Clock input hw/timer/cmsdk-apb-dualtimer: Add Clock input hw/timer/cmsdk-apb-timer: Add Clock input hw/timer/cmsdk-apb-timer: Rename CMSDKAPBTIMER struct to CMSDKAPBTimer ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
-rw-r--r--MAINTAINERS3
-rw-r--r--accel/hvf/entitlements.plist8
-rwxr-xr-xconfigure78
-rw-r--r--docs/devel/clocks.rst16
-rw-r--r--docs/specs/pci-ids.txt1
-rw-r--r--docs/specs/pvpanic.txt13
-rw-r--r--docs/system/arm/virt.rst2
-rw-r--r--hw/arm/Kconfig1
-rw-r--r--hw/arm/armsse.c48
-rw-r--r--hw/arm/mps2-tz.c14
-rw-r--r--hw/arm/mps2.c28
-rw-r--r--hw/arm/musca.c13
-rw-r--r--hw/arm/stellaris.c170
-rw-r--r--hw/arm/virt.c111
-rw-r--r--hw/arm/xlnx-zcu102.c4
-rw-r--r--hw/core/ptimer.c34
-rw-r--r--hw/gpio/Kconfig3
-rw-r--r--hw/gpio/gpio_pwr.c70
-rw-r--r--hw/gpio/meson.build1
-rw-r--r--hw/i386/Kconfig2
-rw-r--r--hw/misc/Kconfig12
-rw-r--r--hw/misc/meson.build4
-rw-r--r--hw/misc/npcm7xx_pwm.c23
-rw-r--r--hw/misc/pvpanic-isa.c94
-rw-r--r--hw/misc/pvpanic-pci.c94
-rw-r--r--hw/misc/pvpanic.c85
-rw-r--r--hw/timer/cmsdk-apb-dualtimer.c53
-rw-r--r--hw/timer/cmsdk-apb-timer.c55
-rw-r--r--hw/watchdog/cmsdk-apb-watchdog.c29
-rw-r--r--include/hw/arm/armsse.h14
-rw-r--r--include/hw/arm/virt.h2
-rw-r--r--include/hw/clock.h15
-rw-r--r--include/hw/misc/pvpanic.h24
-rw-r--r--include/hw/pci/pci.h1
-rw-r--r--include/hw/ptimer.h22
-rw-r--r--include/hw/timer/cmsdk-apb-dualtimer.h5
-rw-r--r--include/hw/timer/cmsdk-apb-timer.h34
-rw-r--r--include/hw/watchdog/cmsdk-apb-watchdog.h5
-rw-r--r--include/qemu/osdep.h12
-rw-r--r--include/qemu/typedefs.h1
-rw-r--r--meson.build34
-rwxr-xr-xscripts/entitlement.sh13
-rw-r--r--target/arm/cpu.h1
-rw-r--r--target/arm/helper.c27
-rw-r--r--target/arm/kvm64.c2
-rw-r--r--tests/qtest/cmsdk-apb-dualtimer-test.c130
-rw-r--r--tests/qtest/cmsdk-apb-timer-test.c75
-rw-r--r--tests/qtest/cmsdk-apb-watchdog-test.c131
-rw-r--r--tests/qtest/meson.build6
-rw-r--r--tests/qtest/npcm7xx_pwm-test.c4
-rw-r--r--tests/qtest/pvpanic-pci-test.c98
-rw-r--r--tests/qtest/xlnx-can-test.c30
52 files changed, 1436 insertions, 319 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index fbb228e..bcd8866 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -581,12 +581,15 @@ F: include/hw/rtc/pl031.h
F: include/hw/arm/primecell.h
F: hw/timer/cmsdk-apb-timer.c
F: include/hw/timer/cmsdk-apb-timer.h
+F: tests/qtest/cmsdk-apb-timer-test.c
F: hw/timer/cmsdk-apb-dualtimer.c
F: include/hw/timer/cmsdk-apb-dualtimer.h
+F: tests/qtest/cmsdk-apb-dualtimer-test.c
F: hw/char/cmsdk-apb-uart.c
F: include/hw/char/cmsdk-apb-uart.h
F: hw/watchdog/cmsdk-apb-watchdog.c
F: include/hw/watchdog/cmsdk-apb-watchdog.h
+F: tests/qtest/cmsdk-apb-watchdog-test.c
F: hw/misc/tz-ppc.c
F: include/hw/misc/tz-ppc.h
F: hw/misc/tz-mpc.c
diff --git a/accel/hvf/entitlements.plist b/accel/hvf/entitlements.plist
new file mode 100644
index 0000000..154f330
--- /dev/null
+++ b/accel/hvf/entitlements.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>com.apple.security.hypervisor</key>
+ <true/>
+</dict>
+</plist>
diff --git a/configure b/configure
index 87de49e..dd22250 100755
--- a/configure
+++ b/configure
@@ -238,6 +238,7 @@ cpu=""
iasl="iasl"
interp_prefix="/usr/gnemul/qemu-%M"
static="no"
+cross_compile="no"
cross_prefix=""
audio_drv_list=""
block_drv_rw_whitelist=""
@@ -318,6 +319,7 @@ fdt="auto"
netmap="no"
sdl="auto"
sdl_image="auto"
+coreaudio="auto"
virtiofsd="auto"
virtfs="auto"
libudev="auto"
@@ -469,6 +471,7 @@ for opt do
optarg=$(expr "x$opt" : 'x[^=]*=\(.*\)')
case "$opt" in
--cross-prefix=*) cross_prefix="$optarg"
+ cross_compile="yes"
;;
--cc=*) CC="$optarg"
;;
@@ -622,13 +625,6 @@ fi
# the correct CPU with the --cpu option.
case $targetos in
Darwin)
- # on Leopard most of the system is 32-bit, so we have to ask the kernel if we can
- # run 64-bit userspace code.
- # If the user didn't specify a CPU explicitly and the kernel says this is
- # 64 bit hw, then assume x86_64. Otherwise fall through to the usual detection code.
- if test -z "$cpu" && test "$(sysctl -n hw.optional.x86_64)" = "1"; then
- cpu="x86_64"
- fi
HOST_DSOSUF=".dylib"
;;
SunOS)
@@ -772,13 +768,8 @@ OpenBSD)
Darwin)
bsd="yes"
darwin="yes"
- if [ "$cpu" = "x86_64" ] ; then
- QEMU_CFLAGS="-arch x86_64 $QEMU_CFLAGS"
- QEMU_LDFLAGS="-arch x86_64 $QEMU_LDFLAGS"
- fi
- audio_drv_list="coreaudio try-sdl"
+ audio_drv_list="try-coreaudio try-sdl"
audio_possible_drivers="coreaudio sdl"
- QEMU_LDFLAGS="-framework CoreFoundation -framework IOKit $QEMU_LDFLAGS"
# Disable attempts to use ObjectiveC features in os/object.h since they
# won't work when we're compiling with gcc as a C compiler.
QEMU_CFLAGS="-DOS_OBJECT_USE_OBJC=0 $QEMU_CFLAGS"
@@ -1691,7 +1682,7 @@ $(echo Deprecated targets: $deprecated_targets_list | \
--target-list-exclude=LIST exclude a set of targets from the default target-list
Advanced options (experts only):
- --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]
+ --cross-prefix=PREFIX use PREFIX for compile tools, PREFIX can be blank [$cross_prefix]
--cc=CC use C compiler CC [$cc]
--iasl=IASL use ACPI compiler IASL [$iasl]
--host-cc=CC use C compiler CC [$host_cc] for code run at
@@ -3146,6 +3137,24 @@ EOF
fi
##########################################
+# detect CoreAudio
+if test "$coreaudio" != "no" ; then
+ coreaudio_libs="-framework CoreAudio"
+ cat > $TMPC << EOF
+#include <CoreAudio/CoreAudio.h>
+int main(void)
+{
+ return (int)AudioGetCurrentHostTime();
+}
+EOF
+ if compile_prog "" "$coreaudio_libs" ; then
+ coreaudio=yes
+ else
+ coreaudio=no
+ fi
+fi
+
+##########################################
# Sound support libraries probe
audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/,/ /g')
@@ -3201,8 +3210,20 @@ for drv in $audio_drv_list; do
fi
;;
- coreaudio)
+ coreaudio | try-coreaudio)
+ if test "$coreaudio" = "no"; then
+ if test "$drv" = "try-coreaudio"; then
+ audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-coreaudio//')
+ else
+ error_exit "$drv check failed" \
+ "Make sure to have the $drv is available."
+ fi
+ else
coreaudio_libs="-framework CoreAudio"
+ if test "$drv" = "try-coreaudio"; then
+ audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-coreaudio/coreaudio/')
+ fi
+ fi
;;
dsound)
@@ -3532,19 +3553,6 @@ if compile_prog "" "" ; then
fi
##########################################
-# preadv probe
-cat > $TMPC <<EOF
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
-int main(void) { return preadv(0, 0, 0, 0); }
-EOF
-preadv=no
-if compile_prog "" "" ; then
- preadv=yes
-fi
-
-##########################################
# fdt probe
case "$fdt" in
@@ -5748,9 +5756,6 @@ fi
if test "$iovec" = "yes" ; then
echo "CONFIG_IOVEC=y" >> $config_host_mak
fi
-if test "$preadv" = "yes" ; then
- echo "CONFIG_PREADV=y" >> $config_host_mak
-fi
if test "$membarrier" = "yes" ; then
echo "CONFIG_MEMBARRIER=y" >> $config_host_mak
fi
@@ -6297,6 +6302,7 @@ echo "cpp_link_args = [${LDFLAGS:+$(meson_quote $LDFLAGS)}]" >> $cross
echo "[binaries]" >> $cross
echo "c = [$(meson_quote $cc)]" >> $cross
test -n "$cxx" && echo "cpp = [$(meson_quote $cxx)]" >> $cross
+test -n "$objcc" && echo "objc = [$(meson_quote $objcc)]" >> $cross
echo "ar = [$(meson_quote $ar)]" >> $cross
echo "nm = [$(meson_quote $nm)]" >> $cross
echo "pkgconfig = [$(meson_quote $pkg_config_exe)]" >> $cross
@@ -6306,7 +6312,7 @@ if has $sdl2_config; then
fi
echo "strip = [$(meson_quote $strip)]" >> $cross
echo "windres = [$(meson_quote $windres)]" >> $cross
-if test -n "$cross_prefix"; then
+if test "$cross_compile" = "yes"; then
cross_arg="--cross-file config-meson.cross"
echo "[host_machine]" >> $cross
if test "$mingw32" = "yes" ; then
@@ -6315,10 +6321,16 @@ if test -n "$cross_prefix"; then
if test "$linux" = "yes" ; then
echo "system = 'linux'" >> $cross
fi
+ if test "$darwin" = "yes" ; then
+ echo "system = 'darwin'" >> $cross
+ fi
case "$ARCH" in
- i386|x86_64)
+ i386)
echo "cpu_family = 'x86'" >> $cross
;;
+ x86_64)
+ echo "cpu_family = 'x86_64'" >> $cross
+ ;;
ppc64le)
echo "cpu_family = 'ppc64'" >> $cross
;;
diff --git a/docs/devel/clocks.rst b/docs/devel/clocks.rst
index 2548d84..c54bbb8 100644
--- a/docs/devel/clocks.rst
+++ b/docs/devel/clocks.rst
@@ -235,6 +235,22 @@ object during device instance init. For example:
/* set initial value to 10ns / 100MHz */
clock_set_ns(clk, 10);
+To enforce that the clock is wired up by the board code, you can
+call ``clock_has_source()`` in your device's realize method:
+
+.. code-block:: c
+
+ if (!clock_has_source(s->clk)) {
+ error_setg(errp, "MyDevice: clk input must be connected");
+ return;
+ }
+
+Note that this only checks that the clock has been wired up; it is
+still possible that the output clock connected to it is disabled
+or has not yet been configured, in which case the period will be
+zero. You should use the clock callback to find out when the clock
+period changes.
+
Fetching clock frequency/period
-------------------------------
diff --git a/docs/specs/pci-ids.txt b/docs/specs/pci-ids.txt
index abbdbca..5e407a6 100644
--- a/docs/specs/pci-ids.txt
+++ b/docs/specs/pci-ids.txt
@@ -64,6 +64,7 @@ PCI devices (other than virtio):
1b36:000d PCI xhci usb host adapter
1b36:000f mdpy (mdev sample device), linux/samples/vfio-mdev/mdpy.c
1b36:0010 PCIe NVMe device (-device nvme)
+1b36:0011 PCI PVPanic device (-device pvpanic-pci)
All these devices are documented in docs/specs.
diff --git a/docs/specs/pvpanic.txt b/docs/specs/pvpanic.txt
index a90fbca..8afcde1 100644
--- a/docs/specs/pvpanic.txt
+++ b/docs/specs/pvpanic.txt
@@ -1,7 +1,7 @@
PVPANIC DEVICE
==============
-pvpanic device is a simulated ISA device, through which a guest panic
+pvpanic device is a simulated device, through which a guest panic
event is sent to qemu, and a QMP event is generated. This allows
management apps (e.g. libvirt) to be notified and respond to the event.
@@ -9,6 +9,9 @@ The management app has the option of waiting for GUEST_PANICKED events,
and/or polling for guest-panicked RunState, to learn when the pvpanic
device has fired a panic event.
+The pvpanic device can be implemented as an ISA device (using IOPORT) or as a
+PCI device.
+
ISA Interface
-------------
@@ -24,6 +27,14 @@ bit 1: a guest panic has happened and will be handled by the guest;
the host should record it or report it, but should not affect
the execution of the guest.
+PCI Interface
+-------------
+
+The PCI interface is similar to the ISA interface except that it uses an MMIO
+address space provided by its BAR0, 1 byte long. Any machine with a PCI bus
+can enable a pvpanic device by adding '-device pvpanic-pci' to the command
+line.
+
ACPI Interface
--------------
diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst
index 32dc5eb..27652ad 100644
--- a/docs/system/arm/virt.rst
+++ b/docs/system/arm/virt.rst
@@ -43,6 +43,8 @@ The virt board supports:
- Secure-World-only devices if the CPU has TrustZone:
- A second PL011 UART
+ - A second PL061 GPIO controller, with GPIO lines for triggering
+ a system reset or system poweroff
- A secure flash memory
- 16MB of secure RAM
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 0a242e4..13cc42d 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -17,6 +17,7 @@ config ARM_VIRT
select PL011 # UART
select PL031 # RTC
select PL061 # GPIO
+ select GPIO_PWR
select PLATFORM_BUS
select SMBIOS
select VIRTIO_MMIO
diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c
index baac027..26e1a8c 100644
--- a/hw/arm/armsse.c
+++ b/hw/arm/armsse.c
@@ -21,6 +21,7 @@
#include "hw/arm/armsse.h"
#include "hw/arm/boot.h"
#include "hw/irq.h"
+#include "hw/qdev-clock.h"
/* Format of the System Information block SYS_CONFIG register */
typedef enum SysConfigFormat {
@@ -47,7 +48,6 @@ static Property iotkit_properties[] = {
DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64),
- DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0),
DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15),
DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000),
DEFINE_PROP_BOOL("CPU0_FPU", ARMSSE, cpu_fpu[0], true),
@@ -59,7 +59,6 @@ static Property armsse_properties[] = {
DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION,
MemoryRegion *),
DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64),
- DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0),
DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15),
DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000),
DEFINE_PROP_BOOL("CPU0_FPU", ARMSSE, cpu_fpu[0], false),
@@ -231,6 +230,16 @@ static void armsse_forward_sec_resp_cfg(ARMSSE *s)
qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in);
}
+static void armsse_mainclk_update(void *opaque)
+{
+ ARMSSE *s = ARM_SSE(opaque);
+ /*
+ * Set system_clock_scale from our Clock input; this is what
+ * controls the tick rate of the CPU SysTick timer.
+ */
+ system_clock_scale = clock_ticks_to_ns(s->mainclk, 1);
+}
+
static void armsse_init(Object *obj)
{
ARMSSE *s = ARM_SSE(obj);
@@ -241,6 +250,10 @@ static void armsse_init(Object *obj)
assert(info->sram_banks <= MAX_SRAM_BANKS);
assert(info->num_cpus <= SSE_MAX_CPUS);
+ s->mainclk = qdev_init_clock_in(DEVICE(s), "MAINCLK",
+ armsse_mainclk_update, s);
+ s->s32kclk = qdev_init_clock_in(DEVICE(s), "S32KCLK", NULL, NULL);
+
memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX);
for (i = 0; i < info->num_cpus; i++) {
@@ -447,9 +460,11 @@ static void armsse_realize(DeviceState *dev, Error **errp)
return;
}
- if (!s->mainclk_frq) {
- error_setg(errp, "MAINCLK property was not set");
- return;
+ if (!clock_has_source(s->mainclk)) {
+ error_setg(errp, "MAINCLK clock was not connected");
+ }
+ if (!clock_has_source(s->s32kclk)) {
+ error_setg(errp, "S32KCLK clock was not connected");
}
assert(info->num_cpus <= SSE_MAX_CPUS);
@@ -710,7 +725,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
* it to the appropriate PPC port; then we can realize the PPC and
* map its upstream ends to the right place in the container.
*/
- qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq);
+ qdev_connect_clock_in(DEVICE(&s->timer0), "pclk", s->mainclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->timer0), errp)) {
return;
}
@@ -720,7 +735,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
object_property_set_link(OBJECT(&s->apb_ppc0), "port[0]", OBJECT(mr),
&error_abort);
- qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq);
+ qdev_connect_clock_in(DEVICE(&s->timer1), "pclk", s->mainclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->timer1), errp)) {
return;
}
@@ -730,7 +745,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
object_property_set_link(OBJECT(&s->apb_ppc0), "port[1]", OBJECT(mr),
&error_abort);
- qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq);
+ qdev_connect_clock_in(DEVICE(&s->dualtimer), "TIMCLK", s->mainclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->dualtimer), errp)) {
return;
}
@@ -888,7 +903,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
/* Devices behind APB PPC1:
* 0x4002f000: S32K timer
*/
- qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK);
+ qdev_connect_clock_in(DEVICE(&s->s32ktimer), "pclk", s->s32kclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->s32ktimer), errp)) {
return;
}
@@ -981,7 +996,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0,
qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0));
- qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK);
+ qdev_connect_clock_in(DEVICE(&s->s32kwatchdog), "WDOGCLK", s->s32kclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->s32kwatchdog), errp)) {
return;
}
@@ -991,7 +1006,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
/* 0x40080000 .. 0x4008ffff : ARMSSE second Base peripheral region */
- qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq);
+ qdev_connect_clock_in(DEVICE(&s->nswatchdog), "WDOGCLK", s->mainclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->nswatchdog), errp)) {
return;
}
@@ -999,7 +1014,7 @@ static void armsse_realize(DeviceState *dev, Error **errp)
armsse_get_common_irq_in(s, 1));
sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000);
- qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq);
+ qdev_connect_clock_in(DEVICE(&s->swatchdog), "WDOGCLK", s->mainclk);
if (!sysbus_realize(SYS_BUS_DEVICE(&s->swatchdog), errp)) {
return;
}
@@ -1104,7 +1119,8 @@ static void armsse_realize(DeviceState *dev, Error **errp)
*/
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container);
- system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq;
+ /* Set initial system_clock_scale from MAINCLK */
+ armsse_mainclk_update(s);
}
static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
@@ -1127,9 +1143,11 @@ static void armsse_idau_check(IDAUInterface *ii, uint32_t address,
static const VMStateDescription armsse_vmstate = {
.name = "iotkit",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
+ VMSTATE_CLOCK(mainclk, ARMSSE),
+ VMSTATE_CLOCK(s32kclk, ARMSSE),
VMSTATE_UINT32(nsccfg, ARMSSE),
VMSTATE_END_OF_LIST()
}
diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c
index 3707876..90caa91 100644
--- a/hw/arm/mps2-tz.c
+++ b/hw/arm/mps2-tz.c
@@ -62,6 +62,7 @@
#include "hw/net/lan9118.h"
#include "net/net.h"
#include "hw/core/split-irq.h"
+#include "hw/qdev-clock.h"
#include "qom/object.h"
#define MPS2TZ_NUMIRQ 92
@@ -100,6 +101,8 @@ struct MPS2TZMachineState {
qemu_or_irq uart_irq_orgate;
DeviceState *lan9118;
SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ];
+ Clock *sysclk;
+ Clock *s32kclk;
};
#define TYPE_MPS2TZ_MACHINE "mps2tz"
@@ -110,6 +113,8 @@ OBJECT_DECLARE_TYPE(MPS2TZMachineState, MPS2TZMachineClass, MPS2TZ_MACHINE)
/* Main SYSCLK frequency in Hz */
#define SYSCLK_FRQ 20000000
+/* Slow 32Khz S32KCLK frequency in Hz */
+#define S32KCLK_FRQ (32 * 1000)
/* Create an alias of an entire original MemoryRegion @orig
* located at @base in the memory map.
@@ -396,13 +401,20 @@ static void mps2tz_common_init(MachineState *machine)
exit(EXIT_FAILURE);
}
+ /* These clocks don't need migration because they are fixed-frequency */
+ mms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
+ clock_set_hz(mms->sysclk, SYSCLK_FRQ);
+ mms->s32kclk = clock_new(OBJECT(machine), "S32KCLK");
+ clock_set_hz(mms->s32kclk, S32KCLK_FRQ);
+
object_initialize_child(OBJECT(machine), TYPE_IOTKIT, &mms->iotkit,
mmc->armsse_type);
iotkitdev = DEVICE(&mms->iotkit);
object_property_set_link(OBJECT(&mms->iotkit), "memory",
OBJECT(system_memory), &error_abort);
qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", MPS2TZ_NUMIRQ);
- qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ);
+ qdev_connect_clock_in(iotkitdev, "MAINCLK", mms->sysclk);
+ qdev_connect_clock_in(iotkitdev, "S32KCLK", mms->s32kclk);
sysbus_realize(SYS_BUS_DEVICE(&mms->iotkit), &error_fatal);
/*
diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c
index 9a8b23c..39add41 100644
--- a/hw/arm/mps2.c
+++ b/hw/arm/mps2.c
@@ -46,6 +46,7 @@
#include "hw/net/lan9118.h"
#include "net/net.h"
#include "hw/watchdog/cmsdk-apb-watchdog.h"
+#include "hw/qdev-clock.h"
#include "qom/object.h"
typedef enum MPS2FPGAType {
@@ -83,6 +84,8 @@ struct MPS2MachineState {
/* CMSDK APB subsystem */
CMSDKAPBDualTimer dualtimer;
CMSDKAPBWatchdog watchdog;
+ CMSDKAPBTimer timer[2];
+ Clock *sysclk;
};
#define TYPE_MPS2_MACHINE "mps2"
@@ -139,6 +142,10 @@ static void mps2_common_init(MachineState *machine)
exit(EXIT_FAILURE);
}
+ /* This clock doesn't need migration because it is fixed-frequency */
+ mms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
+ clock_set_hz(mms->sysclk, SYSCLK_FRQ);
+
/* The FPGA images have an odd combination of different RAMs,
* because in hardware they are different implementations and
* connected to different buses, giving varying performance/size
@@ -330,18 +337,31 @@ static void mps2_common_init(MachineState *machine)
}
/* CMSDK APB subsystem */
- cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ);
- cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ);
+ for (i = 0; i < ARRAY_SIZE(mms->timer); i++) {
+ g_autofree char *name = g_strdup_printf("timer%d", i);
+ hwaddr base = 0x40000000 + i * 0x1000;
+ int irqno = 8 + i;
+ SysBusDevice *sbd;
+
+ object_initialize_child(OBJECT(mms), name, &mms->timer[i],
+ TYPE_CMSDK_APB_TIMER);
+ sbd = SYS_BUS_DEVICE(&mms->timer[i]);
+ qdev_connect_clock_in(DEVICE(&mms->timer[i]), "pclk", mms->sysclk);
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ sysbus_mmio_map(sbd, 0, base);
+ sysbus_connect_irq(sbd, 0, qdev_get_gpio_in(armv7m, irqno));
+ }
+
object_initialize_child(OBJECT(mms), "dualtimer", &mms->dualtimer,
TYPE_CMSDK_APB_DUALTIMER);
- qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ);
+ qdev_connect_clock_in(DEVICE(&mms->dualtimer), "TIMCLK", mms->sysclk);
sysbus_realize(SYS_BUS_DEVICE(&mms->dualtimer), &error_fatal);
sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0,
qdev_get_gpio_in(armv7m, 10));
sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000);
object_initialize_child(OBJECT(mms), "watchdog", &mms->watchdog,
TYPE_CMSDK_APB_WATCHDOG);
- qdev_prop_set_uint32(DEVICE(&mms->watchdog), "wdogclk-frq", SYSCLK_FRQ);
+ qdev_connect_clock_in(DEVICE(&mms->watchdog), "WDOGCLK", mms->sysclk);
sysbus_realize(SYS_BUS_DEVICE(&mms->watchdog), &error_fatal);
sysbus_connect_irq(SYS_BUS_DEVICE(&mms->watchdog), 0,
qdev_get_gpio_in_named(armv7m, "NMI", 0));
diff --git a/hw/arm/musca.c b/hw/arm/musca.c
index b50157f..945643c 100644
--- a/hw/arm/musca.c
+++ b/hw/arm/musca.c
@@ -33,6 +33,7 @@
#include "hw/misc/tz-ppc.h"
#include "hw/misc/unimp.h"
#include "hw/rtc/pl031.h"
+#include "hw/qdev-clock.h"
#include "qom/object.h"
#define MUSCA_NUMIRQ_MAX 96
@@ -82,6 +83,8 @@ struct MuscaMachineState {
UnimplementedDeviceState sdio;
UnimplementedDeviceState gpio;
UnimplementedDeviceState cryptoisland;
+ Clock *sysclk;
+ Clock *s32kclk;
};
#define TYPE_MUSCA_MACHINE "musca"
@@ -96,6 +99,8 @@ OBJECT_DECLARE_TYPE(MuscaMachineState, MuscaMachineClass, MUSCA_MACHINE)
* don't model that in our SSE-200 model yet.
*/
#define SYSCLK_FRQ 40000000
+/* Slow 32Khz S32KCLK frequency in Hz */
+#define S32KCLK_FRQ (32 * 1000)
static qemu_irq get_sse_irq_in(MuscaMachineState *mms, int irqno)
{
@@ -367,6 +372,11 @@ static void musca_init(MachineState *machine)
exit(1);
}
+ mms->sysclk = clock_new(OBJECT(machine), "SYSCLK");
+ clock_set_hz(mms->sysclk, SYSCLK_FRQ);
+ mms->s32kclk = clock_new(OBJECT(machine), "S32KCLK");
+ clock_set_hz(mms->s32kclk, S32KCLK_FRQ);
+
object_initialize_child(OBJECT(machine), "sse-200", &mms->sse,
TYPE_SSE200);
ssedev = DEVICE(&mms->sse);
@@ -375,7 +385,8 @@ static void musca_init(MachineState *machine)
qdev_prop_set_uint32(ssedev, "EXP_NUMIRQ", mmc->num_irqs);
qdev_prop_set_uint32(ssedev, "init-svtor", mmc->init_svtor);
qdev_prop_set_uint32(ssedev, "SRAM_ADDR_WIDTH", mmc->sram_addr_width);
- qdev_prop_set_uint32(ssedev, "MAINCLK", SYSCLK_FRQ);
+ qdev_connect_clock_in(ssedev, "MAINCLK", mms->sysclk);
+ qdev_connect_clock_in(ssedev, "S32KCLK", mms->s32kclk);
/*
* Musca-A takes the default SSE-200 FPU/DSP settings (ie no for
* CPU0 and yes for CPU1); Musca-B1 explicitly enables them for CPU0.
diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c
index 6528231..ad72c09 100644
--- a/hw/arm/stellaris.c
+++ b/hw/arm/stellaris.c
@@ -26,6 +26,7 @@
#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "migration/vmstate.h"
#include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
#include "cpu.h"
#include "qom/object.h"
@@ -357,7 +358,12 @@ static void stellaris_gptm_realize(DeviceState *dev, Error **errp)
/* System controller. */
-typedef struct {
+#define TYPE_STELLARIS_SYS "stellaris-sys"
+OBJECT_DECLARE_SIMPLE_TYPE(ssys_state, STELLARIS_SYS)
+
+struct ssys_state {
+ SysBusDevice parent_obj;
+
MemoryRegion iomem;
uint32_t pborctl;
uint32_t ldopctl;
@@ -371,11 +377,19 @@ typedef struct {
uint32_t dcgc[3];
uint32_t clkvclr;
uint32_t ldoarst;
+ qemu_irq irq;
+ Clock *sysclk;
+ /* Properties (all read-only registers) */
uint32_t user0;
uint32_t user1;
- qemu_irq irq;
- stellaris_board_info *board;
-} ssys_state;
+ uint32_t did0;
+ uint32_t did1;
+ uint32_t dc0;
+ uint32_t dc1;
+ uint32_t dc2;
+ uint32_t dc3;
+ uint32_t dc4;
+};
static void ssys_update(ssys_state *s)
{
@@ -430,7 +444,7 @@ static uint32_t pllcfg_fury[16] = {
static int ssys_board_class(const ssys_state *s)
{
- uint32_t did0 = s->board->did0;
+ uint32_t did0 = s->did0;
switch (did0 & DID0_VER_MASK) {
case DID0_VER_0:
return DID0_CLASS_SANDSTORM;
@@ -456,19 +470,19 @@ static uint64_t ssys_read(void *opaque, hwaddr offset,
switch (offset) {
case 0x000: /* DID0 */
- return s->board->did0;
+ return s->did0;
case 0x004: /* DID1 */
- return s->board->did1;
+ return s->did1;
case 0x008: /* DC0 */
- return s->board->dc0;
+ return s->dc0;
case 0x010: /* DC1 */
- return s->board->dc1;
+ return s->dc1;
case 0x014: /* DC2 */
- return s->board->dc2;
+ return s->dc2;
case 0x018: /* DC3 */
- return s->board->dc3;
+ return s->dc3;
case 0x01c: /* DC4 */
- return s->board->dc4;
+ return s->dc4;
case 0x030: /* PBORCTL */
return s->pborctl;
case 0x034: /* LDOPCTL */
@@ -543,15 +557,26 @@ static bool ssys_use_rcc2(ssys_state *s)
}
/*
- * Caculate the sys. clock period in ms.
+ * Calculate the system clock period. We only want to propagate
+ * this change to the rest of the system if we're not being called
+ * from migration post-load.
*/
-static void ssys_calculate_system_clock(ssys_state *s)
+static void ssys_calculate_system_clock(ssys_state *s, bool propagate_clock)
{
+ /*
+ * SYSDIV field specifies divisor: 0 == /1, 1 == /2, etc. Input
+ * clock is 200MHz, which is a period of 5 ns. Dividing the clock
+ * frequency by X is the same as multiplying the period by X.
+ */
if (ssys_use_rcc2(s)) {
system_clock_scale = 5 * (((s->rcc2 >> 23) & 0x3f) + 1);
} else {
system_clock_scale = 5 * (((s->rcc >> 23) & 0xf) + 1);
}
+ clock_set_ns(s->sysclk, system_clock_scale);
+ if (propagate_clock) {
+ clock_propagate(s->sysclk);
+ }
}
static void ssys_write(void *opaque, hwaddr offset,
@@ -586,7 +611,7 @@ static void ssys_write(void *opaque, hwaddr offset,
s->int_status |= (1 << 6);
}
s->rcc = value;
- ssys_calculate_system_clock(s);
+ ssys_calculate_system_clock(s, true);
break;
case 0x070: /* RCC2 */
if (ssys_board_class(s) == DID0_CLASS_SANDSTORM) {
@@ -598,7 +623,7 @@ static void ssys_write(void *opaque, hwaddr offset,
s->int_status |= (1 << 6);
}
s->rcc2 = value;
- ssys_calculate_system_clock(s);
+ ssys_calculate_system_clock(s, true);
break;
case 0x100: /* RCGC0 */
s->rcgc[0] = value;
@@ -646,9 +671,9 @@ static const MemoryRegionOps ssys_ops = {
.endianness = DEVICE_NATIVE_ENDIAN,
};
-static void ssys_reset(void *opaque)
+static void stellaris_sys_reset_enter(Object *obj, ResetType type)
{
- ssys_state *s = (ssys_state *)opaque;
+ ssys_state *s = STELLARIS_SYS(obj);
s->pborctl = 0x7ffd;
s->rcc = 0x078e3ac0;
@@ -661,14 +686,25 @@ static void ssys_reset(void *opaque)
s->rcgc[0] = 1;
s->scgc[0] = 1;
s->dcgc[0] = 1;
- ssys_calculate_system_clock(s);
+}
+
+static void stellaris_sys_reset_hold(Object *obj)
+{
+ ssys_state *s = STELLARIS_SYS(obj);
+
+ /* OK to propagate clocks from the hold phase */
+ ssys_calculate_system_clock(s, true);
+}
+
+static void stellaris_sys_reset_exit(Object *obj)
+{
}
static int stellaris_sys_post_load(void *opaque, int version_id)
{
ssys_state *s = opaque;
- ssys_calculate_system_clock(s);
+ ssys_calculate_system_clock(s, false);
return 0;
}
@@ -691,30 +727,61 @@ static const VMStateDescription vmstate_stellaris_sys = {
VMSTATE_UINT32_ARRAY(dcgc, ssys_state, 3),
VMSTATE_UINT32(clkvclr, ssys_state),
VMSTATE_UINT32(ldoarst, ssys_state),
+ /* No field for sysclk -- handled in post-load instead */
VMSTATE_END_OF_LIST()
}
};
-static int stellaris_sys_init(uint32_t base, qemu_irq irq,
- stellaris_board_info * board,
- uint8_t *macaddr)
-{
- ssys_state *s;
+static Property stellaris_sys_properties[] = {
+ DEFINE_PROP_UINT32("user0", ssys_state, user0, 0),
+ DEFINE_PROP_UINT32("user1", ssys_state, user1, 0),
+ DEFINE_PROP_UINT32("did0", ssys_state, did0, 0),
+ DEFINE_PROP_UINT32("did1", ssys_state, did1, 0),
+ DEFINE_PROP_UINT32("dc0", ssys_state, dc0, 0),
+ DEFINE_PROP_UINT32("dc1", ssys_state, dc1, 0),
+ DEFINE_PROP_UINT32("dc2", ssys_state, dc2, 0),
+ DEFINE_PROP_UINT32("dc3", ssys_state, dc3, 0),
+ DEFINE_PROP_UINT32("dc4", ssys_state, dc4, 0),
+ DEFINE_PROP_END_OF_LIST()
+};
- s = g_new0(ssys_state, 1);
- s->irq = irq;
- s->board = board;
- /* Most devices come preprogrammed with a MAC address in the user data. */
- s->user0 = macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16);
- s->user1 = macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16);
+static void stellaris_sys_instance_init(Object *obj)
+{
+ ssys_state *s = STELLARIS_SYS(obj);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(s);
- memory_region_init_io(&s->iomem, NULL, &ssys_ops, s, "ssys", 0x00001000);
- memory_region_add_subregion(get_system_memory(), base, &s->iomem);
- ssys_reset(s);
- vmstate_register(NULL, VMSTATE_INSTANCE_ID_ANY, &vmstate_stellaris_sys, s);
- return 0;
+ memory_region_init_io(&s->iomem, obj, &ssys_ops, s, "ssys", 0x00001000);
+ sysbus_init_mmio(sbd, &s->iomem);
+ sysbus_init_irq(sbd, &s->irq);
+ s->sysclk = qdev_init_clock_out(DEVICE(s), "SYSCLK");
}
+static DeviceState *stellaris_sys_init(uint32_t base, qemu_irq irq,
+ stellaris_board_info *board,
+ uint8_t *macaddr)
+{
+ DeviceState *dev = qdev_new(TYPE_STELLARIS_SYS);
+ SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
+
+ /* Most devices come preprogrammed with a MAC address in the user data. */
+ qdev_prop_set_uint32(dev, "user0",
+ macaddr[0] | (macaddr[1] << 8) | (macaddr[2] << 16));
+ qdev_prop_set_uint32(dev, "user1",
+ macaddr[3] | (macaddr[4] << 8) | (macaddr[5] << 16));
+ qdev_prop_set_uint32(dev, "did0", board->did0);
+ qdev_prop_set_uint32(dev, "did1", board->did1);
+ qdev_prop_set_uint32(dev, "dc0", board->dc0);
+ qdev_prop_set_uint32(dev, "dc1", board->dc1);
+ qdev_prop_set_uint32(dev, "dc2", board->dc2);
+ qdev_prop_set_uint32(dev, "dc3", board->dc3);
+ qdev_prop_set_uint32(dev, "dc4", board->dc4);
+
+ sysbus_realize_and_unref(sbd, &error_fatal);
+ sysbus_mmio_map(sbd, 0, base);
+ sysbus_connect_irq(sbd, 0, irq);
+
+ return dev;
+}
/* I2C controller. */
@@ -1280,6 +1347,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
int flash_size;
I2CBus *i2c;
DeviceState *dev;
+ DeviceState *ssys_dev;
int i;
int j;
@@ -1330,16 +1398,15 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board)
}
}
- stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
- board, nd_table[0].macaddr.a);
+ ssys_dev = stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28),
+ board, nd_table[0].macaddr.a);
if (board->dc1 & (1 << 3)) { /* watchdog present */
dev = qdev_new(TYPE_LUMINARY_WATCHDOG);
- /* system_clock_scale is valid now */
- uint32_t mainclk = NANOSECONDS_PER_SECOND / system_clock_scale;
- qdev_prop_set_uint32(dev, "wdogclk-frq", mainclk);
+ qdev_connect_clock_in(dev, "WDOGCLK",
+ qdev_get_clock_out(ssys_dev, "SYSCLK"));
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map(SYS_BUS_DEVICE(dev),
@@ -1553,11 +1620,32 @@ static const TypeInfo stellaris_adc_info = {
.class_init = stellaris_adc_class_init,
};
+static void stellaris_sys_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+ dc->vmsd = &vmstate_stellaris_sys;
+ rc->phases.enter = stellaris_sys_reset_enter;
+ rc->phases.hold = stellaris_sys_reset_hold;
+ rc->phases.exit = stellaris_sys_reset_exit;
+ device_class_set_props(dc, stellaris_sys_properties);
+}
+
+static const TypeInfo stellaris_sys_info = {
+ .name = TYPE_STELLARIS_SYS,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(ssys_state),
+ .instance_init = stellaris_sys_instance_init,
+ .class_init = stellaris_sys_class_init,
+};
+
static void stellaris_register_types(void)
{
type_register_static(&stellaris_i2c_info);
type_register_static(&stellaris_gptm_info);
type_register_static(&stellaris_adc_info);
+ type_register_static(&stellaris_sys_info);
}
type_init(stellaris_register_types)
diff --git a/hw/arm/virt.c b/hw/arm/virt.c
index 86070df..399da73 100644
--- a/hw/arm/virt.c
+++ b/hw/arm/virt.c
@@ -153,6 +153,7 @@ static const MemMapEntry base_memmap[] = {
[VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN },
[VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN},
[VIRT_PVTIME] = { 0x090a0000, 0x00010000 },
+ [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 },
[VIRT_MMIO] = { 0x0a000000, 0x00000200 },
/* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */
[VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 },
@@ -820,17 +821,80 @@ static void virt_powerdown_req(Notifier *n, void *opaque)
}
}
-static void create_gpio(const VirtMachineState *vms)
+static void create_gpio_keys(const VirtMachineState *vms,
+ DeviceState *pl061_dev,
+ uint32_t phandle)
+{
+ gpio_key_dev = sysbus_create_simple("gpio-key", -1,
+ qdev_get_gpio_in(pl061_dev, 3));
+
+ qemu_fdt_add_subnode(vms->fdt, "/gpio-keys");
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-keys", "compatible", "gpio-keys");
+ qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#size-cells", 0);
+ qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#address-cells", 1);
+
+ qemu_fdt_add_subnode(vms->fdt, "/gpio-keys/poweroff");
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-keys/poweroff",
+ "label", "GPIO Key Poweroff");
+ qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys/poweroff", "linux,code",
+ KEY_POWER);
+ qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff",
+ "gpios", phandle, 3, 0);
+}
+
+#define SECURE_GPIO_POWEROFF 0
+#define SECURE_GPIO_RESET 1
+
+static void create_secure_gpio_pwr(const VirtMachineState *vms,
+ DeviceState *pl061_dev,
+ uint32_t phandle)
+{
+ DeviceState *gpio_pwr_dev;
+
+ /* gpio-pwr */
+ gpio_pwr_dev = sysbus_create_simple("gpio-pwr", -1, NULL);
+
+ /* connect secure pl061 to gpio-pwr */
+ qdev_connect_gpio_out(pl061_dev, SECURE_GPIO_RESET,
+ qdev_get_gpio_in_named(gpio_pwr_dev, "reset", 0));
+ qdev_connect_gpio_out(pl061_dev, SECURE_GPIO_POWEROFF,
+ qdev_get_gpio_in_named(gpio_pwr_dev, "shutdown", 0));
+
+ qemu_fdt_add_subnode(vms->fdt, "/gpio-poweroff");
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-poweroff", "compatible",
+ "gpio-poweroff");
+ qemu_fdt_setprop_cells(vms->fdt, "/gpio-poweroff",
+ "gpios", phandle, SECURE_GPIO_POWEROFF, 0);
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-poweroff", "status", "disabled");
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-poweroff", "secure-status",
+ "okay");
+
+ qemu_fdt_add_subnode(vms->fdt, "/gpio-restart");
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-restart", "compatible",
+ "gpio-restart");
+ qemu_fdt_setprop_cells(vms->fdt, "/gpio-restart",
+ "gpios", phandle, SECURE_GPIO_RESET, 0);
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-restart", "status", "disabled");
+ qemu_fdt_setprop_string(vms->fdt, "/gpio-restart", "secure-status",
+ "okay");
+}
+
+static void create_gpio_devices(const VirtMachineState *vms, int gpio,
+ MemoryRegion *mem)
{
char *nodename;
DeviceState *pl061_dev;
- hwaddr base = vms->memmap[VIRT_GPIO].base;
- hwaddr size = vms->memmap[VIRT_GPIO].size;
- int irq = vms->irqmap[VIRT_GPIO];
+ hwaddr base = vms->memmap[gpio].base;
+ hwaddr size = vms->memmap[gpio].size;
+ int irq = vms->irqmap[gpio];
const char compat[] = "arm,pl061\0arm,primecell";
+ SysBusDevice *s;
- pl061_dev = sysbus_create_simple("pl061", base,
- qdev_get_gpio_in(vms->gic, irq));
+ pl061_dev = qdev_new("pl061");
+ s = SYS_BUS_DEVICE(pl061_dev);
+ sysbus_realize_and_unref(s, &error_fatal);
+ memory_region_add_subregion(mem, base, sysbus_mmio_get_region(s, 0));
+ sysbus_connect_irq(s, 0, qdev_get_gpio_in(vms->gic, irq));
uint32_t phandle = qemu_fdt_alloc_phandle(vms->fdt);
nodename = g_strdup_printf("/pl061@%" PRIx64, base);
@@ -847,21 +911,19 @@ static void create_gpio(const VirtMachineState *vms)
qemu_fdt_setprop_string(vms->fdt, nodename, "clock-names", "apb_pclk");
qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", phandle);
- gpio_key_dev = sysbus_create_simple("gpio-key", -1,
- qdev_get_gpio_in(pl061_dev, 3));
- qemu_fdt_add_subnode(vms->fdt, "/gpio-keys");
- qemu_fdt_setprop_string(vms->fdt, "/gpio-keys", "compatible", "gpio-keys");
- qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#size-cells", 0);
- qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys", "#address-cells", 1);
-
- qemu_fdt_add_subnode(vms->fdt, "/gpio-keys/poweroff");
- qemu_fdt_setprop_string(vms->fdt, "/gpio-keys/poweroff",
- "label", "GPIO Key Poweroff");
- qemu_fdt_setprop_cell(vms->fdt, "/gpio-keys/poweroff", "linux,code",
- KEY_POWER);
- qemu_fdt_setprop_cells(vms->fdt, "/gpio-keys/poweroff",
- "gpios", phandle, 3, 0);
+ if (gpio != VIRT_GPIO) {
+ /* Mark as not usable by the normal world */
+ qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled");
+ qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay");
+ }
g_free(nodename);
+
+ /* Child gpio devices */
+ if (gpio == VIRT_GPIO) {
+ create_gpio_keys(vms, pl061_dev, phandle);
+ } else {
+ create_secure_gpio_pwr(vms, pl061_dev, phandle);
+ }
}
static void create_virtio_devices(const VirtMachineState *vms)
@@ -1990,7 +2052,11 @@ static void machvirt_init(MachineState *machine)
if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) {
vms->acpi_dev = create_acpi_ged(vms);
} else {
- create_gpio(vms);
+ create_gpio_devices(vms, VIRT_GPIO, sysmem);
+ }
+
+ if (vms->secure && !vmc->no_secure_gpio) {
+ create_gpio_devices(vms, VIRT_SECURE_GPIO, secure_sysmem);
}
/* connect powerdown request */
@@ -2608,8 +2674,11 @@ DEFINE_VIRT_MACHINE_AS_LATEST(6, 0)
static void virt_machine_5_2_options(MachineClass *mc)
{
+ VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc));
+
virt_machine_6_0_options(mc);
compat_props_add(mc->compat_props, hw_compat_5_2, hw_compat_5_2_len);
+ vmc->no_secure_gpio = true;
}
DEFINE_VIRT_MACHINE(5, 2)
diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c
index 4ef0c51..c971363 100644
--- a/hw/arm/xlnx-zcu102.c
+++ b/hw/arm/xlnx-zcu102.c
@@ -219,12 +219,12 @@ static void xlnx_zcu102_machine_instance_init(Object *obj)
s->secure = false;
/* Default to virt (EL2) being disabled */
s->virt = false;
- object_property_add_link(obj, "xlnx-zcu102.canbus0", TYPE_CAN_BUS,
+ object_property_add_link(obj, "canbus0", TYPE_CAN_BUS,
(Object **)&s->canbus[0],
object_property_allow_set_link,
0);
- object_property_add_link(obj, "xlnx-zcu102.canbus1", TYPE_CAN_BUS,
+ object_property_add_link(obj, "canbus1", TYPE_CAN_BUS,
(Object **)&s->canbus[1],
object_property_allow_set_link,
0);
diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c
index 2aa97cb..6ba19fd 100644
--- a/hw/core/ptimer.c
+++ b/hw/core/ptimer.c
@@ -15,6 +15,7 @@
#include "sysemu/qtest.h"
#include "block/aio.h"
#include "sysemu/cpus.h"
+#include "hw/clock.h"
#define DELTA_ADJUST 1
#define DELTA_NO_ADJUST -1
@@ -348,6 +349,39 @@ void ptimer_set_period(ptimer_state *s, int64_t period)
}
}
+/* Set counter increment interval from a Clock */
+void ptimer_set_period_from_clock(ptimer_state *s, const Clock *clk,
+ unsigned int divisor)
+{
+ /*
+ * The raw clock period is a 64-bit value in units of 2^-32 ns;
+ * put another way it's a 32.32 fixed-point ns value. Our internal
+ * representation of the period is 64.32 fixed point ns, so
+ * the conversion is simple.
+ */
+ uint64_t raw_period = clock_get(clk);
+ uint64_t period_frac;
+
+ assert(s->in_transaction);
+ s->delta = ptimer_get_count(s);
+ s->period = extract64(raw_period, 32, 32);
+ period_frac = extract64(raw_period, 0, 32);
+ /*
+ * divisor specifies a possible frequency divisor between the
+ * clock and the timer, so it is a multiplier on the period.
+ * We do the multiply after splitting the raw period out into
+ * period and frac to avoid having to do a 32*64->96 multiply.
+ */
+ s->period *= divisor;
+ period_frac *= divisor;
+ s->period += extract64(period_frac, 32, 32);
+ s->period_frac = (uint32_t)period_frac;
+
+ if (s->enabled) {
+ s->need_reload = true;
+ }
+}
+
/* Set counter frequency in Hz. */
void ptimer_set_freq(ptimer_state *s, uint32_t freq)
{
diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
index b6fdaa2..f0e7405 100644
--- a/hw/gpio/Kconfig
+++ b/hw/gpio/Kconfig
@@ -8,5 +8,8 @@ config PL061
config GPIO_KEY
bool
+config GPIO_PWR
+ bool
+
config SIFIVE_GPIO
bool
diff --git a/hw/gpio/gpio_pwr.c b/hw/gpio/gpio_pwr.c
new file mode 100644
index 0000000..7714fa0
--- /dev/null
+++ b/hw/gpio/gpio_pwr.c
@@ -0,0 +1,70 @@
+/*
+ * GPIO qemu power controller
+ *
+ * Copyright (c) 2020 Linaro Limited
+ *
+ * Author: Maxim Uvarov <maxim.uvarov@linaro.org>
+ *
+ * Virtual gpio driver which can be used on top of pl061
+ * to reboot and shutdown qemu virtual machine. One of use
+ * case is gpio driver for secure world application (ARM
+ * Trusted Firmware.).
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * QEMU interface:
+ * two named input GPIO lines:
+ * 'reset' : when asserted, trigger system reset
+ * 'shutdown' : when asserted, trigger system shutdown
+ */
+
+#include "qemu/osdep.h"
+#include "hw/sysbus.h"
+#include "sysemu/runstate.h"
+
+#define TYPE_GPIOPWR "gpio-pwr"
+OBJECT_DECLARE_SIMPLE_TYPE(GPIO_PWR_State, GPIOPWR)
+
+struct GPIO_PWR_State {
+ SysBusDevice parent_obj;
+};
+
+static void gpio_pwr_reset(void *opaque, int n, int level)
+{
+ if (level) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
+ }
+}
+
+static void gpio_pwr_shutdown(void *opaque, int n, int level)
+{
+ if (level) {
+ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
+ }
+}
+
+static void gpio_pwr_init(Object *obj)
+{
+ DeviceState *dev = DEVICE(obj);
+
+ qdev_init_gpio_in_named(dev, gpio_pwr_reset, "reset", 1);
+ qdev_init_gpio_in_named(dev, gpio_pwr_shutdown, "shutdown", 1);
+}
+
+static const TypeInfo gpio_pwr_info = {
+ .name = TYPE_GPIOPWR,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(GPIO_PWR_State),
+ .instance_init = gpio_pwr_init,
+};
+
+static void gpio_pwr_register_types(void)
+{
+ type_register_static(&gpio_pwr_info);
+}
+
+type_init(gpio_pwr_register_types)
diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
index 5c0a7d7..79568f0 100644
--- a/hw/gpio/meson.build
+++ b/hw/gpio/meson.build
@@ -1,5 +1,6 @@
softmmu_ss.add(when: 'CONFIG_E500', if_true: files('mpc8xxx.c'))
softmmu_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c'))
+softmmu_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c'))
softmmu_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c'))
softmmu_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c'))
softmmu_ss.add(when: 'CONFIG_PUV3', if_true: files('puv3_gpio.c'))
diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig
index eea059f..7f91f30 100644
--- a/hw/i386/Kconfig
+++ b/hw/i386/Kconfig
@@ -14,7 +14,7 @@ config PC
imply ISA_DEBUG
imply PARALLEL
imply PCI_DEVICES
- imply PVPANIC
+ imply PVPANIC_ISA
imply QXL
imply SEV
imply SGA
diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig
index cf18ac0..19c216f 100644
--- a/hw/misc/Kconfig
+++ b/hw/misc/Kconfig
@@ -121,9 +121,19 @@ config IOTKIT_SYSCTL
config IOTKIT_SYSINFO
bool
-config PVPANIC
+config PVPANIC_COMMON
+ bool
+
+config PVPANIC_PCI
+ bool
+ default y if PCI_DEVICES
+ depends on PCI
+ select PVPANIC_COMMON
+
+config PVPANIC_ISA
bool
depends on ISA_BUS
+ select PVPANIC_COMMON
config AUX
bool
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index 607cd38..6292839 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -13,6 +13,7 @@ softmmu_ss.add(when: 'CONFIG_EMC141X', if_true: files('emc141x.c'))
softmmu_ss.add(when: 'CONFIG_UNIMP', if_true: files('unimp.c'))
softmmu_ss.add(when: 'CONFIG_EMPTY_SLOT', if_true: files('empty_slot.c'))
softmmu_ss.add(when: 'CONFIG_LED', if_true: files('led.c'))
+softmmu_ss.add(when: 'CONFIG_PVPANIC_COMMON', if_true: files('pvpanic.c'))
# ARM devices
softmmu_ss.add(when: 'CONFIG_PL310', if_true: files('arm_l2x0.c'))
@@ -98,7 +99,8 @@ softmmu_ss.add(when: 'CONFIG_IOTKIT_SYSINFO', if_true: files('iotkit-sysinfo.c')
softmmu_ss.add(when: 'CONFIG_ARMSSE_CPUID', if_true: files('armsse-cpuid.c'))
softmmu_ss.add(when: 'CONFIG_ARMSSE_MHU', if_true: files('armsse-mhu.c'))
-softmmu_ss.add(when: 'CONFIG_PVPANIC', if_true: files('pvpanic.c'))
+softmmu_ss.add(when: 'CONFIG_PVPANIC_ISA', if_true: files('pvpanic-isa.c'))
+softmmu_ss.add(when: 'CONFIG_PVPANIC_PCI', if_true: files('pvpanic-pci.c'))
softmmu_ss.add(when: 'CONFIG_AUX', if_true: files('auxbus.c'))
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_scu.c', 'aspeed_sdmc.c', 'aspeed_xdma.c'))
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-sysreg.c'))
diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c
index e99e3cc..dabcb6c 100644
--- a/hw/misc/npcm7xx_pwm.c
+++ b/hw/misc/npcm7xx_pwm.c
@@ -58,6 +58,9 @@ REG32(NPCM7XX_PWM_PWDR3, 0x50);
#define NPCM7XX_CH_INV BIT(2)
#define NPCM7XX_CH_MOD BIT(3)
+#define NPCM7XX_MAX_CMR 65535
+#define NPCM7XX_MAX_CNR 65535
+
/* Offset of each PWM channel's prescaler in the PPR register. */
static const int npcm7xx_ppr_base[] = { 0, 0, 8, 8 };
/* Offset of each PWM channel's clock selector in the CSR register. */
@@ -96,7 +99,7 @@ static uint32_t npcm7xx_pwm_calculate_freq(NPCM7xxPWM *p)
static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM *p)
{
- uint64_t duty;
+ uint32_t duty;
if (p->running) {
if (p->cnr == 0) {
@@ -104,7 +107,7 @@ static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM *p)
} else if (p->cmr >= p->cnr) {
duty = NPCM7XX_PWM_MAX_DUTY;
} else {
- duty = NPCM7XX_PWM_MAX_DUTY * (p->cmr + 1) / (p->cnr + 1);
+ duty = (uint64_t)NPCM7XX_PWM_MAX_DUTY * (p->cmr + 1) / (p->cnr + 1);
}
} else {
duty = 0;
@@ -357,7 +360,13 @@ static void npcm7xx_pwm_write(void *opaque, hwaddr offset,
case A_NPCM7XX_PWM_CNR2:
case A_NPCM7XX_PWM_CNR3:
p = &s->pwm[npcm7xx_cnr_index(offset)];
- p->cnr = value;
+ if (value > NPCM7XX_MAX_CNR) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid cnr value: %u", __func__, value);
+ p->cnr = NPCM7XX_MAX_CNR;
+ } else {
+ p->cnr = value;
+ }
npcm7xx_pwm_update_output(p);
break;
@@ -366,7 +375,13 @@ static void npcm7xx_pwm_write(void *opaque, hwaddr offset,
case A_NPCM7XX_PWM_CMR2:
case A_NPCM7XX_PWM_CMR3:
p = &s->pwm[npcm7xx_cmr_index(offset)];
- p->cmr = value;
+ if (value > NPCM7XX_MAX_CMR) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid cmr value: %u", __func__, value);
+ p->cmr = NPCM7XX_MAX_CMR;
+ } else {
+ p->cmr = value;
+ }
npcm7xx_pwm_update_output(p);
break;
diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c
new file mode 100644
index 0000000..27113ab
--- /dev/null
+++ b/hw/misc/pvpanic-isa.c
@@ -0,0 +1,94 @@
+/*
+ * QEMU simulated pvpanic device.
+ *
+ * Copyright Fujitsu, Corp. 2013
+ *
+ * Authors:
+ * Wen Congyang <wency@cn.fujitsu.com>
+ * Hu Tao <hutao@cn.fujitsu.com>
+ *
+ * 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/log.h"
+#include "qemu/module.h"
+#include "sysemu/runstate.h"
+
+#include "hw/nvram/fw_cfg.h"
+#include "hw/qdev-properties.h"
+#include "hw/misc/pvpanic.h"
+#include "qom/object.h"
+#include "hw/isa/isa.h"
+
+OBJECT_DECLARE_SIMPLE_TYPE(PVPanicISAState, PVPANIC_ISA_DEVICE)
+
+/*
+ * PVPanicISAState for ISA device and
+ * use ioport.
+ */
+struct PVPanicISAState {
+ ISADevice parent_obj;
+
+ uint16_t ioport;
+ PVPanicState pvpanic;
+};
+
+static void pvpanic_isa_initfn(Object *obj)
+{
+ PVPanicISAState *s = PVPANIC_ISA_DEVICE(obj);
+
+ pvpanic_setup_io(&s->pvpanic, DEVICE(s), 1);
+}
+
+static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp)
+{
+ ISADevice *d = ISA_DEVICE(dev);
+ PVPanicISAState *s = PVPANIC_ISA_DEVICE(dev);
+ PVPanicState *ps = &s->pvpanic;
+ FWCfgState *fw_cfg = fw_cfg_find();
+ uint16_t *pvpanic_port;
+
+ if (!fw_cfg) {
+ return;
+ }
+
+ pvpanic_port = g_malloc(sizeof(*pvpanic_port));
+ *pvpanic_port = cpu_to_le16(s->ioport);
+ fw_cfg_add_file(fw_cfg, "etc/pvpanic-port", pvpanic_port,
+ sizeof(*pvpanic_port));
+
+ isa_register_ioport(d, &ps->mr, s->ioport);
+}
+
+static Property pvpanic_isa_properties[] = {
+ DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicISAState, ioport, 0x505),
+ DEFINE_PROP_UINT8("events", PVPanicISAState, pvpanic.events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pvpanic_isa_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ dc->realize = pvpanic_isa_realizefn;
+ device_class_set_props(dc, pvpanic_isa_properties);
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static TypeInfo pvpanic_isa_info = {
+ .name = TYPE_PVPANIC_ISA_DEVICE,
+ .parent = TYPE_ISA_DEVICE,
+ .instance_size = sizeof(PVPanicISAState),
+ .instance_init = pvpanic_isa_initfn,
+ .class_init = pvpanic_isa_class_init,
+};
+
+static void pvpanic_register_types(void)
+{
+ type_register_static(&pvpanic_isa_info);
+}
+
+type_init(pvpanic_register_types)
diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c
new file mode 100644
index 0000000..d629639
--- /dev/null
+++ b/hw/misc/pvpanic-pci.c
@@ -0,0 +1,94 @@
+/*
+ * QEMU simulated PCI pvpanic device.
+ *
+ * Copyright (C) 2020 Oracle
+ *
+ * Authors:
+ * Mihai Carabas <mihai.carabas@oracle.com>
+ *
+ * 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/log.h"
+#include "qemu/module.h"
+#include "sysemu/runstate.h"
+
+#include "hw/nvram/fw_cfg.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "hw/misc/pvpanic.h"
+#include "qom/object.h"
+#include "hw/pci/pci.h"
+
+OBJECT_DECLARE_SIMPLE_TYPE(PVPanicPCIState, PVPANIC_PCI_DEVICE)
+
+/*
+ * PVPanicPCIState for PCI device
+ */
+typedef struct PVPanicPCIState {
+ PCIDevice dev;
+ PVPanicState pvpanic;
+} PVPanicPCIState;
+
+static const VMStateDescription vmstate_pvpanic_pci = {
+ .name = "pvpanic-pci",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_PCI_DEVICE(dev, PVPanicPCIState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void pvpanic_pci_realizefn(PCIDevice *dev, Error **errp)
+{
+ PVPanicPCIState *s = PVPANIC_PCI_DEVICE(dev);
+ PVPanicState *ps = &s->pvpanic;
+
+ pvpanic_setup_io(&s->pvpanic, DEVICE(s), 2);
+
+ pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &ps->mr);
+}
+
+static Property pvpanic_pci_properties[] = {
+ DEFINE_PROP_UINT8("events", PVPanicPCIState, pvpanic.events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void pvpanic_pci_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass);
+
+ device_class_set_props(dc, pvpanic_pci_properties);
+
+ pc->realize = pvpanic_pci_realizefn;
+ pc->vendor_id = PCI_VENDOR_ID_REDHAT;
+ pc->device_id = PCI_DEVICE_ID_REDHAT_PVPANIC;
+ pc->revision = 1;
+ pc->class_id = PCI_CLASS_SYSTEM_OTHER;
+ dc->vmsd = &vmstate_pvpanic_pci;
+
+ set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+}
+
+static TypeInfo pvpanic_pci_info = {
+ .name = TYPE_PVPANIC_PCI_DEVICE,
+ .parent = TYPE_PCI_DEVICE,
+ .instance_size = sizeof(PVPanicPCIState),
+ .class_init = pvpanic_pci_class_init,
+ .interfaces = (InterfaceInfo[]) {
+ { INTERFACE_CONVENTIONAL_PCI_DEVICE },
+ { }
+ }
+};
+
+static void pvpanic_register_types(void)
+{
+ type_register_static(&pvpanic_pci_info);
+}
+
+type_init(pvpanic_register_types);
diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c
index 35d6797..e2cb4a5 100644
--- a/hw/misc/pvpanic.c
+++ b/hw/misc/pvpanic.c
@@ -22,18 +22,6 @@
#include "hw/misc/pvpanic.h"
#include "qom/object.h"
-/* The bit of supported pv event, TODO: include uapi header and remove this */
-#define PVPANIC_F_PANICKED 0
-#define PVPANIC_F_CRASHLOADED 1
-
-/* The pv event value */
-#define PVPANIC_PANICKED (1 << PVPANIC_F_PANICKED)
-#define PVPANIC_CRASHLOADED (1 << PVPANIC_F_CRASHLOADED)
-
-typedef struct PVPanicState PVPanicState;
-DECLARE_INSTANCE_CHECKER(PVPanicState, ISA_PVPANIC_DEVICE,
- TYPE_PVPANIC)
-
static void handle_event(int event)
{
static bool logged;
@@ -54,90 +42,29 @@ static void handle_event(int event)
}
}
-#include "hw/isa/isa.h"
-
-struct PVPanicState {
- ISADevice parent_obj;
-
- MemoryRegion io;
- uint16_t ioport;
- uint8_t events;
-};
-
/* return supported events on read */
-static uint64_t pvpanic_ioport_read(void *opaque, hwaddr addr, unsigned size)
+static uint64_t pvpanic_read(void *opaque, hwaddr addr, unsigned size)
{
PVPanicState *pvp = opaque;
return pvp->events;
}
-static void pvpanic_ioport_write(void *opaque, hwaddr addr, uint64_t val,
+static void pvpanic_write(void *opaque, hwaddr addr, uint64_t val,
unsigned size)
{
handle_event(val);
}
static const MemoryRegionOps pvpanic_ops = {
- .read = pvpanic_ioport_read,
- .write = pvpanic_ioport_write,
+ .read = pvpanic_read,
+ .write = pvpanic_write,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
-static void pvpanic_isa_initfn(Object *obj)
-{
- PVPanicState *s = ISA_PVPANIC_DEVICE(obj);
-
- memory_region_init_io(&s->io, OBJECT(s), &pvpanic_ops, s, "pvpanic", 1);
-}
-
-static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp)
-{
- ISADevice *d = ISA_DEVICE(dev);
- PVPanicState *s = ISA_PVPANIC_DEVICE(dev);
- FWCfgState *fw_cfg = fw_cfg_find();
- uint16_t *pvpanic_port;
-
- if (!fw_cfg) {
- return;
- }
-
- pvpanic_port = g_malloc(sizeof(*pvpanic_port));
- *pvpanic_port = cpu_to_le16(s->ioport);
- fw_cfg_add_file(fw_cfg, "etc/pvpanic-port", pvpanic_port,
- sizeof(*pvpanic_port));
-
- isa_register_ioport(d, &s->io, s->ioport);
-}
-
-static Property pvpanic_isa_properties[] = {
- DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicState, ioport, 0x505),
- DEFINE_PROP_UINT8("events", PVPanicState, events, PVPANIC_PANICKED | PVPANIC_CRASHLOADED),
- DEFINE_PROP_END_OF_LIST(),
-};
-
-static void pvpanic_isa_class_init(ObjectClass *klass, void *data)
+void pvpanic_setup_io(PVPanicState *s, DeviceState *dev, unsigned size)
{
- DeviceClass *dc = DEVICE_CLASS(klass);
-
- dc->realize = pvpanic_isa_realizefn;
- device_class_set_props(dc, pvpanic_isa_properties);
- set_bit(DEVICE_CATEGORY_MISC, dc->categories);
+ memory_region_init_io(&s->mr, OBJECT(dev), &pvpanic_ops, s, "pvpanic", size);
}
-
-static TypeInfo pvpanic_isa_info = {
- .name = TYPE_PVPANIC,
- .parent = TYPE_ISA_DEVICE,
- .instance_size = sizeof(PVPanicState),
- .instance_init = pvpanic_isa_initfn,
- .class_init = pvpanic_isa_class_init,
-};
-
-static void pvpanic_register_types(void)
-{
- type_register_static(&pvpanic_isa_info);
-}
-
-type_init(pvpanic_register_types)
diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c
index f653424..ef49f58 100644
--- a/hw/timer/cmsdk-apb-dualtimer.c
+++ b/hw/timer/cmsdk-apb-dualtimer.c
@@ -25,6 +25,7 @@
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/registerfields.h"
+#include "hw/qdev-clock.h"
#include "hw/timer/cmsdk-apb-dualtimer.h"
#include "migration/vmstate.h"
@@ -105,6 +106,22 @@ static void cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer *s)
qemu_set_irq(s->timerintc, timintc);
}
+static int cmsdk_dualtimermod_divisor(CMSDKAPBDualTimerModule *m)
+{
+ /* Return the divisor set by the current CONTROL.PRESCALE value */
+ switch (FIELD_EX32(m->control, CONTROL, PRESCALE)) {
+ case 0:
+ return 1;
+ case 1:
+ return 16;
+ case 2:
+ case 3: /* UNDEFINED, we treat like 2 (and complained when it was set) */
+ return 256;
+ default:
+ g_assert_not_reached();
+ }
+}
+
static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m,
uint32_t newctrl)
{
@@ -145,7 +162,7 @@ static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m,
default:
g_assert_not_reached();
}
- ptimer_set_freq(m->timer, m->parent->pclk_frq / divisor);
+ ptimer_set_period_from_clock(m->timer, m->parent->timclk, divisor);
}
if (changed & R_CONTROL_MODE_MASK) {
@@ -413,7 +430,8 @@ static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m)
* limit must both be set to 0xffff, so we wrap at 16 bits.
*/
ptimer_set_limit(m->timer, 0xffff, 1);
- ptimer_set_freq(m->timer, m->parent->pclk_frq);
+ ptimer_set_period_from_clock(m->timer, m->parent->timclk,
+ cmsdk_dualtimermod_divisor(m));
ptimer_transaction_commit(m->timer);
}
@@ -431,6 +449,20 @@ static void cmsdk_apb_dualtimer_reset(DeviceState *dev)
s->timeritop = 0;
}
+static void cmsdk_apb_dualtimer_clk_update(void *opaque)
+{
+ CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque);
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
+ CMSDKAPBDualTimerModule *m = &s->timermod[i];
+ ptimer_transaction_begin(m->timer);
+ ptimer_set_period_from_clock(m->timer, m->parent->timclk,
+ cmsdk_dualtimermod_divisor(m));
+ ptimer_transaction_commit(m->timer);
+ }
+}
+
static void cmsdk_apb_dualtimer_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -445,6 +477,8 @@ static void cmsdk_apb_dualtimer_init(Object *obj)
for (i = 0; i < ARRAY_SIZE(s->timermod); i++) {
sysbus_init_irq(sbd, &s->timermod[i].timerint);
}
+ s->timclk = qdev_init_clock_in(DEVICE(s), "TIMCLK",
+ cmsdk_apb_dualtimer_clk_update, s);
}
static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
@@ -452,8 +486,8 @@ static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp)
CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev);
int i;
- if (s->pclk_frq == 0) {
- error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
+ if (!clock_has_source(s->timclk)) {
+ error_setg(errp, "CMSDK APB dualtimer: TIMCLK clock must be connected");
return;
}
@@ -485,9 +519,10 @@ static const VMStateDescription cmsdk_dualtimermod_vmstate = {
static const VMStateDescription cmsdk_apb_dualtimer_vmstate = {
.name = "cmsdk-apb-dualtimer",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
+ VMSTATE_CLOCK(timclk, CMSDKAPBDualTimer),
VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer,
CMSDK_APB_DUALTIMER_NUM_MODULES,
1, cmsdk_dualtimermod_vmstate,
@@ -498,11 +533,6 @@ static const VMStateDescription cmsdk_apb_dualtimer_vmstate = {
}
};
-static Property cmsdk_apb_dualtimer_properties[] = {
- DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBDualTimer, pclk_frq, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -510,7 +540,6 @@ static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data)
dc->realize = cmsdk_apb_dualtimer_realize;
dc->vmsd = &cmsdk_apb_dualtimer_vmstate;
dc->reset = cmsdk_apb_dualtimer_reset;
- device_class_set_props(dc, cmsdk_apb_dualtimer_properties);
}
static const TypeInfo cmsdk_apb_dualtimer_info = {
diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c
index f85f130..ee51ce3 100644
--- a/hw/timer/cmsdk-apb-timer.c
+++ b/hw/timer/cmsdk-apb-timer.c
@@ -35,6 +35,7 @@
#include "hw/sysbus.h"
#include "hw/irq.h"
#include "hw/registerfields.h"
+#include "hw/qdev-clock.h"
#include "hw/timer/cmsdk-apb-timer.h"
#include "migration/vmstate.h"
@@ -67,14 +68,14 @@ static const int timer_id[] = {
0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */
};
-static void cmsdk_apb_timer_update(CMSDKAPBTIMER *s)
+static void cmsdk_apb_timer_update(CMSDKAPBTimer *s)
{
qemu_set_irq(s->timerint, !!(s->intstatus & R_INTSTATUS_IRQ_MASK));
}
static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
{
- CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
uint64_t r;
switch (offset) {
@@ -106,7 +107,7 @@ static uint64_t cmsdk_apb_timer_read(void *opaque, hwaddr offset, unsigned size)
static void cmsdk_apb_timer_write(void *opaque, hwaddr offset, uint64_t value,
unsigned size)
{
- CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
trace_cmsdk_apb_timer_write(offset, value, size);
@@ -181,7 +182,7 @@ static const MemoryRegionOps cmsdk_apb_timer_ops = {
static void cmsdk_apb_timer_tick(void *opaque)
{
- CMSDKAPBTIMER *s = CMSDK_APB_TIMER(opaque);
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
if (s->ctrl & R_CTRL_IRQEN_MASK) {
s->intstatus |= R_INTSTATUS_IRQ_MASK;
@@ -191,7 +192,7 @@ static void cmsdk_apb_timer_tick(void *opaque)
static void cmsdk_apb_timer_reset(DeviceState *dev)
{
- CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
trace_cmsdk_apb_timer_reset();
s->ctrl = 0;
@@ -203,23 +204,34 @@ static void cmsdk_apb_timer_reset(DeviceState *dev)
ptimer_transaction_commit(s->timer);
}
+static void cmsdk_apb_timer_clk_update(void *opaque)
+{
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(opaque);
+
+ ptimer_transaction_begin(s->timer);
+ ptimer_set_period_from_clock(s->timer, s->pclk, 1);
+ ptimer_transaction_commit(s->timer);
+}
+
static void cmsdk_apb_timer_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
- CMSDKAPBTIMER *s = CMSDK_APB_TIMER(obj);
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(obj);
memory_region_init_io(&s->iomem, obj, &cmsdk_apb_timer_ops,
s, "cmsdk-apb-timer", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->timerint);
+ s->pclk = qdev_init_clock_in(DEVICE(s), "pclk",
+ cmsdk_apb_timer_clk_update, s);
}
static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
{
- CMSDKAPBTIMER *s = CMSDK_APB_TIMER(dev);
+ CMSDKAPBTimer *s = CMSDK_APB_TIMER(dev);
- if (s->pclk_frq == 0) {
- error_setg(errp, "CMSDK APB timer: pclk-frq property must be set");
+ if (!clock_has_source(s->pclk)) {
+ error_setg(errp, "CMSDK APB timer: pclk clock must be connected");
return;
}
@@ -230,29 +242,25 @@ static void cmsdk_apb_timer_realize(DeviceState *dev, Error **errp)
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
ptimer_transaction_begin(s->timer);
- ptimer_set_freq(s->timer, s->pclk_frq);
+ ptimer_set_period_from_clock(s->timer, s->pclk, 1);
ptimer_transaction_commit(s->timer);
}
static const VMStateDescription cmsdk_apb_timer_vmstate = {
.name = "cmsdk-apb-timer",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
- VMSTATE_PTIMER(timer, CMSDKAPBTIMER),
- VMSTATE_UINT32(ctrl, CMSDKAPBTIMER),
- VMSTATE_UINT32(value, CMSDKAPBTIMER),
- VMSTATE_UINT32(reload, CMSDKAPBTIMER),
- VMSTATE_UINT32(intstatus, CMSDKAPBTIMER),
+ VMSTATE_PTIMER(timer, CMSDKAPBTimer),
+ VMSTATE_CLOCK(pclk, CMSDKAPBTimer),
+ VMSTATE_UINT32(ctrl, CMSDKAPBTimer),
+ VMSTATE_UINT32(value, CMSDKAPBTimer),
+ VMSTATE_UINT32(reload, CMSDKAPBTimer),
+ VMSTATE_UINT32(intstatus, CMSDKAPBTimer),
VMSTATE_END_OF_LIST()
}
};
-static Property cmsdk_apb_timer_properties[] = {
- DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBTIMER, pclk_frq, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -260,13 +268,12 @@ static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data)
dc->realize = cmsdk_apb_timer_realize;
dc->vmsd = &cmsdk_apb_timer_vmstate;
dc->reset = cmsdk_apb_timer_reset;
- device_class_set_props(dc, cmsdk_apb_timer_properties);
}
static const TypeInfo cmsdk_apb_timer_info = {
.name = TYPE_CMSDK_APB_TIMER,
.parent = TYPE_SYS_BUS_DEVICE,
- .instance_size = sizeof(CMSDKAPBTIMER),
+ .instance_size = sizeof(CMSDKAPBTimer),
.instance_init = cmsdk_apb_timer_init,
.class_init = cmsdk_apb_timer_class_init,
};
diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c
index 5bbadad..302f171 100644
--- a/hw/watchdog/cmsdk-apb-watchdog.c
+++ b/hw/watchdog/cmsdk-apb-watchdog.c
@@ -30,6 +30,7 @@
#include "hw/irq.h"
#include "hw/qdev-properties.h"
#include "hw/registerfields.h"
+#include "hw/qdev-clock.h"
#include "hw/watchdog/cmsdk-apb-watchdog.h"
#include "migration/vmstate.h"
@@ -309,6 +310,15 @@ static void cmsdk_apb_watchdog_reset(DeviceState *dev)
ptimer_transaction_commit(s->timer);
}
+static void cmsdk_apb_watchdog_clk_update(void *opaque)
+{
+ CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque);
+
+ ptimer_transaction_begin(s->timer);
+ ptimer_set_period_from_clock(s->timer, s->wdogclk, 1);
+ ptimer_transaction_commit(s->timer);
+}
+
static void cmsdk_apb_watchdog_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
@@ -318,6 +328,8 @@ static void cmsdk_apb_watchdog_init(Object *obj)
s, "cmsdk-apb-watchdog", 0x1000);
sysbus_init_mmio(sbd, &s->iomem);
sysbus_init_irq(sbd, &s->wdogint);
+ s->wdogclk = qdev_init_clock_in(DEVICE(s), "WDOGCLK",
+ cmsdk_apb_watchdog_clk_update, s);
s->is_luminary = false;
s->id = cmsdk_apb_watchdog_id;
@@ -327,9 +339,9 @@ static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
{
CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev);
- if (s->wdogclk_frq == 0) {
+ if (!clock_has_source(s->wdogclk)) {
error_setg(errp,
- "CMSDK APB watchdog: wdogclk-frq property must be set");
+ "CMSDK APB watchdog: WDOGCLK clock must be connected");
return;
}
@@ -340,15 +352,16 @@ static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp)
PTIMER_POLICY_NO_COUNTER_ROUND_DOWN);
ptimer_transaction_begin(s->timer);
- ptimer_set_freq(s->timer, s->wdogclk_frq);
+ ptimer_set_period_from_clock(s->timer, s->wdogclk, 1);
ptimer_transaction_commit(s->timer);
}
static const VMStateDescription cmsdk_apb_watchdog_vmstate = {
.name = "cmsdk-apb-watchdog",
- .version_id = 1,
- .minimum_version_id = 1,
+ .version_id = 2,
+ .minimum_version_id = 2,
.fields = (VMStateField[]) {
+ VMSTATE_CLOCK(wdogclk, CMSDKAPBWatchdog),
VMSTATE_PTIMER(timer, CMSDKAPBWatchdog),
VMSTATE_UINT32(control, CMSDKAPBWatchdog),
VMSTATE_UINT32(intstatus, CMSDKAPBWatchdog),
@@ -360,11 +373,6 @@ static const VMStateDescription cmsdk_apb_watchdog_vmstate = {
}
};
-static Property cmsdk_apb_watchdog_properties[] = {
- DEFINE_PROP_UINT32("wdogclk-frq", CMSDKAPBWatchdog, wdogclk_frq, 0),
- DEFINE_PROP_END_OF_LIST(),
-};
-
static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
@@ -372,7 +380,6 @@ static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data)
dc->realize = cmsdk_apb_watchdog_realize;
dc->vmsd = &cmsdk_apb_watchdog_vmstate;
dc->reset = cmsdk_apb_watchdog_reset;
- device_class_set_props(dc, cmsdk_apb_watchdog_properties);
}
static const TypeInfo cmsdk_apb_watchdog_info = {
diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h
index 77f8677..676cd4f 100644
--- a/include/hw/arm/armsse.h
+++ b/include/hw/arm/armsse.h
@@ -37,9 +37,10 @@
* per-CPU identity and control register blocks
*
* QEMU interface:
+ * + Clock input "MAINCLK": clock for CPUs and most peripherals
+ * + Clock input "S32KCLK": slow 32KHz clock used for a few peripherals
* + QOM property "memory" is a MemoryRegion containing the devices provided
* by the board model.
- * + QOM property "MAINCLK" is the frequency of the main system clock
* + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts.
* (In hardware, the SSE-200 permits the number of expansion interrupts
* for the two CPUs to be configured separately, but we restrict it to
@@ -103,6 +104,7 @@
#include "hw/misc/armsse-mhu.h"
#include "hw/misc/unimp.h"
#include "hw/or-irq.h"
+#include "hw/clock.h"
#include "hw/core/split-irq.h"
#include "hw/cpu/cluster.h"
#include "qom/object.h"
@@ -153,9 +155,9 @@ struct ARMSSE {
TZPPC apb_ppc0;
TZPPC apb_ppc1;
TZMPC mpc[IOTS_NUM_MPC];
- CMSDKAPBTIMER timer0;
- CMSDKAPBTIMER timer1;
- CMSDKAPBTIMER s32ktimer;
+ CMSDKAPBTimer timer0;
+ CMSDKAPBTimer timer1;
+ CMSDKAPBTimer s32ktimer;
qemu_or_irq ppc_irq_orgate;
SplitIRQ sec_resp_splitter;
SplitIRQ ppc_irq_splitter[NUM_PPCS];
@@ -209,10 +211,12 @@ struct ARMSSE {
uint32_t nsccfg;
+ Clock *mainclk;
+ Clock *s32kclk;
+
/* Properties */
MemoryRegion *board_memory;
uint32_t exp_numirq;
- uint32_t mainclk_frq;
uint32_t sram_addr_width;
uint32_t init_svtor;
bool cpu_fpu[SSE_MAX_CPUS];
diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h
index e4a2d21..36fcb29 100644
--- a/include/hw/arm/virt.h
+++ b/include/hw/arm/virt.h
@@ -81,6 +81,7 @@ enum {
VIRT_GPIO,
VIRT_SECURE_UART,
VIRT_SECURE_MEM,
+ VIRT_SECURE_GPIO,
VIRT_PCDIMM_ACPI,
VIRT_ACPI_GED,
VIRT_NVDIMM_ACPI,
@@ -127,6 +128,7 @@ struct VirtMachineClass {
bool kvm_no_adjvtime;
bool no_kvm_steal_time;
bool acpi_expose_flash;
+ bool no_secure_gpio;
};
struct VirtMachineState {
diff --git a/include/hw/clock.h b/include/hw/clock.h
index 6382f34..e5f45e2 100644
--- a/include/hw/clock.h
+++ b/include/hw/clock.h
@@ -140,6 +140,21 @@ void clock_clear_callback(Clock *clk);
void clock_set_source(Clock *clk, Clock *src);
/**
+ * clock_has_source:
+ * @clk: the clock
+ *
+ * Returns true if the clock has a source clock connected to it.
+ * This is useful for devices which have input clocks which must
+ * be connected by the board/SoC code which creates them. The
+ * device code can use this to check in its realize method that
+ * the clock has been connected.
+ */
+static inline bool clock_has_source(const Clock *clk)
+{
+ return clk->source != NULL;
+}
+
+/**
* clock_set:
* @clk: the clock to initialize.
* @value: the clock's value, 0 means unclocked
diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h
index ae0c818..ca3c5bb 100644
--- a/include/hw/misc/pvpanic.h
+++ b/include/hw/misc/pvpanic.h
@@ -17,13 +17,33 @@
#include "qom/object.h"
-#define TYPE_PVPANIC "pvpanic"
+#define TYPE_PVPANIC_ISA_DEVICE "pvpanic"
+#define TYPE_PVPANIC_PCI_DEVICE "pvpanic-pci"
#define PVPANIC_IOPORT_PROP "ioport"
+/* The bit of supported pv event, TODO: include uapi header and remove this */
+#define PVPANIC_F_PANICKED 0
+#define PVPANIC_F_CRASHLOADED 1
+
+/* The pv event value */
+#define PVPANIC_PANICKED (1 << PVPANIC_F_PANICKED)
+#define PVPANIC_CRASHLOADED (1 << PVPANIC_F_CRASHLOADED)
+
+/*
+ * PVPanicState for any device type
+ */
+typedef struct PVPanicState PVPanicState;
+struct PVPanicState {
+ MemoryRegion mr;
+ uint8_t events;
+};
+
+void pvpanic_setup_io(PVPanicState *s, DeviceState *dev, unsigned size);
+
static inline uint16_t pvpanic_port(void)
{
- Object *o = object_resolve_path_type("", TYPE_PVPANIC, NULL);
+ Object *o = object_resolve_path_type("", TYPE_PVPANIC_ISA_DEVICE, NULL);
if (!o) {
return 0;
}
diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h
index 259f9c9..66db084 100644
--- a/include/hw/pci/pci.h
+++ b/include/hw/pci/pci.h
@@ -107,6 +107,7 @@ extern bool pci_available;
#define PCI_DEVICE_ID_REDHAT_PCIE_BRIDGE 0x000e
#define PCI_DEVICE_ID_REDHAT_MDPY 0x000f
#define PCI_DEVICE_ID_REDHAT_NVME 0x0010
+#define PCI_DEVICE_ID_REDHAT_PVPANIC 0x0011
#define PCI_DEVICE_ID_REDHAT_QXL 0x0100
#define FMT_PCIBUS PRIx64
diff --git a/include/hw/ptimer.h b/include/hw/ptimer.h
index 412763f..c443218 100644
--- a/include/hw/ptimer.h
+++ b/include/hw/ptimer.h
@@ -166,6 +166,28 @@ void ptimer_transaction_commit(ptimer_state *s);
void ptimer_set_period(ptimer_state *s, int64_t period);
/**
+ * ptimer_set_period_from_clock - Set counter increment from a Clock
+ * @s: ptimer to configure
+ * @clk: pointer to Clock object to take period from
+ * @divisor: value to scale the clock frequency down by
+ *
+ * If the ptimer is being driven from a Clock, this is the preferred
+ * way to tell the ptimer about the period, because it avoids any
+ * possible rounding errors that might happen if the internal
+ * representation of the Clock period was converted to either a period
+ * in ns or a frequency in Hz.
+ *
+ * If the ptimer should run at the same frequency as the clock,
+ * pass 1 as the @divisor; if the ptimer should run at half the
+ * frequency, pass 2, and so on.
+ *
+ * This function will assert if it is called outside a
+ * ptimer_transaction_begin/commit block.
+ */
+void ptimer_set_period_from_clock(ptimer_state *s, const Clock *clock,
+ unsigned int divisor);
+
+/**
* ptimer_set_freq - Set counter frequency in Hz
* @s: ptimer to configure
* @freq: counter frequency in Hz
diff --git a/include/hw/timer/cmsdk-apb-dualtimer.h b/include/hw/timer/cmsdk-apb-dualtimer.h
index 08d9e6f..f3ec86c 100644
--- a/include/hw/timer/cmsdk-apb-dualtimer.h
+++ b/include/hw/timer/cmsdk-apb-dualtimer.h
@@ -16,7 +16,7 @@
* https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
*
* QEMU interface:
- * + QOM property "pclk-frq": frequency at which the timer is clocked
+ * + Clock input "TIMCLK": clock (for both timers)
* + sysbus MMIO region 0: the register bank
* + sysbus IRQ 0: combined timer interrupt TIMINTC
* + sysbus IRO 1: timer block 1 interrupt TIMINT1
@@ -28,6 +28,7 @@
#include "hw/sysbus.h"
#include "hw/ptimer.h"
+#include "hw/clock.h"
#include "qom/object.h"
#define TYPE_CMSDK_APB_DUALTIMER "cmsdk-apb-dualtimer"
@@ -61,7 +62,7 @@ struct CMSDKAPBDualTimer {
/*< public >*/
MemoryRegion iomem;
qemu_irq timerintc;
- uint32_t pclk_frq;
+ Clock *timclk;
CMSDKAPBDualTimerModule timermod[CMSDK_APB_DUALTIMER_NUM_MODULES];
uint32_t timeritcr;
diff --git a/include/hw/timer/cmsdk-apb-timer.h b/include/hw/timer/cmsdk-apb-timer.h
index 0d80b2a..c4c7eae 100644
--- a/include/hw/timer/cmsdk-apb-timer.h
+++ b/include/hw/timer/cmsdk-apb-timer.h
@@ -15,20 +15,27 @@
#include "hw/qdev-properties.h"
#include "hw/sysbus.h"
#include "hw/ptimer.h"
+#include "hw/clock.h"
#include "qom/object.h"
#define TYPE_CMSDK_APB_TIMER "cmsdk-apb-timer"
-OBJECT_DECLARE_SIMPLE_TYPE(CMSDKAPBTIMER, CMSDK_APB_TIMER)
+OBJECT_DECLARE_SIMPLE_TYPE(CMSDKAPBTimer, CMSDK_APB_TIMER)
-struct CMSDKAPBTIMER {
+/*
+ * QEMU interface:
+ * + Clock input "pclk": clock for the timer
+ * + sysbus MMIO region 0: the register bank
+ * + sysbus IRQ 0: timer interrupt TIMERINT
+ */
+struct CMSDKAPBTimer {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
MemoryRegion iomem;
qemu_irq timerint;
- uint32_t pclk_frq;
struct ptimer_state *timer;
+ Clock *pclk;
uint32_t ctrl;
uint32_t value;
@@ -36,25 +43,4 @@ struct CMSDKAPBTIMER {
uint32_t intstatus;
};
-/**
- * cmsdk_apb_timer_create - convenience function to create TYPE_CMSDK_APB_TIMER
- * @addr: location in system memory to map registers
- * @pclk_frq: frequency in Hz of the PCLK clock (used for calculating baud rate)
- */
-static inline DeviceState *cmsdk_apb_timer_create(hwaddr addr,
- qemu_irq timerint,
- uint32_t pclk_frq)
-{
- DeviceState *dev;
- SysBusDevice *s;
-
- dev = qdev_new(TYPE_CMSDK_APB_TIMER);
- s = SYS_BUS_DEVICE(dev);
- qdev_prop_set_uint32(dev, "pclk-frq", pclk_frq);
- sysbus_realize_and_unref(s, &error_fatal);
- sysbus_mmio_map(s, 0, addr);
- sysbus_connect_irq(s, 0, timerint);
- return dev;
-}
-
#endif
diff --git a/include/hw/watchdog/cmsdk-apb-watchdog.h b/include/hw/watchdog/cmsdk-apb-watchdog.h
index 3da0d43..c6b3e78 100644
--- a/include/hw/watchdog/cmsdk-apb-watchdog.h
+++ b/include/hw/watchdog/cmsdk-apb-watchdog.h
@@ -16,7 +16,7 @@
* https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit
*
* QEMU interface:
- * + QOM property "wdogclk-frq": frequency at which the watchdog is clocked
+ * + Clock input "WDOGCLK": clock for the watchdog's timer
* + sysbus MMIO region 0: the register bank
* + sysbus IRQ 0: watchdog interrupt
*
@@ -33,6 +33,7 @@
#include "hw/sysbus.h"
#include "hw/ptimer.h"
+#include "hw/clock.h"
#include "qom/object.h"
#define TYPE_CMSDK_APB_WATCHDOG "cmsdk-apb-watchdog"
@@ -51,9 +52,9 @@ struct CMSDKAPBWatchdog {
/*< public >*/
MemoryRegion iomem;
qemu_irq wdogint;
- uint32_t wdogclk_frq;
bool is_luminary;
struct ptimer_state *timer;
+ Clock *wdogclk;
uint32_t control;
uint32_t intstatus;
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index b6ffdc1..ba15be9 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -710,4 +710,16 @@ static inline void qemu_thread_jit_write(void) {}
static inline void qemu_thread_jit_execute(void) {}
#endif
+/**
+ * Platforms which do not support system() return ENOSYS
+ */
+#ifndef HAVE_SYSTEM_FUNCTION
+#define system platform_does_not_support_system
+static inline int platform_does_not_support_system(const char *command)
+{
+ errno = ENOSYS;
+ return -1;
+}
+#endif /* !HAVE_SYSTEM_FUNCTION */
+
#endif
diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h
index 976b529..68deb74 100644
--- a/include/qemu/typedefs.h
+++ b/include/qemu/typedefs.h
@@ -34,6 +34,7 @@ typedef struct BlockDriverState BlockDriverState;
typedef struct BusClass BusClass;
typedef struct BusState BusState;
typedef struct Chardev Chardev;
+typedef struct Clock Clock;
typedef struct CompatProperty CompatProperty;
typedef struct CoMutex CoMutex;
typedef struct CPUAddressSpace CPUAddressSpace;
diff --git a/meson.build b/meson.build
index a58c6f6..f00b775 100644
--- a/meson.build
+++ b/meson.build
@@ -1127,6 +1127,9 @@ config_host_data.set('HAVE_DRM_H', cc.has_header('libdrm/drm.h'))
config_host_data.set('HAVE_PTY_H', cc.has_header('pty.h'))
config_host_data.set('HAVE_SYS_IOCCOM_H', cc.has_header('sys/ioccom.h'))
config_host_data.set('HAVE_SYS_KCOV_H', cc.has_header('sys/kcov.h'))
+config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include <stdlib.h>'))
+
+config_host_data.set('CONFIG_PREADV', cc.has_function('preadv', prefix: '#include <sys/uio.h>'))
ignored = ['CONFIG_QEMU_INTERP_PREFIX'] # actually per-target
arrays = ['CONFIG_AUDIO_DRIVERS', 'CONFIG_BDRV_RW_WHITELIST', 'CONFIG_BDRV_RO_WHITELIST']
@@ -2164,9 +2167,14 @@ foreach target : target_dirs
}]
endif
foreach exe: execs
- emulators += {exe['name']:
- executable(exe['name'], exe['sources'],
- install: true,
+ exe_name = exe['name']
+ exe_sign = 'CONFIG_HVF' in config_target
+ if exe_sign
+ exe_name += '-unsigned'
+ endif
+
+ emulator = executable(exe_name, exe['sources'],
+ install: not exe_sign,
c_args: c_args,
dependencies: arch_deps + deps + exe['dependencies'],
objects: lib.extract_all_objects(recursive: true),
@@ -2174,7 +2182,23 @@ foreach target : target_dirs
link_depends: [block_syms, qemu_syms] + exe.get('link_depends', []),
link_args: link_args,
gui_app: exe['gui'])
- }
+
+ if exe_sign
+ emulators += {exe['name'] : custom_target(exe['name'],
+ install: true,
+ install_dir: get_option('bindir'),
+ depends: emulator,
+ output: exe['name'],
+ command: [
+ meson.current_source_dir() / 'scripts/entitlement.sh',
+ meson.current_build_dir() / exe_name,
+ meson.current_build_dir() / exe['name'],
+ meson.current_source_dir() / 'accel/hvf/entitlements.plist'
+ ])
+ }
+ else
+ emulators += {exe['name']: emulator}
+ endif
if 'CONFIG_TRACE_SYSTEMTAP' in config_host
foreach stp: [
@@ -2414,7 +2438,7 @@ summary_info += {'PIE': get_option('b_pie')}
summary_info += {'static build': config_host.has_key('CONFIG_STATIC')}
summary_info += {'malloc trim support': has_malloc_trim}
summary_info += {'membarrier': config_host.has_key('CONFIG_MEMBARRIER')}
-summary_info += {'preadv support': config_host.has_key('CONFIG_PREADV')}
+summary_info += {'preadv support': config_host_data.get('CONFIG_PREADV')}
summary_info += {'fdatasync': config_host.has_key('CONFIG_FDATASYNC')}
summary_info += {'madvise': config_host.has_key('CONFIG_MADVISE')}
summary_info += {'posix_madvise': config_host.has_key('CONFIG_POSIX_MADVISE')}
diff --git a/scripts/entitlement.sh b/scripts/entitlement.sh
new file mode 100755
index 0000000..c540fa6
--- /dev/null
+++ b/scripts/entitlement.sh
@@ -0,0 +1,13 @@
+#!/bin/sh -e
+#
+# Helper script for the build process to apply entitlements
+
+SRC="$1"
+DST="$2"
+ENTITLEMENT="$3"
+
+trap 'rm "$DST.tmp"' exit
+cp -af "$SRC" "$DST.tmp"
+codesign --entitlements "$ENTITLEMENT" --force -s - "$DST.tmp"
+mv "$DST.tmp" "$DST"
+trap '' exit
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
index df0d677..d080239 100644
--- a/target/arm/cpu.h
+++ b/target/arm/cpu.h
@@ -922,6 +922,7 @@ struct ARMCPU {
uint32_t id_mmfr4;
uint32_t id_pfr0;
uint32_t id_pfr1;
+ uint32_t id_pfr2;
uint32_t mvfr0;
uint32_t mvfr1;
uint32_t mvfr2;
diff --git a/target/arm/helper.c b/target/arm/helper.c
index d2ead3f..47e266d 100644
--- a/target/arm/helper.c
+++ b/target/arm/helper.c
@@ -6567,11 +6567,21 @@ static void define_debug_regs(ARMCPU *cpu)
*/
int i;
int wrps, brps, ctx_cmps;
- ARMCPRegInfo dbgdidr = {
- .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 0,
- .access = PL0_R, .accessfn = access_tda,
- .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdidr,
- };
+
+ /*
+ * The Arm ARM says DBGDIDR is optional and deprecated if EL1 cannot
+ * use AArch32. Given that bit 15 is RES1, if the value is 0 then
+ * the register must not exist for this cpu.
+ */
+ if (cpu->isar.dbgdidr != 0) {
+ ARMCPRegInfo dbgdidr = {
+ .name = "DBGDIDR", .cp = 14, .crn = 0, .crm = 0,
+ .opc1 = 0, .opc2 = 0,
+ .access = PL0_R, .accessfn = access_tda,
+ .type = ARM_CP_CONST, .resetvalue = cpu->isar.dbgdidr,
+ };
+ define_one_arm_cp_reg(cpu, &dbgdidr);
+ }
/* Note that all these register fields hold "number of Xs minus 1". */
brps = arm_num_brps(cpu);
@@ -6580,7 +6590,6 @@ static void define_debug_regs(ARMCPU *cpu)
assert(ctx_cmps <= brps);
- define_one_arm_cp_reg(cpu, &dbgdidr);
define_arm_cp_regs(cpu, debug_cp_reginfo);
if (arm_feature(&cpu->env, ARM_FEATURE_LPAE)) {
@@ -7662,11 +7671,11 @@ void register_cp_regs_for_features(ARMCPU *cpu)
.access = PL1_R, .type = ARM_CP_CONST,
.accessfn = access_aa64_tid3,
.resetvalue = 0 },
- { .name = "MVFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
+ { .name = "ID_PFR2", .state = ARM_CP_STATE_BOTH,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 4,
.access = PL1_R, .type = ARM_CP_CONST,
.accessfn = access_aa64_tid3,
- .resetvalue = 0 },
+ .resetvalue = cpu->isar.id_pfr2 },
{ .name = "MVFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5,
.access = PL1_R, .type = ARM_CP_CONST,
@@ -12409,7 +12418,7 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr,
*attrs = (MemTxAttrs) {};
- ret = get_phys_addr(env, addr, 0, mmu_idx, &phys_addr,
+ ret = get_phys_addr(env, addr, MMU_DATA_LOAD, mmu_idx, &phys_addr,
attrs, &prot, &page_size, &fi, &cacheattrs);
if (ret) {
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
index f74bac2..3c37fc4 100644
--- a/target/arm/kvm64.c
+++ b/target/arm/kvm64.c
@@ -578,6 +578,8 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf)
ARM64_SYS_REG(3, 0, 0, 1, 0));
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr1,
ARM64_SYS_REG(3, 0, 0, 1, 1));
+ err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr2,
+ ARM64_SYS_REG(3, 0, 0, 3, 4));
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0,
ARM64_SYS_REG(3, 0, 0, 1, 2));
err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0,
diff --git a/tests/qtest/cmsdk-apb-dualtimer-test.c b/tests/qtest/cmsdk-apb-dualtimer-test.c
new file mode 100644
index 0000000..ad6a758
--- /dev/null
+++ b/tests/qtest/cmsdk-apb-dualtimer-test.c
@@ -0,0 +1,130 @@
+/*
+ * QTest testcase for the CMSDK APB dualtimer device
+ *
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+
+/* IoTKit/ARMSSE dualtimer; driven at 25MHz in mps2-an385, so 40ns per tick */
+#define TIMER_BASE 0x40002000
+
+#define TIMER1LOAD 0
+#define TIMER1VALUE 4
+#define TIMER1CONTROL 8
+#define TIMER1INTCLR 0xc
+#define TIMER1RIS 0x10
+#define TIMER1MIS 0x14
+#define TIMER1BGLOAD 0x18
+
+#define TIMER2LOAD 0x20
+#define TIMER2VALUE 0x24
+#define TIMER2CONTROL 0x28
+#define TIMER2INTCLR 0x2c
+#define TIMER2RIS 0x30
+#define TIMER2MIS 0x34
+#define TIMER2BGLOAD 0x38
+
+#define CTRL_ENABLE (1 << 7)
+#define CTRL_PERIODIC (1 << 6)
+#define CTRL_INTEN (1 << 5)
+#define CTRL_PRESCALE_1 (0 << 2)
+#define CTRL_PRESCALE_16 (1 << 2)
+#define CTRL_PRESCALE_256 (2 << 2)
+#define CTRL_32BIT (1 << 1)
+#define CTRL_ONESHOT (1 << 0)
+
+static void test_dualtimer(void)
+{
+ g_assert_true(readl(TIMER_BASE + TIMER1RIS) == 0);
+
+ /* Start timer: will fire after 40000 ns */
+ writel(TIMER_BASE + TIMER1LOAD, 1000);
+ /* enable in free-running, wrapping, interrupt mode */
+ writel(TIMER_BASE + TIMER1CONTROL, CTRL_ENABLE | CTRL_INTEN);
+
+ /* Step to just past the 500th tick and check VALUE */
+ clock_step(500 * 40 + 1);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER1RIS), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER1VALUE), ==, 500);
+
+ /* Just past the 1000th tick: timer should have fired */
+ clock_step(500 * 40);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER1RIS), ==, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER1VALUE), ==, 0);
+
+ /*
+ * We are in free-running wrapping 16-bit mode, so on the following
+ * tick VALUE should have wrapped round to 0xffff.
+ */
+ clock_step(40);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER1VALUE), ==, 0xffff);
+
+ /* Check that any write to INTCLR clears interrupt */
+ writel(TIMER_BASE + TIMER1INTCLR, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER1RIS), ==, 0);
+
+ /* Turn off the timer */
+ writel(TIMER_BASE + TIMER1CONTROL, 0);
+}
+
+static void test_prescale(void)
+{
+ g_assert_true(readl(TIMER_BASE + TIMER2RIS) == 0);
+
+ /* Start timer: will fire after 40 * 256 * 1000 == 1024000 ns */
+ writel(TIMER_BASE + TIMER2LOAD, 1000);
+ /* enable in periodic, wrapping, interrupt mode, prescale 256 */
+ writel(TIMER_BASE + TIMER2CONTROL,
+ CTRL_ENABLE | CTRL_INTEN | CTRL_PERIODIC | CTRL_PRESCALE_256);
+
+ /* Step to just past the 500th tick and check VALUE */
+ clock_step(40 * 256 * 501);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER2RIS), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER2VALUE), ==, 500);
+
+ /* Just past the 1000th tick: timer should have fired */
+ clock_step(40 * 256 * 500);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER2RIS), ==, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER2VALUE), ==, 0);
+
+ /* In periodic mode the tick VALUE now reloads */
+ clock_step(40 * 256);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER2VALUE), ==, 1000);
+
+ /* Check that any write to INTCLR clears interrupt */
+ writel(TIMER_BASE + TIMER2INTCLR, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + TIMER2RIS), ==, 0);
+
+ /* Turn off the timer */
+ writel(TIMER_BASE + TIMER2CONTROL, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int r;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_start("-machine mps2-an385");
+
+ qtest_add_func("/cmsdk-apb-dualtimer/dualtimer", test_dualtimer);
+ qtest_add_func("/cmsdk-apb-dualtimer/prescale", test_prescale);
+
+ r = g_test_run();
+
+ qtest_end();
+
+ return r;
+}
diff --git a/tests/qtest/cmsdk-apb-timer-test.c b/tests/qtest/cmsdk-apb-timer-test.c
new file mode 100644
index 0000000..e85e1f7
--- /dev/null
+++ b/tests/qtest/cmsdk-apb-timer-test.c
@@ -0,0 +1,75 @@
+/*
+ * QTest testcase for the CMSDK APB timer device
+ *
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "libqtest-single.h"
+
+/* IoTKit/ARMSSE-200 timer0; driven at 25MHz in mps2-an385, so 40ns per tick */
+#define TIMER_BASE 0x40000000
+
+#define CTRL 0
+#define VALUE 4
+#define RELOAD 8
+#define INTSTATUS 0xc
+
+static void test_timer(void)
+{
+ g_assert_true(readl(TIMER_BASE + INTSTATUS) == 0);
+
+ /* Start timer: will fire after 40 * 1000 == 40000 ns */
+ writel(TIMER_BASE + RELOAD, 1000);
+ writel(TIMER_BASE + CTRL, 9);
+
+ /* Step to just past the 500th tick and check VALUE */
+ clock_step(40 * 500 + 1);
+ g_assert_cmpuint(readl(TIMER_BASE + INTSTATUS), ==, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + VALUE), ==, 500);
+
+ /* Just past the 1000th tick: timer should have fired */
+ clock_step(40 * 500);
+ g_assert_cmpuint(readl(TIMER_BASE + INTSTATUS), ==, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + VALUE), ==, 0);
+
+ /* VALUE reloads at the following tick */
+ clock_step(40);
+ g_assert_cmpuint(readl(TIMER_BASE + VALUE), ==, 1000);
+
+ /* Check write-1-to-clear behaviour of INTSTATUS */
+ writel(TIMER_BASE + INTSTATUS, 0);
+ g_assert_cmpuint(readl(TIMER_BASE + INTSTATUS), ==, 1);
+ writel(TIMER_BASE + INTSTATUS, 1);
+ g_assert_cmpuint(readl(TIMER_BASE + INTSTATUS), ==, 0);
+
+ /* Turn off the timer */
+ writel(TIMER_BASE + CTRL, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int r;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_start("-machine mps2-an385");
+
+ qtest_add_func("/cmsdk-apb-timer/timer", test_timer);
+
+ r = g_test_run();
+
+ qtest_end();
+
+ return r;
+}
diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c
new file mode 100644
index 0000000..2710cb1
--- /dev/null
+++ b/tests/qtest/cmsdk-apb-watchdog-test.c
@@ -0,0 +1,131 @@
+/*
+ * QTest testcase for the CMSDK APB watchdog device
+ *
+ * Copyright (c) 2021 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/bitops.h"
+#include "libqtest-single.h"
+
+/*
+ * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz,
+ * which is 80ns per tick.
+ */
+#define WDOG_BASE 0x40000000
+
+#define WDOGLOAD 0
+#define WDOGVALUE 4
+#define WDOGCONTROL 8
+#define WDOGINTCLR 0xc
+#define WDOGRIS 0x10
+#define WDOGMIS 0x14
+#define WDOGLOCK 0xc00
+
+#define SSYS_BASE 0x400fe000
+#define RCC 0x60
+#define SYSDIV_SHIFT 23
+#define SYSDIV_LENGTH 4
+
+static void test_watchdog(void)
+{
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+
+ writel(WDOG_BASE + WDOGCONTROL, 1);
+ writel(WDOG_BASE + WDOGLOAD, 1000);
+
+ /* Step to just past the 500th tick */
+ clock_step(500 * 80 + 1);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500);
+
+ /* Just past the 1000th tick: timer should have fired */
+ clock_step(500 * 80);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0);
+
+ /* VALUE reloads at following tick */
+ clock_step(80);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
+
+ /* Writing any value to WDOGINTCLR clears the interrupt and reloads */
+ clock_step(500 * 80);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1);
+ writel(WDOG_BASE + WDOGINTCLR, 0);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+}
+
+static void test_clock_change(void)
+{
+ uint32_t rcc;
+
+ /*
+ * Test that writing to the stellaris board's RCC register to
+ * change the system clock frequency causes the watchdog
+ * to change the speed it counts at.
+ */
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+
+ writel(WDOG_BASE + WDOGCONTROL, 1);
+ writel(WDOG_BASE + WDOGLOAD, 1000);
+
+ /* Step to just past the 500th tick */
+ clock_step(80 * 500 + 1);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500);
+
+ /* Rewrite RCC.SYSDIV from 16 to 8, so the clock is now 40ns per tick */
+ rcc = readl(SSYS_BASE + RCC);
+ g_assert_cmpuint(extract32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH), ==, 0xf);
+ rcc = deposit32(rcc, SYSDIV_SHIFT, SYSDIV_LENGTH, 7);
+ writel(SSYS_BASE + RCC, rcc);
+
+ /* Just past the 1000th tick: timer should have fired */
+ clock_step(40 * 500);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1);
+
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0);
+
+ /* VALUE reloads at following tick */
+ clock_step(41);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
+
+ /* Writing any value to WDOGINTCLR clears the interrupt and reloads */
+ clock_step(40 * 500);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1);
+ writel(WDOG_BASE + WDOGINTCLR, 0);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000);
+ g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0);
+}
+
+int main(int argc, char **argv)
+{
+ int r;
+
+ g_test_init(&argc, &argv, NULL);
+
+ qtest_start("-machine lm3s811evb");
+
+ qtest_add_func("/cmsdk-apb-watchdog/watchdog", test_watchdog);
+ qtest_add_func("/cmsdk-apb-watchdog/watchdog_clock_change",
+ test_clock_change);
+
+ r = g_test_run();
+
+ qtest_end();
+
+ return r;
+}
diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 16d0462..c83bc21 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -33,7 +33,8 @@ qtests_i386 = \
(config_host.has_key('CONFIG_LINUX') and \
config_all_devices.has_key('CONFIG_ISA_IPMI_BT') ? ['ipmi-bt-test'] : []) + \
(config_all_devices.has_key('CONFIG_WDT_IB700') ? ['wdt_ib700-test'] : []) + \
- (config_all_devices.has_key('CONFIG_PVPANIC') ? ['pvpanic-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_PVPANIC_ISA') ? ['pvpanic-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_PVPANIC_PCI') ? ['pvpanic-pci-test'] : []) + \
(config_all_devices.has_key('CONFIG_HDA') ? ['intel-hda-test'] : []) + \
(config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : []) + \
(config_all_devices.has_key('CONFIG_IOH3420') ? ['ioh3420-test'] : []) + \
@@ -141,6 +142,9 @@ qtests_npcm7xx = \
'npcm7xx_timer-test',
'npcm7xx_watchdog_timer-test']
qtests_arm = \
+ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \
+ (config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \
(config_all_devices.has_key('CONFIG_PFLASH_CFI02') ? ['pflash-cfi02-test'] : []) + \
(config_all_devices.has_key('CONFIG_NPCM7XX') ? qtests_npcm7xx : []) + \
['arm-cpu-features',
diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c
index 63557d2c..3d82654 100644
--- a/tests/qtest/npcm7xx_pwm-test.c
+++ b/tests/qtest/npcm7xx_pwm-test.c
@@ -272,7 +272,7 @@ static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
{
- uint64_t duty;
+ uint32_t duty;
if (cnr == 0) {
/* PWM is stopped. */
@@ -280,7 +280,7 @@ static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
} else if (cmr >= cnr) {
duty = MAX_DUTY;
} else {
- duty = MAX_DUTY * (cmr + 1) / (cnr + 1);
+ duty = (uint64_t)MAX_DUTY * (cmr + 1) / (cnr + 1);
}
if (inverted) {
diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c
new file mode 100644
index 0000000..2358852
--- /dev/null
+++ b/tests/qtest/pvpanic-pci-test.c
@@ -0,0 +1,98 @@
+/*
+ * QTest testcase for PV Panic PCI device
+ *
+ * Copyright (C) 2020 Oracle
+ *
+ * Authors:
+ * Mihai Carabas <mihai.carabas@oracle.com>
+ *
+ * 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 "libqos/libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "libqos/pci.h"
+#include "libqos/pci-pc.h"
+#include "hw/pci/pci_regs.h"
+
+static void test_panic_nopause(void)
+{
+ uint8_t val;
+ QDict *response, *data;
+ QTestState *qts;
+ QPCIBus *pcibus;
+ QPCIDevice *dev;
+ QPCIBar bar;
+
+ qts = qtest_init("-device pvpanic-pci,addr=04.0 -action panic=none");
+ pcibus = qpci_new_pc(qts, NULL);
+ dev = qpci_device_find(pcibus, QPCI_DEVFN(0x4, 0x0));
+ qpci_device_enable(dev);
+ bar = qpci_iomap(dev, 0, NULL);
+
+ qpci_memread(dev, bar, 0, &val, sizeof(val));
+ g_assert_cmpuint(val, ==, 3);
+
+ val = 1;
+ qpci_memwrite(dev, bar, 0, &val, sizeof(val));
+
+ response = qtest_qmp_eventwait_ref(qts, "GUEST_PANICKED");
+ g_assert(qdict_haskey(response, "data"));
+ data = qdict_get_qdict(response, "data");
+ g_assert(qdict_haskey(data, "action"));
+ g_assert_cmpstr(qdict_get_str(data, "action"), ==, "run");
+ qobject_unref(response);
+
+ g_free(dev);
+ qpci_free_pc(pcibus);
+ qtest_quit(qts);
+}
+
+static void test_panic(void)
+{
+ uint8_t val;
+ QDict *response, *data;
+ QTestState *qts;
+ QPCIBus *pcibus;
+ QPCIDevice *dev;
+ QPCIBar bar;
+
+ qts = qtest_init("-device pvpanic-pci,addr=04.0 -action panic=pause");
+ pcibus = qpci_new_pc(qts, NULL);
+ dev = qpci_device_find(pcibus, QPCI_DEVFN(0x4, 0x0));
+ qpci_device_enable(dev);
+ bar = qpci_iomap(dev, 0, NULL);
+
+ qpci_memread(dev, bar, 0, &val, sizeof(val));
+ g_assert_cmpuint(val, ==, 3);
+
+ val = 1;
+ qpci_memwrite(dev, bar, 0, &val, sizeof(val));
+
+ response = qtest_qmp_eventwait_ref(qts, "GUEST_PANICKED");
+ g_assert(qdict_haskey(response, "data"));
+ data = qdict_get_qdict(response, "data");
+ g_assert(qdict_haskey(data, "action"));
+ g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause");
+ qobject_unref(response);
+
+ g_free(dev);
+ qpci_free_pc(pcibus);
+ qtest_quit(qts);
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+ qtest_add_func("/pvpanic-pci/panic", test_panic);
+ qtest_add_func("/pvpanic-pci/panic-nopause", test_panic_nopause);
+
+ ret = g_test_run();
+
+ return ret;
+}
diff --git a/tests/qtest/xlnx-can-test.c b/tests/qtest/xlnx-can-test.c
index 3d11200..54de71a 100644
--- a/tests/qtest/xlnx-can-test.c
+++ b/tests/qtest/xlnx-can-test.c
@@ -138,9 +138,9 @@ static void test_can_bus(void)
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
- " -object can-bus,id=canbus0"
- " -machine xlnx-zcu102.canbus0=canbus0"
- " -machine xlnx-zcu102.canbus1=canbus0"
+ " -object can-bus,id=canbus"
+ " -machine canbus0=canbus"
+ " -machine canbus1=canbus"
);
/* Configure the CAN0 and CAN1. */
@@ -175,9 +175,9 @@ static void test_can_loopback(void)
uint32_t status = 0;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
- " -object can-bus,id=canbus0"
- " -machine xlnx-zcu102.canbus0=canbus0"
- " -machine xlnx-zcu102.canbus1=canbus0"
+ " -object can-bus,id=canbus"
+ " -machine canbus0=canbus"
+ " -machine canbus1=canbus"
);
/* Configure the CAN0 in loopback mode. */
@@ -223,9 +223,9 @@ static void test_can_filter(void)
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
- " -object can-bus,id=canbus0"
- " -machine xlnx-zcu102.canbus0=canbus0"
- " -machine xlnx-zcu102.canbus1=canbus0"
+ " -object can-bus,id=canbus"
+ " -machine canbus0=canbus"
+ " -machine canbus1=canbus"
);
/* Configure the CAN0 and CAN1. */
@@ -271,9 +271,9 @@ static void test_can_sleepmode(void)
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
- " -object can-bus,id=canbus0"
- " -machine xlnx-zcu102.canbus0=canbus0"
- " -machine xlnx-zcu102.canbus1=canbus0"
+ " -object can-bus,id=canbus"
+ " -machine canbus0=canbus"
+ " -machine canbus1=canbus"
);
/* Configure the CAN0. */
@@ -317,9 +317,9 @@ static void test_can_snoopmode(void)
uint8_t can_timestamp = 1;
QTestState *qts = qtest_init("-machine xlnx-zcu102"
- " -object can-bus,id=canbus0"
- " -machine xlnx-zcu102.canbus0=canbus0"
- " -machine xlnx-zcu102.canbus1=canbus0"
+ " -object can-bus,id=canbus"
+ " -machine canbus0=canbus"
+ " -machine canbus1=canbus"
);
/* Configure the CAN0. */